Skip to main content

Implementation Plan 18.2: Landscape Pivot & State Stabilization

This document outlines the detailed architectural design and step-by-step implementation plan to transition the active game arena from a vertical scrolling portrait layout to a modern, desktop-native landscape split-screen. It also stabilizes the backend state transition engine to eliminate fatal database transaction hangs (RESOLVING status lock) caused by floating-point math violations on Prisma Int columns.

📋 Architectural Context & Objectives

Our objective is to consolidate Sandbox Crucible UI/UX feedback and backend state machine failures into a single, cohesive patch. This plan centers on:
  1. Responsive Viewport Containment (Phase A): Eliminate global page scrolling. Lock active board UI inside a dual-column flex container fitting standard viewport heights (h-screen lg:overflow-hidden).
  2. Component Separation & Collisions (Phase B): Adapt contender slot layouts based on their geographic grid placement to prevent interactive action overlaps with the centered News Stub, and sync wallet aesthetics directly with the physical Political Compass Meme coordinates (Labor Top-Left, Dinars Top-Right).
  3. Player Retention Flow (Phase C): Eliminate dead-ends inside the game loop by offering clear secondary routing once a match reaches its terminal RESOLVED status.
  4. Integer Math Safety (Phase D): Ensure 100% database schema compliance by rounding ELO deltas, payouts, distance outcomes, and tax increments before hitting Prisma transactions.

🛠️ Proposed Changes

Phase A: The Landscape Pivot

This phase re-architects the primary screen layout of the active game board into a geographic dual-column interface designed specifically for landscape/desktop monitors.

[MODIFY] page.tsx

  • Target: Overhaul the outer container and the main grid flow in the DEBATE_PHASE.
  • Implementation Design:
    • Add h-screen flex flex-col overflow-hidden to the root layout container in /arena/[lobbyId]/page.tsx to completely lock down the viewport height and prevent global body scrollbars.
    • Refactor the compact header to remain height-bounded (shrink-0 flex items-center justify-between border-b border-gray-800 pb-3 mb-4).
    • Restructure the main active area (DEBATE_PHASE) to split the screen into two main columns using flex flex-col lg:flex-row items-stretch justify-between gap-6 w-full h-full min-h-0:
      1. Left Column (The Arena):
        • CSS classes: flex-1 min-h-0 flex items-center justify-center bg-zinc-950/20 border border-zinc-900/60 rounded-3xl relative overflow-hidden h-full p-4 lg:p-6.
        • Renders the classic 2x2 Political Compass quadrant grid and the centered, absolutely positioned Active News Stub. This column dynamically expands to fill the remaining height.
      2. Right Column (The Command Center Sidebar):
        • CSS classes: w-full lg:w-96 shrink-0 flex flex-col h-full min-h-0 bg-gray-900/20 border border-gray-800/80 rounded-3xl p-4 lg:p-5 overflow-hidden.
        • To preserve space and ensure scroll-free immersion, this Right Column will be structured as an internally scrollable command panel using overflow-y-auto h-full pr-1 scrollbar-thin scrollbar-thumb-zinc-800.
        • Inside this column, we place:
          • Tactical HUD: The existing coordinates map, base values, delta preview calculations, and the primary “Lock In Vector Stack” action button.
          • BASED/CRINGE Action Buttons: The local player’s primary vote-casting buttons when they are required to vote on opponents’ vectors.
          • PlayerHand (The Fanned Hand): Relocate PlayerHand from the bottom-level full-width section to the bottom section of this scrollable Right Column sidebar. The cards will render neatly as an internal deck or compact list within the command center, ensuring all interaction remains visible on a single screen.

Phase B: Slot Collisions & Wallet Alignment

This phase adapts layout components dynamically to resolve collision overlapping and realigns visual currency geography.

1. Preventing Contender Slot Center Collisions

  • File: page.tsx
  • Target: Inside the ContenderSlot component, adapt the layout flow dynamically based on the quadrant the slot represents.
  • Problem: In Authoritarian (top row) slots, rendering the BASED/CRINGE buttons below the card slot pushes the buttons towards the absolute center of the Political Compass, causing them to collide with and overlap the central Active News Stub.
  • Solution:
    • Add an optional quadrant?: string prop to ContenderSlotProps and pass the player’s quadrant from the parent rendering loop.
    • Inside ContenderSlot, conditionally toggle the layout flow of the contender card container and action buttons.
    • If the player’s quadrant is AUTH_LEFT or AUTH_RIGHT, replace flex-col with flex-col-reverse on the parent card-and-button column wrapper:
    • This pushes the BASED/CRINGE buttons above the card in the Authoritarian slots (upwards and away from the center News Stub) and keeps them below the card in Libertarian slots (downwards and away from the center News Stub), entirely eliminating overlap collision.

