/* ==========================================================================
   gomoku.css — gomoku's arena (src/assets/js/games/gomoku/view.mjs), inside
   the table games.css draws: the board as a grid of hairline squares, eleven,
   thirteen or fifteen a side, the discs on it (games.css .g-disc: filled for
   one side, a ring for the other) and a line saying whose are whose. Every
   class here is .gm-*.
   ========================================================================== */

/* The column the board is given is its measure (cqw, below): fifteen squares always fit a phone. */
.gm {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(14 * var(--s, 1px));
}

/* The board: --n squares a side (view.mjs writes the number on the element) with one hairline
   between each two and one all round. The inner lines are the board's own ground showing through
   the 1px gaps and the outer one is its border, so every line is one pixel and none is drawn twice.
   One measure sets the rest: --cell, a square's side. It is the least of three: 56 design pixels,
   the size four in a row's cells are; what the column allows (--fit: 21px for fifteen squares in
   the 335px column of a 375px phone); and what the window's height allows (--tall), so that on a
   desktop the whole game — the table's top line down to its buttons — is on screen at once, which
   fifteen squares of 48px would not be. Height never presses a square under 24 design pixels: a
   window that short scrolls instead. In whole pixels, so the lines fall on the pixel grid.
   (A variable cannot fall back the way a property does, so the measure is built up: by the window,
   for a browser without container units, taking the page's 20px gutters as given; then by the
   column itself; then in whole pixels.) */
.gm-board {
  --n: 15;
  --fit: calc((100vw - 40px - (var(--n) + 1) * 1px) / var(--n));
  --tall: max(calc(24 * var(--s, 1px)), calc((100vh - 300 * var(--s, 1px)) / var(--n)));
  --cell: min(calc(56 * var(--s, 1px)), var(--fit), var(--tall));
  --disc: calc(var(--cell) * 0.7);
  display: grid;
  grid-template-columns: repeat(var(--n), var(--cell));
  grid-auto-rows: var(--cell);
  gap: 1px;
  border: 1px solid var(--hairline);
  background: var(--hairline);
  -webkit-user-select: none;
  user-select: none;
  transition: border-color 150ms ease;
}
@supports (width: 1cqw) {
  .gm-board { --fit: calc((100cqw - (var(--n) + 1) * 1px) / var(--n)); }
}
/* A disc is its square less a whole number of pixels either side, so it sits exactly in the middle. */
@supports (width: 1cqw) and (width: round(down, 1px, 1px)) {
  .gm-board {
    --cell: round(down, min(calc(56 * var(--s, 1px)), var(--fit), var(--tall)), 1px);
    --disc: calc(var(--cell) - 2 * round(var(--cell) * 0.15, 1px));
  }
}
/* Over, the board is a picture under the results, and the whole of it should be on the screen with
   them — the line that won may be anywhere on it. How much height that leaves depends on where the
   page stands, which only the page knows: view.mjs measures it as the game ends and writes --tall
   on the board itself, so the squares give way to the results and never the other way about. */
/* While the turn is this player's the board's own frame is green, as the table's "your turn" is:
   the board says at a glance whether it is waiting on you. */
.gm--live .gm-board { border-color: var(--green); }
/* A row is a row to a screen reader only: its squares lie straight on the board's grid. */
.gm-row { display: contents; }

/* A square: the button that is there while it can be placed on, and the two faces of a disc, all
   three in the one place. What is raised on the board (a marked square, the cursor) is raised over
   its neighbours' lines and over nothing else on the page. */
.gm-cell {
  position: relative;
  display: grid;
  place-items: center;
  background: var(--bg);
  transition: background-color 150ms ease;
}
.gm-cell > * { grid-area: 1 / 1; }
/* A taken square shows the face its mark says: X the filled one, O the ring. A face changes colour
   at once, never by way of another: the faint disc under the pointer is the same element as the
   disc placed there a moment later, and a fade would carry its pink into the board. */
.gm-cell > .g-disc { visibility: hidden; }
.gm-cell[data-mark="X"] > .g-disc--solid,
.gm-cell[data-mark="O"] > .g-disc--ring { visibility: visible; }
/* A ring on a small square keeps its hole: a finer line than the 3px of a large one. */
.gm-cell > .g-disc--ring { border-width: clamp(2px, calc(var(--cell) * 0.08), calc(3 * var(--s, 1px))); }

/* The disc just placed, until the next one: its square framed in green and filled a touch, as 24
   marks the tile that is picked, and the disc itself green. (Four in a row frames the square and
   leaves the disc in ink; on a square of 21px among forty discs the frame alone took looking for.)
   The frame is drawn over the hairlines round the square. */
.gm-cell--last,
.gm-cell--win {
  z-index: 1;
  outline: 1px solid var(--green);
  background: color-mix(in srgb, var(--green) 14%, var(--bg));
}
/* The five that won are green in their frames too, and every other disc steps back: the game is
   over, and the line is what is left to look at. */
.gm-cell--last > .g-disc,
.gm-cell--win > .g-disc { color: var(--green); }
.gm--over .gm-cell:not(.gm-cell--win) > .g-disc { color: var(--ink-muted); }
/* A press on a square that is taken: it says no for a moment (view.mjs takes it off again). */
.gm-cell--no { background: color-mix(in srgb, var(--red) 14%, var(--bg)); }

