/* ==========================================================================
   codebreaker.css — codebreaker's arena (src/assets/js/games/codebreaker/view.mjs),
   inside the table games.css draws: the hidden code over a ladder of guesses (six
   to twelve rows, the table's choice), each a row of hairline tiles (24's tiles,
   small) with its score beside it in pegs (games.css .g-peg) and in words; the
   pad that fills the row; a legend for the pegs; and a row for each of the
   others. Every class here is .cb-*.
   ========================================================================== */

/* One centred column, all of it mono. --cb-len is the code's length (view.mjs sets it, 3 to 6, and
   says the same in data-digits); everything that depends on it — a row of tiles, a row of pegs, the
   board's width — is worked out from it here, so nothing is measured in script and nothing moves
   when a guess lands. The ladder is as many rows as the game has guesses, each of the same height. */
.cb {
  --cb-len: 4;
  --cb-tile: calc(32 * var(--s, 1px));
  --cb-gap: calc(4 * var(--s, 1px)); /* between two tiles */
  --cb-row-gap: calc(4 * var(--s, 1px)); /* between two rows */
  --cb-col: calc(14 * var(--s, 1px)); /* between a row's number, its tiles and its score */
  --cb-run: calc(var(--cb-len) * var(--cb-tile) + (var(--cb-len) - 1) * var(--cb-gap)); /* a row of tiles */
  --cb-peg-gap: calc(6 * var(--s, 1px));
  --cb-pegs: calc(var(--cb-len) * 11 * var(--s, 1px) + (var(--cb-len) - 1) * var(--cb-peg-gap)); /* a row of pegs, 11px each (games.css) */
  --cb-words: 18ch; /* "2 in place, 1 near" */
  --cb-told: calc(var(--cb-pegs) + 2ch + var(--cb-words)); /* the pegs, a gap, the words */
  display: flex;
  flex-direction: column;
  align-items: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: calc(13 * var(--s, 1px));
  color: var(--ink);
}
/* A code of six digits: with 32px tiles its row, the score to the right of it and as much room
   again to the left (below), would be 744px — 24 more than the table's column, and the tiles would
   leave the centre line. So its tiles are 28px, as on a small phone, and stand 2px closer to the
   number and the score: 716px. (The phones' own sizes for six digits are at the foot of the sheet.) */
.cb[data-digits="6"] {
  --cb-tile: calc(28 * var(--s, 1px));
  --cb-col: calc(12 * var(--s, 1px));
}
.cb[data-digits="6"] .cb-tile { font-size: calc(16 * var(--s, 1px)); }

/* ---- The code and the ladder ------------------------------------------------ */
/* Rows of one height, all there from the start: the code, then one for each guess. A row is its
   number, its tiles, and what the guess was told. The tiles stand on the arena's centre line, over
   the pad that fills them, the number hanging to their left and the score to their right: the two
   outer columns share what room is left, and where there is not enough for that (a phone) the
   score keeps its width and the tiles give way to the left. */
.cb-board {
  inline-size: calc(var(--cb-run) + 2 * (var(--cb-col) + var(--cb-told)));
  max-inline-size: 100%;
  text-align: start;
}
.cb-row {
  display: grid;
  grid-template-columns: minmax(2ch, 1fr) var(--cb-run) minmax(var(--cb-told), 1fr);
  column-gap: var(--cb-col);
  align-items: center;
  block-size: var(--cb-tile);
  margin-block-start: var(--cb-row-gap);
}
.cb-n {
  justify-self: end;
  color: var(--ink-muted);
}
.cb-tiles {
  display: flex;
  gap: var(--cb-gap);
}
/* A tile: 24's hairline square, small. Empty in the row being typed until a digit lands in it. */
.cb-tile {
  display: flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: var(--cb-tile);
  block-size: var(--cb-tile);
  border: 1px solid var(--hairline);
  font-size: calc(17 * var(--s, 1px));
  font-weight: 500;
  line-height: 1;
}
/* What a guess was told: the pegs at their own tight pitch, in a box as wide as a full row of them,
   then the words. */
