/* THE play screen's layout — the one owner of where .game-main's children sit, in every focus
   mode, at every width. Component looks stay in play/play-board.css + play-panels.css, shell chrome in shell/app-*.css;
   neither may place a play column again. Linked LAST of the play sheets, so a mode rule wins by
   position and needs no !important and no specificity race with the base it overrides.

   Three contracts, each silent if broken:
   --play-layout  row | stack   the stacking breakpoint, published so board-fit.js never mirrors
                                the number. It is declared HERE and nowhere else.
   --rail-l / --rail-r          the width each side column occupies BESIDE the board, 0 when it
                                does not (stacked, hidden, absent). They ARE the grid's side
                                tracks, so the sizer's arithmetic and the layout are the same
                                two numbers rather than two guesses about each other.
   is-plan-column / is-plan-live / is-left-collapsed
                                published on .game-screen by renderGame. A token the board's
                                track reads cannot be re-pinned from inside a column, so every
                                fact the layout needs lands on the screen element.

   The board is never centred by a phantom margin. It takes the width the columns leave, and a
   hidden column's track collapses to 0 — which is what centres it when a side is empty. Sizing
   it for symmetry instead cost the spectator ~390px of board. */

.game-screen {
  --play-gap: 18px;
  --lp-w: 296px;
  --rp-w: 388px;
  --rail-l: var(--lp-w);
  --rail-r: var(--rp-w);
  --play-layout: row;
  /* Which of the three End-turn mounts is live. Exactly one is not `none` in every combination
     below; ten rules across three files used to decide it. */
  --et-panel: flex;
  --et-board: none;
  --et-band: none;
  /* No scroll anchoring on this screen. The board is re-fitted a frame after every STATE_SYNC and
     the panels move with the result; the browser compensates by shifting the PAGE scroll, which
     reads as the screen creeping downwards on every move. */
  overflow-anchor: none;
}

/* The rails are minimums with an EQUAL flex factor, so width the board does not use is split
   evenly between them: the board centres itself for free whenever it is height-capped, and gives
   the room back — never centring by shrinking — when it is not. Fixed tracks left the spare
   OUTSIDE the grid, which put the board half a rail off centre and cut the action band short. */
.game-main {
  display: grid;
  grid-template-columns:
    minmax(var(--rail-l), 1fr) minmax(0, auto) minmax(var(--rail-r), 1fr);
  grid-template-areas:
    "left board right"
    "band band  band";
  align-items: start;
  justify-content: center;
  gap: var(--play-gap);
  width: 100%;
}

/* min-width:0 on all three: a grid item's automatic minimum is its MAX-CONTENT, and the player
   bar's six columns floor the board card near 460px — which overflowed the track and clipped the
   play column wherever the row was tight. */
.game-main > .left-col-wrap {
  grid-area: left; justify-self: end; width: var(--lp-w); min-width: 0;
}
.game-main > .board-area {
  grid-area: board; justify-self: center; min-width: 0;
  display: flex; flex-direction: column;
}
.game-main > .right-panel {
  grid-area: right; justify-self: start; width: var(--rp-w); min-width: 0;
}
.game-main > .ultra-focus-actions { grid-area: band; }

/* No max-height on the board card: board-fit.js measures the room it actually has, and an 85vh
   cap it does not know about clipped the card out from under the board — in focus mode, where
   the recovered chrome is largest, the left panel painted over the board's last rows. */
.game-main > .board-area { max-height: none; }

.left-panel { width: 100%; }
.left-panel  > .btn-end-turn { display: var(--et-panel); }
.game-end-turn-mobile        { display: var(--et-board); }
.ultra-focus-actions         { display: var(--et-band); }

/* Collapsing a column re-pins its rail, so the board takes the room back instead of leaving a
   gap where the panel was. The left panel's End turn is hidden with the rest of it, so the copy
   below the board becomes the live one. */
.game-screen.is-left-collapsed { --lp-w: 34px; --et-panel: none; --et-board: block; }
.game-screen.is-rp-collapsed   { --rp-w: 32px; }
/* A spectator has no left column in the DOM at all, and answers no prompts. */
.game-screen.is-spectating     { --lp-w: 0px; --rail-l: 0px; --et-panel: none; --et-board: none; }

.left-col-wrap.is-collapsed .left-panel > *:not(.play-panel-toggle) { display: none; }
.right-panel.is-collapsed   > *:not(.play-panel-toggle) { display: none; }

/* No padding on TOP: the column gap above already separates it, and adding to that stood End turn
   noticeably further from the plan than the plan sits from the board. */
