/* ── Responsive layout ───────────────────────────────────────────────── At 1180px and below the columns wrap: the board column takes a full-width row and the results and PGN columns wrap beneath it. Sizes are authored in rem. */ @media (max-width: 1180px) { html, body { height: auto; overflow-y: auto; } :root { --pgn-col-w: 18.75rem; } .content { grid-template-columns: minmax(0, 1fr) var(--pgn-col-w); height: auto; overflow: visible; padding: var(--space-4); } .left-col { grid-column: 1 / -1; height: auto; } /* Horizontal auto margins centre a fixed-width flex item on the cross axis of a column-direction container. */ .board-group { margin: 0 auto; } .right-col, .pgn-col { height: min(60vh, 520px); } } /* ── Phone: swipeable screens ─────────────────────────────────────────── Below 768px the layout is three horizontally swipeable screens in column order: board, results, PGN. Each screen fills the space between the header and the bottom screen navigation; a horizontal swipe moves to the next one, while a vertical swipe scrolls that screen's own content. The board is sized to the visible height so the whole board screen fits; below its 14rem floor the screen scrolls from the top. */ @media (max-width: 767px) { :root { --screen-nav-h: calc(3rem + env(safe-area-inset-bottom, 0px)); --viewport-h: 100vh; /* Fixed heights above and around the board: three control rows, column padding 1rem, gaps 1.875rem, error line 0.875rem plus 0.5rem slack, tray padding 1.25rem and tray borders 4px. The remainder holds the board and its two trays: 1.25 x board. Update when the height above the board changes. */ --board-fit-h: calc((var(--viewport-h) - var(--header-h) - var(--screen-nav-h) - 5.5rem - 3 * var(--control-h) - 4px) / 1.25); --board-size: max(14rem, min(var(--board-size-cap), var(--board-fit-h))); } @supports (height: 100dvh) { :root { --viewport-h: 100dvh; } } html, body { overflow-x: hidden; } .content { grid-template-columns: repeat(3, 100vw); overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scroll-snap-type: x mandatory; min-height: 0; height: calc(100vh - var(--header-h) - var(--screen-nav-h)); height: calc(100dvh - var(--header-h) - var(--screen-nav-h)); width: 100vw; max-width: none; padding: 0; gap: 0; } .left-col, .right-col, .pgn-col { grid-column: auto; max-width: 100vw; min-height: 0; height: calc(100vh - var(--header-h) - var(--screen-nav-h)); height: calc(100dvh - var(--header-h) - var(--screen-nav-h)); scroll-snap-align: start; /* Containing block for absolutely positioned descendants (e.g. .sr-only). */ position: relative; } .left-col { overflow-y: auto; padding: var(--space-2) var(--space-4); } .board-group { margin: 0 auto; } .right-col, .pgn-col { padding: var(--space-4); overflow: hidden; } /* Logo and nav span two rows; the subtitle sits below the title. */ .site-header { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-rows: auto auto; align-content: center; column-gap: 0.625rem; row-gap: 0; } .site-header__logo, .site-header__nav { grid-row: 1 / span 2; } .site-header__brand { display: contents; } .site-header__sep { display: none; } .site-header__title, .site-header__sub { grid-column: 2; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.2; } .site-header__title { grid-row: 1; align-self: end; } .site-header__sub { grid-row: 2; align-self: start; font-size: 0.6875rem; } .screen-nav { display: grid; } .toast-container { bottom: calc(var(--screen-nav-h) + var(--space-3)); } /* The moves list owns vertical panning so a drag on it is not claimed by .content's horizontal scroll-snap. */ .moves-body-scroll { touch-action: pan-y; -webkit-overflow-scrolling: touch; overscroll-behavior-y: contain; } /* The table spans the full screen width, so it is not widened for the scrollbar. */ .moves-body-scroll.has-vscroll .moves-table--body { width: 100%; } .move-line { --pgn-rank-width: var(--pgn-rank-w-compact); } } @media (max-width: 480px) { :root { --board-size-cap: min(360px, 88vw); } .left-col, .right-col, .pgn-col { padding-left: max(var(--space-4), env(safe-area-inset-left)); padding-right: max(var(--space-4), env(safe-area-inset-right)); } .site-header { padding-left: max(var(--space-5), env(safe-area-inset-left)); padding-right: max(var(--space-5), env(safe-area-inset-right)); } /* Title, count and CSV button do not fit one row at this width. */ .panel-header { flex-wrap: wrap; row-gap: 6px; } /* .btn--grow deliberately doesn't shrink (readable label + icon at any width) - six buttons can't all fit one row this narrow, so the toolbar wraps instead of overflowing. */ .fen-actions { flex-wrap: wrap; row-gap: 6px; } } /* ── Touch input sizing ───────────────────────────────────────────────── (hover: none) and (pointer: coarse) targets touchscreens regardless of viewport width. Control heights come from --control-h in tokens.css. Inputs use a >=16px font size because iOS Safari zooms the page when a focused input's font size is under 16px. */ @media (hover: none) and (pointer: coarse) { .fen-input { font-size: var(--text-lg); } .pgn-dialog__textarea { font-size: var(--text-lg); } /* #board handles its own drag gestures; letting the browser also interpret them as page-scroll/pinch-zoom fights the drag. */ .spare-piece, .promotion-piece-btn { touch-action: none; } }