.cb-told {
  display: flex;
  align-items: center;
  column-gap: 2ch;
  min-inline-size: 0;
  white-space: nowrap;
}
.cb-pegs {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--cb-peg-gap);
  inline-size: var(--cb-pegs);
}
.cb-pegs > .g-peg { flex: none; }
.cb-words {
  min-inline-size: 0;
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink-muted);
}

/* The code: face down — dashed, a question mark, as a hidden die is — while it stands; face up and
   green once the race is over. Its caption sits where a guess has its score. */
.cb-row--code { margin-block: 0 calc(10 * var(--s, 1px)); }
.cb-row--code .cb-pegs { display: none; }
.cb-row--code .cb-tile {
  border-style: dashed;
  color: var(--ink-muted);
  font-weight: 400;
}
.cb-row--open .cb-tile {
  border-style: solid;
  border-color: var(--green);
  color: var(--green);
  font-weight: 500;
}
.cb-row--open .cb-words { color: var(--green); }

/* A guess not reached yet is only its number, as faint as the race strip's track. */
.cb-row--empty .cb-n { color: var(--hairline); }
/* The guess being typed: its number in green, and the next tile to fill green and filled a touch,
   as a picked tile is in 24. */
.cb-row--now .cb-n {
  color: var(--green);
  font-weight: 500;
}
.cb-tile--next {
  border-color: var(--green);
  background: color-mix(in srgb, var(--green) 10%, transparent);
}
/* The latest guess is the one to read: its words in ink, the older ones' muted. */
.cb-row--last .cb-words { color: var(--ink); }
/* The guess that cracked the code: all of it green. (Two classes on the pegs: games.css gives a
   peg its own colour, and may load after this sheet.) */
.cb-row--cracked .cb-n,
.cb-row--cracked .cb-tile,
.cb-row--cracked .cb-words,
.cb-row--cracked .g-peg {
  color: var(--green);
  border-color: var(--green);
}
.cb-row--cracked .cb-n,
.cb-row--cracked .cb-words { font-weight: 500; }
/* A key that could do nothing marks the tiles it is about, for a moment: the ones a short row
   still lacks, the one that already holds the digit, the row that already made this guess. */
.cb-tile.cb-tile--bad {
  border-color: var(--red);
  background: none;
  color: var(--red);
}
/* The score arrives where the guess is: a fade, once, for a guess just made here. */
.cb-told--new { animation: cb-told-in 200ms ease-out; }
@keyframes cb-told-in {
  from { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .cb-told--new { animation: none; }
}

/* ---- The line over the pad, and the pad ---------------------------------------- */
/* Which guess this is, always; beside it, for a moment, what is wrong with the row. One line of a
   fixed height, as wide as the pad. */
.cb-play {
  inline-size: calc(300 * var(--s, 1px));
  max-inline-size: 100%;
  margin-block-start: calc(14 * var(--s, 1px));
}
.cb-line {
  display: flex;
  justify-content: space-between;
  column-gap: 2ch;
  block-size: calc(20 * var(--s, 1px));
  line-height: calc(20 * var(--s, 1px));
  margin-block-end: calc(8 * var(--s, 1px));
  white-space: nowrap;
}
.cb-count {
  flex: none;
  color: var(--ink-muted);
}
.cb-count--cracked {
  color: var(--green);
  font-weight: 500;
}
.cb-note {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--ink-muted);
}
.cb-note--bad { color: var(--red); }
/* Two rows of five digits, then delete and guess: ten columns, a digit across two of them. */
.cb-pad {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: calc(8 * var(--s, 1px));
}
.cb-key {
  grid-column: span 2;
  min-block-size: calc(48 * var(--s, 1px));
  padding: 0;
  font-family: var(--font-mono);
  font-size: calc(18 * var(--s, 1px));
  line-height: 1;
  color: var(--ink);
  background: none;
  border: 1px solid var(--hairline);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition:
    color 150ms ease,
    border-color 150ms ease;
}
.cb-key--delete,
.cb-key--guess {
  grid-column: span 5;
  font-size: calc(13 * var(--s, 1px));
}
/* Guess is the one green key, and only once the row is full. */
.cb-key--guess {
  color: var(--green);
  border-color: var(--green);
  font-weight: 500;
}
@media (hover: hover) {
  .cb-key:hover:not([aria-disabled="true"]) {
    color: var(--pink-text);
    border-color: var(--pink);
  }
}
/* A key with nothing to do: a digit while the row is full, delete while it is empty, guess before
   it is full — and every key once there is nothing left to guess. */