.ultra-focus-actions {
  justify-content: center;
  padding: 0 8px 12px;
  box-sizing: border-box;
}
.ultra-focus-actions > .btn-end-turn {
  width: 100%; max-width: 420px;
  justify-content: center;
  padding: 12px 16px;
  font-size: 1rem; font-weight: 700;
}

/* ── Stacked: board, then the columns under it, then the action band ───────────────────────── */
/* The stacking breakpoint. This number lives HERE only — JS reads --play-layout. */
@media (max-width: 1440px) {
  .game-screen {
    --play-layout: stack;
    --rail-l: 0px;
    --rail-r: 0px;
    --et-panel: none;
    --et-board: block;
  }
  /* FLEX here, not a one-column grid: a grid row whose only item is `display: none` still costs
     its gaps, so ultra-focus (no right panel) opened a phantom 18px under the plan and stood End
     turn 50px clear of the line it commits. A flex column skips a hidden child entirely. */
  .game-main {
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }
  .game-main > .board-area          { order: 1; }
  .game-main > .left-col-wrap       { order: 2; }
  .game-main > .right-panel         { order: 3; }
  .game-main > .ultra-focus-actions { order: 4; }
  /* `align-self`, NOT `justify-self`: the latter is grid-only and does nothing in a flex column,
     so the board card inherited `align-items: stretch` and spread across the whole row with the
     grid hugging its left edge. The fused column is content-width and centred; the side columns
     take the full width, which is the container default. */
  .game-main > .board-area { align-self: center; }
  .game-main > .left-col-wrap,
  .game-main > .right-panel { width: auto; }
  /* Nothing distributes a measured height here, so every column scrolls with the page rather
     than inside itself. Released together: releasing only two left the left panel a 85vh box
     with its own scrollbar in the middle of a page that already scrolls. */
  .left-col-wrap, .left-panel, .right-panel, .game-panel {
    max-height: none; height: auto; overflow: visible;
  }
  /* The collapse toggles are a row-layout affordance; below this width a collapsed column would
     be unreachable to undo, so both columns come back open. */
  .play-panel-toggle { display: none; }
  .left-col-wrap.is-collapsed .left-panel > *:not(.play-panel-toggle, .btn-end-turn),
  .right-panel.is-collapsed   > *:not(.play-panel-toggle) { display: revert; }
  .left-col-wrap.is-collapsed .left-panel > .move-plan { display: flex; }
  /* A queued line and the button that commits it must not have the panel's cards between them,
     so End turn comes back INTO the column, directly under the plan (which leads it, order:-1).
     The board's own row stands down — it lives inside .board-area, where `order` cannot lift the
     plan above it across parents. */
  .game-screen.is-plan-live { --et-panel: flex; --et-board: none; }
  /* The decider says PANEL here, but the collapse hide above is (0,4,0) and the mount rule only
     (0,2,0) — so a collapsed left column with a live plan below 1440px had End turn at ZERO
     visible mounts, with the collapse toggle hidden and no way to recover. */
  .game-screen.is-plan-live .left-col-wrap.is-collapsed .left-panel > .btn-end-turn {
    display: var(--et-panel);
  }
}

/* ── Focus: the shell chrome goes; the play columns stay until there is no room ────────────── */
/* Short OR narrow, either focus flavour: the board, its plan, and End turn. The height arm is
   deliberately unbounded in width — a window under 601px tall has no room for a column under
   the board however wide it is. */
@media (max-width: 640px), (max-height: 600px) {
  .app-shell.is-focus .game-screen {
    --rail-l: 0px; --rail-r: 0px;
    --et-panel: none; --et-board: none; --et-band: flex;
  }
  .app-shell.is-focus .game-main > .left-col-wrap:not(.has-plan),
  .app-shell.is-focus .game-main > .right-panel,
  .app-shell.is-focus .game-screen .ai-mode-panel,
  .app-shell.is-focus .game-screen .setup-stepper,
  .app-shell.is-focus .game-screen .readiness-bar { display: none; }
  .app-shell.is-focus .left-col-wrap.has-plan .left-panel > *:not(.move-plan) { display: none; }
  .app-shell.is-focus .game-main { gap: 8px; }
  /* Both properties: this block fires in EITHER model — narrow is a flex column, but a short WIDE
     window (1920x560) is still the grid. `justify-self` is grid-only, `align-self` is what a flex
     column reads. */
  .app-shell.is-focus .game-main > .board-area { justify-self: stretch; align-self: stretch; }
  /* The bottom bar is hidden in focus, so the room .app-main reserved for it is the board's. */
  .app-shell.is-focus .app-main { padding-bottom: 0; }
  /* The exit pill is fixed to the viewport's top-right, and here the banner reaches it — it sat
     over the clocks. Reserve its lane rather than moving it: the foot is the action band's. */
  .app-shell.is-focus .game-screen .turn-banner { padding-right: 104px; }
}

