/* The board, the clue bar and the keyboard.

   The look is a newspaper puzzle: one left margin that everything hangs off, hairline
   rules instead of cards, a serif for anything you read and a sans for anything you
   count. The only colour is the players' own, because that is the game.

   Board colours are Theme.swift, declared twice, light and dark, so a browser and a
   phone looking at the same puzzle show the same grid. */

:root {
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;

  /* The dark square in the brand mark, the same one index.html and the invitation use. */
  --slab: #22252b;

  --grid-line: #1a1a1a;
  --grid-frame: #1a1a1a;
  --grid-block: #1a1a1a;
  --grid-empty: #ffffff;
  --grid-glyph: #1a1a1a;
  --grid-num: #6b6b6b;
  --grid-selected: #ffd44d;
  --grid-entry: rgba(51, 130, 237, .20);
  --grid-clue: #ecebe6;
  --grid-wrong: #ffb0b0;
  --grid-wrong-rule: #cf2b2b;
  --grid-wrong-ink: #6d0f0f;
  --grid-tint: .22;

  /* Squares that were handed over rather than worked out. */
  --p-nobody: 150,150,150;

  /* One colour per player, in the order PlayerPalette hands them out. */
  --p0: 51,130,237;   --p1: 242,107,89;  --p2: 66,173,107;  --p3: 148,102,222;
  --p4: 242,179,51;   --p5: 41,168,173;  --p6: 230,112,168; --p7: 107,122,143;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Light rules on a dark board. Black ones would disappear into the squares and the
       grid would read as one slab. */
    --slab: #0c0e11;

    --grid-line: #6f6f6f;
    --grid-frame: #6f6f6f;
    --grid-block: #0b0b0b;
    --grid-empty: #333333;
    --grid-glyph: #f5f5f5;
    --grid-num: #9e9e9e;
    --grid-selected: #a3781c;
    --grid-entry: rgba(120, 175, 255, .22);
    --grid-clue: #262a30;
    --grid-wrong: #7a2020;
    --grid-wrong-rule: #ff7676;
    --grid-wrong-ink: #ffe2e2;
    --grid-tint: .42;
  }
}

/* A class that sets display beats the hidden attribute on its own, and the board, the
   keyboard and the status bar are all switched with hidden. */
[hidden] { display: none !important; }

body.playing {
  display: block;
  padding: 0;
  min-height: 100dvh;
}

/* A fixed height, not a minimum, because the board is sized from what is left over once
   the header, the names, the clue and the keyboard have taken theirs. Eight players push
   the roster onto three lines, and on a small phone that has to come out of the grid
   rather than off the bottom of the screen. */
.game {
  --gutter: 18px;
  display: flex;
  flex-direction: column;
  height: 100dvh;
  max-width: 620px;
  margin: 0 auto;
  overflow: hidden;
}

/* One left edge. The masthead, the names, any notice, the clue, the grid and the keys
   all start here, which is the whole of the layout. */
.gamebar,
.roster,
.status,
.layout,
.keys { padding-left: var(--gutter); padding-right: var(--gutter); }

/* ------------------------------------------------------------------ masthead */

.gamebar {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding-top: 16px;
  padding-bottom: 11px;
  border-bottom: 1px solid var(--rule);
}

.gamebar .grow { flex: 1; min-width: 0; }

/* The one control on the board: everything that is not the puzzle lives behind it. */
.icon {
  flex: none;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  padding: 0;
  cursor: pointer;
  color: var(--ink);
  background: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
}

.icon:hover { border-color: var(--ink); }

/* The mark is the way back out to the site, which is what a masthead is for. Leaving
   costs nothing: the board is on the server and the link comes back to the same seat,
   in the same colour, with the same answers. */
.mast {
  flex: none;
  width: 36px;
  height: 36px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 2px;
}