/* An empty square on this player's turn: the whole square is the button, and it shows nothing
   until it is pointed at. No other square is a control, and none is while the turn is not yours. */
.gm-sq {
  position: absolute;
  inset: 0;
  z-index: 1;
  -webkit-appearance: none;
  appearance: none;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
/* Where a disc would go: this player's own, faint and pink — the face the square would show (the
   button comes first in the square, so the faces are its later siblings) — under the pointer, only
   where there is a hover and only while the pointer was the last to point (view.mjs sets .gm--keys
   once the keyboard is), so the board never shows two places at once. */
@media (hover: hover) {
  .gm--live:not(.gm--keys)[data-you="solid"] .gm-sq:hover ~ .g-disc--solid,
  .gm--live:not(.gm--keys)[data-you="ring"] .gm-sq:hover ~ .g-disc--ring {
    visibility: visible;
    color: var(--pink);
    opacity: 0.55;
  }
}
/* A tap's aim, on a touch screen: the same faint disc, and a pink frame round its square that
   stays until the second tap. Plain enough to be read on a square of 21px. */
.gm-cell--aim {
  z-index: 3;
  outline: 2px solid var(--pink);
  outline-offset: -1px;
}
.gm[data-you="solid"] .gm-cell--aim > .g-disc--solid,
.gm[data-you="ring"] .gm-cell--aim > .g-disc--ring {
  visibility: visible;
  color: var(--pink);
  opacity: 0.7;
}

/* The keyboard's place. The board is the one stop for Tab; what shows where the keys will act is
   the ring on the cursor's square (base.css's ring, drawn inside the square so it covers no line
   of its neighbours), not a ring round the whole board. On your turn an empty square under it
   carries the faint disc, as under the pointer. */
.gm-board:focus-visible { outline: none; }
.gm-board:focus-visible .gm-cell--cursor {
  z-index: 2;
  outline: calc(2 * var(--s, 1px)) solid var(--green);
  outline-offset: calc(-2 * var(--s, 1px));
}
.gm--live.gm--keys[data-you="solid"] .gm-board:focus-visible .gm-cell--cursor[data-mark="."] > .g-disc--solid,
.gm--live.gm--keys[data-you="ring"] .gm-board:focus-visible .gm-cell--cursor[data-mark="."] > .g-disc--ring {
  visibility: visible;
  color: var(--pink);
  opacity: 0.55;
}

/* Whose discs are whose: "you are" and the disc itself, then the other player's. */
.gm-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: calc(2 * var(--s, 1px)) 1.2ch;
}
.gm-who {
  display: inline-flex;
  align-items: center;
  gap: 1ch;
  min-inline-size: 0;
  white-space: nowrap;
}
/* A dot between the two, which is no part of what a screen reader says (the first line is for a
   browser that does not know the second). */
.gm-who + .gm-who::before {
  content: "\00b7";
  content: "\00b7" / "";
}
/* A name gives way before the line breaks (as in the table's top line): twenty characters, the
   longest a name can be typed, fit beside the rest on any but the narrowest phone, so this cuts
   only a name in a wider script. */
.gm-who-name {
  max-inline-size: 20ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.gm-legend .g-disc {
  --disc: calc(13 * var(--s, 1px));
  flex: none;
}
/* A ring this small keeps its hole: a thinner line than the board's. */
.gm-legend .g-disc--ring { border-width: calc(2 * var(--s, 1px)); }

/* ---- A reader's own colours (Windows' high contrast) --------------------------- */
/* There the browser repaints every background as the page's own, and the board's lines, which are
   its ground showing between the squares, would be gone. The board keeps its own painting, with
   the site's colours named over again as the reader's: lines in the system's grey, squares on its
   canvas, discs in its text colour, and its highlight wherever the board is green or pink. A fill
   of fourteen parts in a hundred says little there, so the marks are a second line inside the
   square as well. */
@media (forced-colors: active) {
  .gm-board {
    forced-color-adjust: none;
    --bg: Canvas;
    --ink: CanvasText;
    --ink-muted: CanvasText;
    --hairline: GrayText;
    --green: Highlight;
    --pink: Highlight;
    --red: CanvasText;
  }
  /* (games.css gives a filled disc the text colour outright; on the board it follows its own.) */
  .gm-cell > .g-disc--solid { background: currentColor; }
  .gm-cell--last,
  .gm-cell--win,
  .gm-cell--aim {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
  .gm-cell--no {
    outline: 2px dashed CanvasText;
    outline-offset: -2px;
  }
}

/* ---- Less motion ------------------------------------------------------------ */
/* A disc placed stands where it is at once (view.mjs plays no arrival), and the board's colours
   change without a fade. */
@media (prefers-reduced-motion: reduce) {
  .gm-board,
  .gm-cell { transition: none; }
}

/* ---- Phones ---------------------------------------------------------------- */
@media (max-width: 600px) {
  .gm { gap: calc(12 * var(--s, 1px)); }
}
@media (max-width: 359.98px) {
  .gm-who-name { max-inline-size: 12ch; }
}