.cb-key[aria-disabled="true"] {
  color: var(--ink-muted);
  border-color: var(--hairline-soft);
  font-weight: 400;
  cursor: default;
}
/* A digit that is in the row is not on the pad: its key loses its box and keeps only a trace of the
   digit, until delete brings it back. */
.cb-key.cb-key--used {
  color: var(--hairline);
  border-color: transparent;
}

/* ---- The others, and the legend ------------------------------------------------ */
/* One row each, right under the pad: their animal, their name, how many guesses they have made, and
   the pegs of their best one — muted, since the ladder above is ours and these are not. The columns
   are the same in every row (the rows lend their cells to one grid), each with a width of its own,
   so nothing moves when a count changes; on a narrow screen it is the name that gives way. */
.cb-others {
  display: grid;
  grid-template-columns: max-content minmax(0, max-content) 10ch max-content;
  justify-content: center;
  align-items: center;
  column-gap: 2ch;
  inline-size: 100%;
  margin-block-start: calc(14 * var(--s, 1px));
  line-height: calc(22 * var(--s, 1px));
  text-align: start;
  white-space: nowrap;
  color: var(--ink-muted);
}
.cb-others:empty { display: none; }
.cb-other { display: contents; }
.cb-other-critter {
  justify-self: end;
  white-space: pre;
}
.cb-other-name {
  min-inline-size: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.cb-other-best {
  display: flex;
  align-items: center;
  column-gap: 1ch;
}
/* Before their first guess there is no best one: the place is kept, empty. */
.cb-other-best--none { visibility: hidden; }
/* Their pegs are muted like the rest of their row: ink is for our own. (Two classes, as above.) */
.cb-others .g-peg { color: var(--ink-muted); }
.cb-other--left > * { opacity: 0.55; }
/* Whoever cracked the code: their pegs, all in place, in green. */
.cb-other--cracked .cb-other-best,
.cb-other--cracked .g-peg { color: var(--green); }
.cb-other--cracked .cb-other-label { font-weight: 500; }
/* What the pegs mean, drawn with the pegs; an item never breaks in two. Under them, on a line of
   its own, the one thing a row of pegs beside a row of digits does not say by itself. */
.cb-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0 3ch;
  margin-block-start: calc(12 * var(--s, 1px));
  font-size: calc(12 * var(--s, 1px));
  line-height: calc(20 * var(--s, 1px));
  color: var(--ink-muted);
}
.cb-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 1ch;
  white-space: nowrap;
}
/* Every peg in a box of a peg's size, so the three captions start at the same distance. */
.cb-legend-peg {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  inline-size: calc(11 * var(--s, 1px));
}
.cb-legend-note {
  flex-basis: 100%;
  text-align: center;
}

/* ---- Phones ---------------------------------------------------------------- */
/* The tiles a little smaller and closer, and a row's words under its pegs, so a row of five digits
   with its score fits a 375px screen; the pad the full width of the column, its keys 46px tall. */