.mast:hover { border-color: var(--ink); }
.mast .mk { display: grid; grid-template-columns: repeat(2, 8px); grid-template-rows: repeat(2, 8px); gap: 2px; }
.mast .mk i { display: block; }
.mast .mk i:nth-child(1) { background: rgb(var(--p0)); }
.mast .mk i:nth-child(2) { background: rgb(var(--p1)); }
.mast .mk i:nth-child(3) { background: rgb(var(--p2)); }
.mast .mk i:nth-child(4) { background: var(--slab); }
.icon svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.gamebar h1 {
  font-family: var(--serif);
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: -.01em;
  margin: 0;
  line-height: 1.15;
}

.gamebar .meta {
  margin: 5px 0 0;
  font-size: .7rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ------------------------------------------------------------------ the byline
   Who is in, in the order they arrived, each with the colour their letters carry and
   how many of them are on the board. It opens the scores and the join code. */

.roster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  /* Spacing rather than rules between names: a rule looks right on one line and wrong
     the moment the row wraps, because the second line then starts indented past it. */
  gap: 9px 22px;
  padding-top: 11px;
  padding-bottom: 12px;
  width: 100%;
  border: none;
  border-bottom: 1px solid var(--rule);
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}

.roster .who {
  display: flex;
  align-items: center;
  gap: 7px;
  max-width: 100%;
}

.roster .who .dot {
  width: 9px;
  height: 9px;
  background: rgb(var(--player));
  flex: none;
}