/* ── Ultra-focus: no panels at any width. A live plan keeps its column beside the board ────── */
.app-shell.is-ultra-focus .game-screen {
  --lp-w: 296px;
  --rail-l: 0px;
  --rail-r: 0px;
  --et-panel: none; --et-board: none; --et-band: flex;
}
/* `has-plan` sits on the COLUMN, which cannot re-pin a token the board's track reads; the
   renderer publishes the same fact on the screen, where both can see it. */
.app-shell.is-ultra-focus .game-screen.is-plan-column { --rail-l: var(--lp-w); }
.app-shell.is-ultra-focus .game-main > .left-col-wrap:not(.has-plan),
.app-shell.is-ultra-focus .game-main > .right-panel { display: none; }
.app-shell.is-ultra-focus .game-main > .left-col-wrap.has-plan { display: flex; }
/* The band above carries End turn, so the column holds the strip alone. */
.app-shell.is-ultra-focus .left-col-wrap.has-plan .left-panel > *:not(.move-plan) { display: none; }
/* ULTRA-focus only: it is the mode with no toggle to undo a collapse, and the only one that
   re-pins `--lp-w` to 296px, which is what gives the strip room. `flex`, not `revert`: revert
   discards move-plan.css's own display and leaves the strip a UA block. */
.app-shell.is-ultra-focus .left-col-wrap.has-plan.is-collapsed .left-panel { width: 100%; overflow: visible; }
.app-shell.is-ultra-focus .left-col-wrap.has-plan.is-collapsed .left-panel > .move-plan { display: flex; }

@media (max-width: 1440px) {
  /* Stacked, the plan column takes the row under the board like any other — no width override
     here: the stacked rule above already sizes it, and a higher-specificity `auto` out-ranked the
     short-landscape cap and stood a 200px strip under a full-width board. */
  .app-shell.is-ultra-focus .game-screen.is-plan-column { --rail-l: 0px; }
}

/* ── Narrow ───────────────────────────────────────────────────────────────────────────────── */
@media (max-width: 640px) {
  .game-main { gap: 8px; }
  /* Edge to edge on a phone — `align-self` is the flex-column one that actually applies here. */
  .game-main > .board-area { justify-self: stretch; align-self: stretch; align-items: flex-start; }
  .game-main > .board-area > * { width: 100%; box-sizing: border-box; }
  .game-main > .board-area .group-status-bar { text-align: left; }
  /* The board is the one exception to the screen's gutter; the columns take it back themselves.
     PADDING, not width + margin: a mode rule that re-pins the width keeps the margins, which is
     how ultra-focus's plan column ran 24px past the right edge of a phone. */
  .game-main > .left-col-wrap,
  .game-main > .right-panel { padding-inline: 0.75rem; }
  .game-panel { flex-direction: column; gap: 10px; }
  .game-panel > * { flex: initial; min-width: 0; width: 100%; }
}

/* ── Portrait tablet / large phone ─────────────────────────────────────────────────────────── */
/* 768, not 640: between the two, a wrapping panel row still fits two cards side by side, which
   is how a large phone got the unit card and the Actions card on one line. */
@media (max-width: 768px) {
  .game-panel { width: 100%; flex-direction: column; gap: 10px; }
  .game-panel > * { flex: initial; min-width: 0; width: 100%; }
  .play-board-header { margin-left: 0; }
  .group-status-bar { margin-inline-start: 0; }
}

/* ── Short landscape ──────────────────────────────────────────────────────────────────────── */
/* The board is sized by measurement, so this block sets no --cell-size of its own — the clamp
   that used to live here was defeated by the sizer's inline value anyway, and only the columns
   still need releasing. */
@media (orientation: landscape) and (max-height: 600px) {
  /* width:100% as well as the cap — `justify-self: center` alone leaves a grid item shrink-wrapped,
     which stood a 200px plan strip in the middle of a full-width board. */
  .game-main > .left-col-wrap,
  .game-main > .right-panel { width: 100%; max-width: 600px; justify-self: center; }
  /* Keeps the stat bar from forcing the board card wider than the board itself. */
  .game-screen .player-bar { flex-wrap: wrap; }
}