@media (max-width: 600px) {
  .cb {
    --cb-tile: calc(30 * var(--s, 1px));
    --cb-col: calc(10 * var(--s, 1px));
    --cb-told: var(--cb-words);
    font-size: calc(12 * var(--s, 1px));
  }
  .cb-tile { font-size: calc(16 * var(--s, 1px)); }
  /* A code of six digits has 28px tiles here, closer to each other and to the score, so that its
     row and its words fit the screen of an iPhone (390px); its rows stand a little further apart,
     as they do on a 360px phone below, since the pegs and their words nearly fill a row that tall. */
  .cb[data-digits="6"] {
    --cb-tile: calc(28 * var(--s, 1px));
    --cb-gap: calc(3 * var(--s, 1px));
    --cb-col: calc(8 * var(--s, 1px));
    --cb-row-gap: calc(6 * var(--s, 1px));
  }
  /* The pegs and their words keep close (26 of the row's 30px), so the eye pairs them with each
     other and not with the row above or below — and a row is never shorter than the two of them,
     however small its tiles (the smallest, of a six-digit code on a narrow phone, are 23px). */
  .cb-row { block-size: max(var(--cb-tile), calc(26 * var(--s, 1px))); }
  .cb-told {
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    row-gap: calc(2 * var(--s, 1px));
  }
  .cb-words { line-height: calc(13 * var(--s, 1px)); }
  /* Every gap a little closer: with one other player and ten guesses or fewer the whole arena,
     legend and all, fits the screen of an iPhone (390 by 659) under the 8px the view keeps above
     the ladder; with twelve, the ladder and the pad do, and the rest is a scroll below. */
  .cb-row--code { margin-block-end: calc(6 * var(--s, 1px)); }
  .cb-play {
    inline-size: 100%;
    margin-block-start: calc(8 * var(--s, 1px));
  }
  .cb-line { margin-block-end: calc(6 * var(--s, 1px)); }
  .cb-pad { gap: calc(6 * var(--s, 1px)); }
  .cb-key { min-block-size: calc(46 * var(--s, 1px)); }
  .cb-others {
    column-gap: 1.5ch;
    margin-block-start: calc(10 * var(--s, 1px));
  }
  /* The word before their pegs gives its room to their name (a screen reader still hears it). */
  .cb-other-label { display: none; }
  .cb-legend {
    margin-block-start: calc(8 * var(--s, 1px));
    line-height: calc(18 * var(--s, 1px));
  }
}
/* Six digits on a 375px phone: 26px tiles. (Five keep their 30 down to here.) */
@media (max-width: 389.98px) {
  .cb[data-digits="6"] { --cb-tile: calc(26 * var(--s, 1px)); }
  .cb[data-digits="6"] .cb-tile { font-size: calc(15 * var(--s, 1px)); }
}
/* Narrower than that (a 360px phone), the tiles give a little more — and the rows stand a little
   further apart, since the pegs and their words now fill a row's height. Six digits: 24px tiles. */
@media (max-width: 374.98px) {
  .cb {
    --cb-tile: calc(28 * var(--s, 1px));
    --cb-gap: calc(3 * var(--s, 1px));
    --cb-row-gap: calc(6 * var(--s, 1px));
  }
  .cb[data-digits="6"] {
    --cb-tile: calc(24 * var(--s, 1px));
    --cb-gap: calc(2 * var(--s, 1px));
  }
  .cb[data-digits="6"] .cb-tile { font-size: calc(14 * var(--s, 1px)); }
}
/* And on the narrowest (320px) nothing may push the page sideways: the number takes no more than
   its own width, and the score what is left — where five digits or six leave too little for the
   words, they are cut short and the pegs say it all. Six digits: 23px tiles, which leave their six
   pegs just room enough. */
@media (max-width: 359.98px) {
  .cb {
    --cb-tile: calc(26 * var(--s, 1px));
    --cb-col: calc(8 * var(--s, 1px));
  }
  .cb-tile { font-size: calc(15 * var(--s, 1px)); }
  .cb[data-digits="6"] { --cb-tile: calc(23 * var(--s, 1px)); }
  .cb-row { grid-template-columns: 2ch var(--cb-run) minmax(0, 1fr); }
  .cb-others {
    column-gap: 1ch;
    font-size: calc(11 * var(--s, 1px));
  }
}