2. Compass-Aligned Wallet Currency Order

  • File: WalletOverlay.tsx
  • Target: Swap the rendering index of Dinars and Labor inside the grid display to mirror geographic political positions.
  • Geography Map:
    • Top-Left: AUTH_LEFT (Labor / ⚒)
    • Top-Right: AUTH_RIGHT (Dinars / đ)
    • Bottom-Left: LIB_LEFT (Pronouns / ❀)
    • Bottom-Right: LIB_RIGHT (Monke / 🐒)
  • Modifications:
    • Swap the rendering order in BOTH Inline Mode (lines 251-285) and the Floating Drawer list (lines 362-396) so labor (Labor) is declared and rendered before dinars (Dinars).
    • This ensures that when read as a 2x2 grid in inline mode, Labor correctly aligns with the top-left and Dinars with the top-right, creating a flawless mental map with the Political Compass.

Phase C: The Infinite Loop (Post-Match Summary Navigation)

This phase addresses post-match navigation limits by providing immediate secondary looping actions.

[MODIFY] page.tsx

  • Target: Rework the action segment of the RESOLVED page view (lines 985-992).
  • Problem: When a match concludes, only a single button is provided to “Return to Arena Entryway”, which interrupts the play-queue loop.
  • Solution:
    • Add two prominent, high-fidelity action buttons positioned side-by-side or cleanly stacked inside the summary pane:
      1. “Next Match” (Primary Action):
        • Renders as a highly visible, pulsing green/indigo gradient button (bg-gradient-to-r from-emerald-600 to-indigo-600 hover:from-emerald-500 hover:to-indigo-500 shadow-lg hover:shadow-emerald-500/20).
        • Redirects the player back to /arena and automatically triggers a new matchmaking queue or triggers an immediate join sequence to maintain high combat engagement.
      2. “Return to Entryway” (Secondary Action):
        • Renders as a neutral, high-quality dark bordered button (border border-zinc-700 hover:bg-zinc-800 text-zinc-300).
        • Gracefully exits the lobby and redirects the player to the entryway dashboard (/arena).

Phase D: The RESOLVING State Hotfix (Backend Integer Safety)

This phase resolves a critical backend exception where floating-point math violations on Postgres Int columns caused a fatal transaction rollback, locking lobbies in an infinite RESOLVING state.

[MODIFY] resolve.js

  • Problem: Faction tax yields (e.g. payout * 0.10) and multi-axis Euclidean distances generate float values (e.g. 23.7291). When these are sent as values to Prisma database increment operations on strict Postgres integer columns (such as elo and currentGip), the SQL query crashes with a type exception, forcing a database rollback. Because the state machine status update to RESOLVED occurs after this database transaction, the table remains permanently locked in RESOLVING.
  • Solution:
    • Update resolve.js to ensure absolute type compliance by wrapping all ELO, distance, and tax calculations in Math.round() prior to hitting database operations.
    • Refactor applyPlayerResolveMutation (lines 75-148):
      • Wrap the database ELO update:
      • Wrap the database Wallet balance update:
      • Wrap GIP tax calculations and faction updates:
    • Wrap Distance Outcomes:
      • Wrap calculateDistance outcomes or distance values inside the ranking map loop (line 254) in Math.round() to guarantee strict integer compliance before values are distributed to the payload:
    • Trace Logging Overhaul (Catch Block) (lines 482-486):
      • Expand the catch block of the POST /debate/resolve router to dump complete error stack traces:

🧪 Verification Plan

Automated Tests

Run the Express backend test suite to ensure that ELO and payouts resolve cleanly without any SQL compiler/runtime type exceptions:

Manual Verification Flow

  1. Currency Orientation Audit:
    • Open /arena (with local servers running). Inspect the dashboard wallet overlay. Confirm that Labor is listed top-left, and Dinars is listed top-right. Open the profile control dropdown, and confirm the vertical balance order lists Labor then Dinars.
  2. Layout Overhaul & Scroll-Lock Test:
    • Join a match (/arena/[lobbyId]). Verify that the active debate phase is presented as a high-end landscape split layout. Attempt to force-scroll the page vertically—the page viewport should remain completely locked and static.
    • Confirm that the PlayerHand renders cleanly inside the Right Column sidebar (Command Center) alongside the Tactical HUD and Lock-In action button.
  3. Collision Avoidance Audit:
    • Position a player in AUTH_LEFT or AUTH_RIGHT. Open the vote-casting prompt. Confirm that the “BASED/CRINGE” voting buttons render above the card frame, perfectly avoiding any central News Stub overlap.
  4. Prisma Integer Compliance & Flow Check:
    • Play a match to completion. Ensure that the backend successfully resolves and transitions the state from RESOLVING to RESOLVED with zero database transaction exceptions.
    • Observe that the post-match summary displays both the “Next Match” queue button and “Return to Entryway” secondary button side-by-side.