8dda9541a3
The previous formula card_width = window.x / 9 with card_height = 1.4 * card_width ignored the window height entirely. On a 1920×1080 window a 13-card face-up tableau column extended ~377 px below the viewport bottom — visible reproduction in the smoke test. compute_layout now derives two card_width candidates: one from the horizontal grid budget (window.x / 9, unchanged) and one from the vertical budget needed to seat 13 fanned cards plus the foundation row, vertical_gap, and h_gap bottom margin. The smaller of the two wins, so width remains the limiter on standard landscape windows and height takes over on tall or short-wide aspect ratios. The math is solved algebraically in a single substitution to avoid iteration. When height is the limiter the original layout would have squished the grid against the left edge; col_x now folds in a horizontal centring offset that collapses to the existing geometry whenever width is the limiter, so no other module needed an update. Adds MAX_TABLEAU_CARDS = 13.0 (King-down-to-Ace worst case) and a locally mirrored TABLEAU_FAN_FRAC = 0.25 — the original lives in card_plugin and importing it would have created a circular dep with layout. The duplication is doc-flagged so future drift gets noticed. Four new tests pin both regimes: the height-limiter activates on a 1920×1080 window, stays inactive on a 900×1600 portrait window, and the worst-case 13-card column fits on both 1280×800 and 1920×1080 within the bottom margin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>