.roster .who .name {
  font-size: .72rem;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.roster .who.me .name { font-weight: 800; }

.roster .who .tally {
  font-size: .72rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}

.roster .who .tally::before { content: "·"; padding-right: 6px; opacity: .55; }

.roster:hover .name { text-decoration: underline; text-underline-offset: 3px; }

/* ------------------------------------------------------------------ buttons
   A rule and a word in small capitals. Nothing is rounded, and nothing is filled until
   you are over it. */

.pill {
  font: inherit;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  cursor: pointer;
  color: var(--ink);
  background: none;
  border: 1px solid var(--ink);
  border-radius: 2px;
  padding: 8px 13px;
  white-space: nowrap;
}

.pill:hover { background: var(--ink); color: var(--bg); }

/* A button waiting on the network says so by going quiet, not by disappearing. */
.pill:disabled { opacity: .5; cursor: default; }
.pill:disabled:hover { background: none; color: var(--ink); }

/* ------------------------------------------------------------------ notices */

.status {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-top: 12px;
  padding-bottom: 12px;
  /* The rule under it carries the state, rather than a bar down the side: everything on
     this page starts at the same left margin, decoration included. */
  border-bottom: 2px solid var(--ink);
}

.status span {
  flex: 1;
  min-width: 0;
  font-family: var(--serif);
  font-size: 1.02rem;
}

/* The way on to the next puzzle sits in the line that says this one is out. */
.status .pill { flex: none; }

.status.good { border-bottom-color: rgb(var(--p2)); }
.status.bad { border-bottom-color: var(--grid-wrong-rule); }

/* ------------------------------------------------------------------ layout */

.layout {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.boardside {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* On a phone the clue sits under the board, within reach of the thumb and right above
   the keyboard, which is where the app puts it. The wide layout moves it up top. */
.boardside .boardwrap { order: 1; }
.boardside .cluebar { order: 2; }

.boardwrap {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding: 12px 0;
}

/* A 15 or a 21 does not fit the width of a phone at a square you can hit, so it scrolls
   instead of shrinking. sizeBoard puts this class on only when the grid is wider than
   the space, so every board that does fit is untouched. */
.boardwrap.scrolls {
  overflow: auto;
  align-items: flex-start;
  justify-content: flex-start;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}

.boardwrap.scrolls .board { flex: 0 0 auto; }

/* ------------------------------------------------------------------ board */

.board {
  /* Set from the space the wrapper actually has, in sizeBoard. */
  --board: 300px;
  /* The square, rules taken out. Set from sizeBoard alongside --board, and only a
     fallback here so the file still reads on its own. */
  --square: calc((var(--board) - (var(--cols) + 1) * 1px) / var(--cols));
  width: var(--board);
  display: grid;
  grid-template-columns: repeat(var(--cols), 1fr);
  /* Rows have to be declared too. Left implicit they size to their content, so a row
     with no letters in it yet is shorter than the rows around it and the grid breathes
     as the puzzle fills. */
  grid-template-rows: repeat(var(--rows), 1fr);
  aspect-ratio: var(--cols) / var(--rows);
  outline: 2px solid var(--grid-frame);
  background: var(--grid-line);
  gap: 1px;
  padding: 1px;
  touch-action: manipulation;
}

.sq {
  position: relative;
  border: 0;
  border-radius: 0;
  font-family: inherit;
  padding: 0;
  appearance: none;
  /* One square of room when the board is scrolled to bring this one back into view, so
     the square being typed in lands inside the board rather than flush against its edge
     with the rest of the answer hidden. */
  scroll-margin: 27px;
  background: var(--grid-empty);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  color: var(--grid-glyph);
  font-size: calc(var(--square) * .52);
  line-height: 1;
  padding-top: calc(var(--square) * .1);
  user-select: none;
  cursor: pointer;
}

.sq.block { background: var(--grid-block); cursor: default; }

/* A letter sits on a wash of its author's colour. Mixed into the square's own colour
   rather than laid over it as a transparency: the grid rules behind the squares are
   black, and a see-through tint picks them up and turns muddy. The tint that reads on
   white is invisible on a dark square, so the dark board takes a heavier one. */
.sq.filled {
  background-color: color-mix(in srgb, rgb(var(--player)) calc(var(--grid-tint) * 100%), var(--grid-empty));
}

/* The answer you are on is a wash laid over whatever the square already is, so the
   colour of whoever filled it survives underneath. */
.sq.entry { background-image: linear-gradient(var(--grid-entry), var(--grid-entry)); }

.sq.selected { background-color: var(--grid-selected); background-image: none; }

/* Only after somebody asks. Checking is a request, not a running commentary: a square
   that is wrong looks exactly like one nobody has got to yet until it is asked for.
   A player's own colour can be pink too, so being wrong is not left to the wash alone:
   it takes a rule around the square and a different colour of letter. */
.sq.wrong {
  background-color: var(--grid-wrong);
  background-image: none;
  color: var(--grid-wrong-ink);
  box-shadow: inset 0 0 0 2px var(--grid-wrong-rule);
}

.sq.selected.wrong { box-shadow: inset 0 0 0 3px var(--grid-selected); }

.sq .n {
  position: absolute;
  top: 1px;
  left: 3px;
  font-size: calc(var(--square) * .24);
  font-weight: 500;
  color: var(--grid-num);
  padding: 0;
  line-height: 1.1;
}

/* ------------------------------------------------------------------ the clue you are on */

.cluebar {
  display: flex;
  align-items: baseline;
  gap: 14px;
  padding: 13px 0;
  border-top: 1px solid var(--rule);
  color: var(--ink);
}

.cluebar button:not(.clue) {
  font: inherit;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  background: none;
  border: 1px solid var(--rule);
  border-radius: 2px;
  color: inherit;
  /* As tall as the label and the clue together, so the arrows read as part of the row
     rather than two chips floating above it. */
  width: 40px;
  align-self: stretch;
  min-height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex: none;
  order: 3;
}

/* A drawn chevron rather than a typed one: the glyph carries its own vertical metrics
   and sits low in the box however the box is centred, and the path does not. */
.cluebar button svg {
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.cluebar button:hover { border-color: var(--ink); }
.cluebar button:first-of-type { order: 2; }
.cluebar button.clue {
  background: none; border: 0; padding: 0; text-align: left; color: inherit; font: inherit;
}

.cluebar .clue { flex: 1; min-width: 0; cursor: pointer; order: 1; }

.cluebar .label {
  display: block;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 3px;
}

.cluebar .text {
  display: block;
  font-family: var(--serif);
  font-size: 1.15rem;
  line-height: 1.3;
}

/* ------------------------------------------------------------------ clue lists */

.cluelists { display: none; }

.cluelists h3 {
  margin: 0 0 8px;
  padding-bottom: 7px;
  border-bottom: 1px solid var(--ink);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink);
}

.cluelists ol {
  list-style: none;
  margin: 0;
  padding: 0 6px 0 0;
  overflow-y: auto;
  height: 100%;
}

.cluelists li {
  padding: 0;
  cursor: pointer;
  font-family: var(--serif);
  font-size: 1rem;
  line-height: 1.35;
  box-shadow: inset 3px 0 0 transparent;
}

.cluelists li:hover { background: var(--grid-clue); }

.cluelists li b {
  min-width: 22px;
  text-align: right;
  font-family: inherit;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.cluelists li.done { color: var(--muted); }

/* The answer you are in, and the one crossing it, which is the pair a solver reads. */
.cluelists li.crossing { background: var(--grid-clue); }
.cluelists li.active {
  background: var(--grid-clue);
  box-shadow: inset 3px 0 0 var(--ink);
  font-weight: 600;
}

/* ------------------------------------------------------------------ keyboard */

.keys {
  padding-top: 8px;
  padding-bottom: calc(10px + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  gap: 6px;
  border-top: 1px solid var(--rule);
}

.keys .row { display: flex; gap: 5px; justify-content: center; }

.keys button {
  font: inherit;
  font-size: 1.2rem;
  font-weight: 500;
  height: 46px;
  flex: 1;
  max-width: 46px;
  cursor: pointer;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 3px;
  padding: 0;
  user-select: none;
  touch-action: manipulation;
}

.keys button:active { background: var(--grid-clue); }
.keys button.wide { max-width: 62px; flex: 1.35; font-size: 1.05rem; }

/* ------------------------------------------------------------------ wide
   Room for both clue lists, so the page reads like a printed puzzle rather than a phone
   holding one clue at a time. A physical keyboard is a fair assumption at this width,
   which is why the drawn one goes. */

@media (min-width: 900px) {
  .game { max-width: 1180px; --gutter: 28px; }

  .layout {
    flex-direction: row;
    gap: 32px;
    padding-bottom: 20px;
    align-items: stretch;
  }

  .boardside { flex: 1 1 auto; min-width: 0; }
  .boardwrap { padding: 0; align-items: flex-start; }

  /* The clue you are on reads as a caption over the grid, under a rule of its own. */
  .boardside .cluebar {
    order: -1;
    border-top: none;
    border-bottom: 1px solid var(--rule);
    padding-top: 0;
    margin-bottom: 16px;
  }

  .cluelists {
    display: flex;
    gap: 26px;
    flex: 0 0 clamp(340px, 42%, 520px);
    min-height: 0;
  }

  .cluelists section {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  .keys { display: none; }
}

/* ------------------------------------------------------------------ confetti */

.confetti {
  position: fixed;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 20;
}

.confetti i {
  position: absolute;
  top: -12px;
  width: 9px;
  height: 14px;
  animation: fall linear forwards;
}

@keyframes fall {
  to { transform: translateY(102vh) rotate(720deg); }
}

@media (prefers-reduced-motion: reduce) {
  .confetti { display: none; }
}

/* ------------------------------------------------------------------ scores */

dialog {
  border: 1px solid var(--ink);
  border-radius: 2px;
  background: var(--panel);
  color: var(--ink);
  padding: 26px;
  width: min(92vw, 400px);
  box-shadow: var(--shadow);
}

dialog::backdrop { background: rgba(20, 22, 26, .45); }

dialog h2 {
  margin: 0 0 6px;
  font-family: var(--serif);
  font-size: 1.4rem;
  font-weight: 700;
}

.cheer {
  margin: 0 0 18px;
  font-family: var(--serif);
  color: var(--muted);
  font-size: 1rem;
}

.scores {
  display: flex;
  flex-direction: column;
}

.scores .line {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 0;
  border-bottom: 1px solid var(--rule);
  font-size: .92rem;
}

.scores .dot { width: 9px; height: 9px; background: rgb(var(--player)); flex: none; }

.scores .who {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: .74rem;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
}

.scores .me { font-weight: 800; }
.scores .count { font-weight: 700; font-variant-numeric: tabular-nums; }
.scores .unit { font-size: .72rem; color: var(--muted); letter-spacing: .03em; }

.joincode {
  font: 700 1.5rem/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: .16em;
  color: var(--ink);
  display: block;
  margin: 10px 0 14px;
  user-select: all;
}

.note.count { margin: 10px 0 0; font-weight: 600; color: var(--ink); }

dialog .close { margin-top: 18px; width: 100%; }

/* ------------------------------------------------------------------ the settings sheet */

.section {
  margin: 22px 0 8px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--ink);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
}

.note { margin: 0; color: var(--muted); font-size: .84rem; line-height: 1.5; }

.invite-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.invite-acts a { text-decoration: none; text-align: center; }
.group-invite { overflow-wrap: anywhere; user-select: all; font: .76rem/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted); }

/* Sending the board somewhere, in the ink colour rather than each network's own: the
   only colour allowed on this page is the players'. */
.sharerow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.sharemark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  cursor: pointer;
  color: var(--ink);
  background: none;
  border: 1px solid var(--line);
  border-radius: 2px;
}
.sharemark:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.sharemark svg { display: block; width: 17px; height: 17px; fill: currentColor; }

.pill.wide { display: block; width: 100%; margin-top: 8px; text-align: center; }
.pill.danger { border-color: var(--grid-wrong-rule); color: var(--grid-wrong-rule); }
.pill.danger:hover { background: var(--grid-wrong-rule); color: var(--panel); }

/* Telling us something. The field borrows the join form's input rather than inventing a
   second one, because there are only two places on this site where anybody types a
   sentence and they should look the same. */
/* A pill is a word or two and holds one line. These are sentences, and the longest of
   them runs out of the dialog and off the side of a narrow phone rather than wrapping,
   so here alone the line is allowed to break. */
#report-reasons .pill { margin-top: 10px; text-align: left; white-space: normal; }
#report-prompt { margin: 16px 0 8px; }
#report-body {
  font: inherit;
  width: 100%;
  padding: 11px 12px;
  border-radius: 2px;
  border: 1px solid var(--line);
  background: var(--bg);
  color: var(--ink);
  resize: vertical;
}
#report-body:focus { outline: 2px solid var(--ink); outline-offset: -1px; }
#report-with { margin-top: 8px; }
#report-foot { margin-top: 18px; }
#report #report-send { background: var(--ink); color: var(--bg); }
#report #report-send:hover { background: none; color: var(--ink); }
#report #report-send:disabled { background: none; color: var(--ink); }

.ask-actions { display: flex; gap: 10px; margin-top: 20px; }
.ask-actions .pill { flex: 1; text-align: center; }
#ask #ask-yes { background: var(--ink); color: var(--bg); }
#ask #ask-yes:hover { background: none; color: var(--ink); }

/* ------------------------------------------------------------------ notices */

.notice {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  background: var(--ink);
  color: var(--bg);
  padding: 10px 16px;
  border-radius: 2px;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  box-shadow: var(--shadow);
  z-index: 10;
}

.session-recovery {
  width: min(30rem, calc(100% - 32px));
  box-sizing: border-box;
  font-size: 1rem;
  line-height: 1.5;
  letter-spacing: normal;
  text-transform: none;
}
.session-recovery p { margin: 0 0 12px; }
.session-recovery button {
  padding: 9px 14px;
  background: var(--bg);
  color: var(--ink);
  border: 1px solid currentColor;
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}
.session-recovery button:focus-visible { outline: 2px solid var(--bg); outline-offset: 3px; }

/* Native controls preserve the newspaper layout and expose a visible keyboard focus. */
.clue-choice { display: flex; gap: 10px; width: 100%; padding: 5px 6px 5px 9px; border: 0; background: none; color: inherit; font: inherit; text-align: left; cursor: pointer; }
.sq:focus-visible { outline: 3px solid var(--ink); outline-offset: -3px; z-index: 1; }
.clue-choice:focus-visible, .cluebar .clue:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
