:root {
  --blue: #1897FF;
  --green: #07E316;
  --magenta: #FF0099;
  /* One red for everything that has gone wrong, so a rejected pin and a rejected score are the same
     colour rather than four shades collected one at a time. Pure red was what most of these used and
     it is a harsh thing to read a sentence in; this is the darker red Bootstrap settled on. */
  --error-red: #b02a37;
  /* The same job on the sidebar, which is a blue to green gradient. The red above on that background
     is very close to unreadable, so a refusal there is a pale pink instead. It is the one place the
     colour has to differ, and it differs because the surface does, not because nobody chose. */
  --error-pink: #FFD9D9;
}

html,
body {
  height: 100%;
  overflow-x: hidden;
  background-color: #1897FF;
  background: linear-gradient(180deg, var(--blue) 0%, var(--green) 100%);
}

body {
  -ms-overflow-style: none;
  scrollbar-width: none;
}

#paste-score {
  min-height: 100px;
}

/* A paste the parser could not make anything of. Pink behind it rather than a message alone,
   because the message is under the box and the box is what you are looking at.

   Worn as a class rather than written onto the element, which is how this used to be done and is
   why the dark theme could not reach it: a colour set in code beats every rule in the stylesheet,
   so the warning came out pale pink with near-white text on it. Light text on a light box is a
   warning you cannot read, printed over the thing it is warning you about. */
#paste-score.paste-unreadable {
  background-color: #FFEFF1;
  color: #212529;
}

/* Matches the refresh symbol next to the preview. */
#paste-button {
  padding-left: 5px;
}

/* The whole line pastes, so it reads as the label it replaced rather than as a link, and gets a
   finger sized target on a phone. */
#paste-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  display: inline-block;
  min-height: 38px;
}

#paste-button-error {
  color: var(--error-red);
}

/* Bootstrap only fades the blue when the button is off, which still reads as something to press. */
#modal-submit:disabled {
  background-color: #6c757d;
  border-color: #6c757d;
}

#modal-error {
  color: var(--error-red);
}

#manual-score-error {
  color: var(--error-red);
}

#manual-score-hint {
  color: #6c757d;
  font-size: 0.85rem;
}

#other-name-error {
  color: var(--error-red);
}

#other-name-known {
  color: darkorange;
}

#other-url-hint {
  color: #6c757d;
  font-size: 0.85rem;
}

#wordle-answer-hint {
  color: #6c757d;
  font-size: 0.85rem;
}

#wordle-answer-error {
  color: var(--error-red);
  font-size: 0.85rem;
}

.game-divider h5 {
  color: #6c757d;
}

#modal-warning {
  color: darkorange;
}

/* The same orange as the Redactle warning above, and for the same reason: both are the modal saying
   the day this is going in under may not be the day you assumed. */
#score-date-note {
  color: darkorange;
  /* A sentence rather than a caption, so it gets a line of its own underneath the box by being made
     full width in a row that is allowed to wrap. */
  flex-basis: 100%;
}

/* Red once the box and the share flatly disagree, which only happens when the day has been set by
   hand. Orange is the modal pointing something out; red is it saying these two cannot both be right,
   which is what the boxes above use it for too. */
#score-date-note.mismatch {
  color: var(--error-red);
}

/* The question the red note leads to, sitting at the foot of the body so it is the last thing read
   before the button it is about. */
#score-date-check {
  color: var(--error-red);
}

/* Sits with the date question, under the preview and above the button, because it is the same kind
   of thing: something about the press that just happened rather than about a field. */
#submit-error {
  color: var(--error-red);
  margin-top: 10px;
}

/* The label and the box share a line, because a date is a small thing to give a whole row to. */
#score-date-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 10px;
}

#score-date {
  width: auto;
}

/* The same grey the quiet hints use, so a shut date box and its label read as one thing. */
#score-date-label.muted {
  color: #6c757d;
}

#pretty-date {
  color: #1897FF;
}

#date-selector {
  padding-bottom: 15px;
  max-height: 90vh;
  overflow-y: scroll;
}

#select-date-button {
  background: linear-gradient(90deg, var(--blue) 70%, var(--green) 100%);
}

/* Was #expand-button, when there was one of it. The colour comes with it because the button has not
   really changed job, it has only stopped speaking for eleven games it was not looking at. */
.expand-button {
  background-color: var(--magenta);
}

.put-game-blobs-here {
  height: auto;
  max-height: 0;
  overflow: hidden;
  -moz-transition: max-height 0.5s ease-out;
  -o-transition: max-height 0.5s ease-out;
  -webkit-transition: max-height 0.5s ease-out;
  transition: max-height 0.5s ease-out;
}

#username {
  text-transform: lowercase;
}

/* Hide scrollbar for Chrome, Safari and Opera */
body::-webkit-scrollbar {
  display: none;
}

/* Above the overlay, including the raised one an account request puts up. It shared a z-index with
   it before, so the thing telling you to wait was being greyed out by the thing making you. */
.loader {
  position: fixed;
  left: 50%;
  top: 50%;
  z-index: 2001;
  width: 200px;
  height: 200px;
  margin: -100px;
  border: 25px double #f3f3f3;
  border-radius: 50%;
  border-top: 25px solid;
  border-top-color: #3498db;
  -webkit-animation: spin 2s ease-in-out infinite;
  animation: spin 2s ease-in-out infinite;
  /* transitions */
  -moz-transition: opacity 0.5s ease-out, border-top-color 1.5s linear;
  -o-transition: opacity 0.5s ease-out, border-top-color 1.5s linear;
  -webkit-transition: opacity 0.5s ease-out, border-top-color 1.5s linear;
  transition: opacity 0.5s ease-out, border-top-color 1.5s linear;
}

.overlay {
  display: none;
  opacity: 0;
  background-color: #000;
  position: fixed;
  width: 100%;
  height: 100%;
  top: 0px;
  left: 0px;
  /* Above everything on the page and below everything that frames it. It was 1, which was above the
     page back when nothing on the page had a z-index of its own, and then the rank tabs were given a
     2 so they would sit on the artwork rather than under it -- and sat over the tint as well, bright
     and still clickable while the rest of the site was greyed out and waiting. 9 rather than 3 so the
     next thing that needs to lift itself off a picture does not have to come back here. The top bar
     is 10 and the sidebar is 98, and both are meant to stay above it: this is a tint, and the menu
     goes on working underneath it. */
  z-index: 9;
  -moz-transition: opacity 0.5s ease-out;
  -o-transition: opacity 0.5s ease-out;
  -webkit-transition: opacity 0.5s ease-out;
  transition: opacity 0.5s ease-out;
}

@-webkit-keyframes spin {
  0% {
    -webkit-transform: rotate(0deg);
  }

  100% {
    -webkit-transform: rotate(360deg);
  }
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
}

.card-title {
  text-transform: capitalize;
  font-weight: bold;
}

/* The name is what you click to go and play on the scoreboard, so it keeps the heading's colour
   rather than taking the default link blue: it still has to read as the title of the row, and the
   pointer and the underline on hover are enough to say it goes somewhere. */
.card-title a {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}

.card-title a:hover {
  text-decoration: underline;
}

/* Every way into a game is one of these: the box picture on a tile, the banner on a row, the name on
   the scoreboard, the small box on the stats page. They all leave the site, and until now they all
   took a tap in complete silence -- you pressed a picture, nothing about it changed, and then some
   time later a different website turned up. Long enough on a slow connection to press it again.

   So it dims and shrinks under the finger, and then stays that way. The press half of that is what
   says the tap landed; the staying is the half that matters, because the wait does not start until
   the finger comes off, and :active ends at exactly that moment. It is cleared on pageshow, which
   covers coming back: a fresh load has none of these set anyway, and a restore from the back button
   would otherwise find the picture still dimmed from a trip taken minutes ago.

   Opacity rather than a filter because half of these are text and half are pictures, and opacity is
   the one that works on both -- filter and transform do nothing at all to a plain inline element.
   The shrink is asked of the picture inside rather than the link around it for the same reason, an
   img being a replaced element and transformable where its anchor is not. The text links just dim,
   which is enough for a word. */
.link {
  transition: opacity 120ms ease;
}

.link img {
  transition: transform 120ms ease;
}

.link:active,
.link.link-opening {
  opacity: 0.55;
}

.link:active img,
.link.link-opening img {
  transform: scale(0.96);
}

/* What stands in for the Play and Submit buttons on a game already in for the day being shown.
   Exactly as tall as those two were together, which is 38 each and the 4 between them, so a game
   crossing the divider does not change the height of its row. The box pictures are all square.
   The picture is a block so the line it would otherwise sit on does not add a gap under it. */
.score-box-link {
  display: inline-block;
}

/* The tile pictures on the home page, and the reason the page used to grow under you on a slow
   connection. Each one is square and fills the width of its column, so how tall it will be is
   settled by the layout and could always have been known before it arrived -- but an img with no
   size is nothing until it loads, so eighteen of them landing one at a time shoved everything
   underneath down eighteen times. Now the space is there from the start and the name sits in it
   until the picture turns up. */
.card-img-top {
  aspect-ratio: 1 / 1;
}

/* Width came from the picture itself, which meant it was nothing at all until the picture landed and
   whatever sat beside it slid sideways when it did. Every box picture is square -- all eighteen of
   them, whatever else differs -- so the width is 80px from the start and the row is laid out once. */
.score-box-img {
  display: block;
  height: 80px;
  aspect-ratio: 1 / 1;
}

.card-title svg {
  margin-left: 5px;
}

/* The list view. The picture takes whatever width the button does not, and the height cap is what
   really sets its size: a 900x180 banner in a row this wide is held to its height and comes out
   about 410px across, which is the row from edge to edge on a phone. The other row is not a game and
   has no banner, so its square icon is grown to the same height to keep every row the same. */
.game-row {
  margin-bottom: 10px;
}

/* Positioned so today's crown can sit on the banner rather than take a place in the row. Nothing
   else moves: it has no offsets of its own.

   Held to 520px and centred, which is as wide as this row ever needs to be: a banner at its largest,
   the gap, and the Submit button. Past that the pictures would go on growing and the page would turn
   into a stack of billboards, so the column is allowed to get wider than the row and the leftover is
   split evenly either side. */
.game-row-body {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}

.game-row-link {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Every banner is 900x180, so the row knows how tall it will be before a byte of it has arrived and
   nothing below it has to move once it does.

   The picture takes whatever the row has left and the ratio decides the rest, so logged out, with no
   button beside it, it takes the lot: wider pictures and a taller row rather than a narrow picture
   sitting in the middle of a lot of nothing. The two caps are the same size said two ways -- 520 wide
   is 104 tall at five to one -- and they exist so the box is never anything but the picture. A box
   wider than the ratio allows is a box with the artwork centred in it by object-fit and white down
   either side, which nobody noticed until the rank tab was hung off the bottom left corner and
   landed in the white instead of on the picture. */
.game-row-img {
  width: 100%;
  max-width: 520px;
  aspect-ratio: 5 / 1;
  max-height: 104px;
  object-fit: contain;
}

.game-row-icon {
  height: 82px;
  width: 82px;
}

.game-row-name {
  font-size: 1.5em;
}

/* The button is stretched rather than given a height of its own, so it stays as tall as the banner
   beside it however tall the banner is allowed to get. Bootstrap lays a button out with padding, so
   it has to be told to centre its own text once it is taller than that padding wants it to be. */
.game-row-submit {
  flex: 0 0 auto;
  min-width: 100px;
  align-self: stretch;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Only one of the two views is on the page at a time, and which one is decided by a class on the
   container rather than by rebuilding, so switching costs nothing and neither view loses its state. */
.game-div .game-row {
  display: none;
}

.game-div.list-view .game-col {
  display: none;
}

.game-div.list-view .game-row {
  display: block;
}

/* Games slide between the two halves rather than jumping, so a score arriving from another device
   reads as the page rearranging itself instead of redrawing. .reordering is the one frame in which a
   game is put back where it came from, which has to happen with no transition or it would be seen
   travelling backwards to its old place first. */
.game-div .game-col,
.game-div .game-row,
.game-div .game-divider {
  transition: transform 0.4s ease;
}

.game-div .reordering {
  transition: none;
}

/* The profile list. Built like the list view rows and travels like them, because it is the same set
   of games in the same order and the star is the one thing on the site that changes that order --
   watching a game move is the only proof the press did anything. */
.game-count {
  margin-bottom: 10px;
}

.game-count-body {
  display: flex;
  align-items: center;
  gap: 10px;
}

.game-count-img {
  height: 48px;
  width: 48px;
}

.game-count-name {
  flex: 1;
  min-width: 0;
  font-size: 1.2em;
}

/* Grey until it is pressed, which is the star saying it is a thing you could do rather than a thing
   that is true. Bootstrap is not asked for a button here: the whole control is the star itself, and
   anything drawn around it would be a box around a single character. */
.game-count-star {
  flex: 0 0 auto;
  background: none;
  border: none;
  padding: 0 4px;
  font-size: 1.6em;
  line-height: 1;
  color: #dee2e6;
}

.game-count-star.favorite {
  color: #ffc107;
}

/* Read in a column down the right hand edge, so the count is what the eye follows rather than
   something to be found again on every line. */
.game-count-plays {
  flex: 0 0 auto;
  min-width: 2.5em;
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: #6c757d;
}

.game-counter .game-count {
  transition: transform 0.4s ease;
}

.game-counter .reordering {
  transition: none;
}

#profile-display-name {
  font-size: 1.2em;
  padding-right: 6px;
}

#profile-games-hint {
  color: #6c757d;
  font-size: 0.85rem;
}

.card-body {
  padding: 0px;
  padding-bottom: 15px;
}

.scoreboard {
  /* background-color: blue; */
  -ms-overflow-style: none;
  scrollbar-width: none;
}

/* Hide scrollbar for Chrome, Safari and Opera */
.scoreboard::-webkit-scrollbar {
  display: none;
}

.pre-score-blob {
  background-color: whitesmoke;
}

/* Standing in for the picture every other game has. It holds the same square the images do, or the
   tile it sits in comes out a different height from the ones beside it. */
.other-img {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: whitesmoke;
  border-radius: 10px;
}

.other-img svg {
  width: 40%;
  height: 40%;
  color: #6c757d;
}

.other-name {
  color: #6c757d;
  margin-bottom: 0;
  padding-top: 8px;
}

/* One block of scores per game, so they sit under their own heading rather than running into the
   next game's. */
#other-scores pre {
  margin-bottom: 0;
}

#score-pts {
  padding-left: 5px;
}

#score-preview {
  padding: 5px;
  background-color: whitesmoke;
  border-radius: 10px;
}

/* GeoGrid's board is three by three and so is everything drawn about it, in the submit modal and on
   the scoreboard both. Laid out as a grid rather than a table because the boxes have to stay square
   as the column narrows, and a square that is defined by its own aspect ratio does that on its own
   without a single number in here having to know how wide the page is. */
.geogrid-grids {
  display: flex;
  gap: 10px;
}

/* One day the import could not settle by itself. Boxed and spaced because the page holds a list of
   them and two sets of grids running together read as one set of four. */
.import-conflict + .import-conflict {
  border-top: 1px solid rgba(128, 128, 128, 0.3);
}

.import-conflict-day {
  font-weight: 600;
}

/* The number the whole disagreement is about, so it is the thing between the grid and the button
   rather than something to go looking for. */
.import-conflict-score {
  font-size: 1.1rem;
  padding: 3px 0 5px 0;
}

.geogrid-pane {
  flex: 1 1 0;
  /* Without this a flex child refuses to go narrower than the things inside it, and on a phone that
     is the right hand grid pushing the left one off the side of the modal. */
  min-width: 0;
  text-align: center;
}

.geogrid-grid {
  display: grid;
  /* Sized to the emoji rather than stretched across whatever room the modal has going spare. A grid
     given a share of the width puts a twenty pixel flag in the middle of a seventy pixel square and
     the nine of them read as nine mostly-empty boxes, which is a picture of the padding rather than
     of the round. Nine flags is a small thing and it is allowed to look like one. */
  grid-template-columns: repeat(3, 34px);
  justify-content: center;
  gap: 2px;
  padding: 5px;
  width: fit-content;
  margin: 0 auto;
  background-color: whitesmoke;
  border-radius: 10px;
}

/* Wrapping, because on the scoreboard a box holds one flag for every person who played and four
   people is four flags in a square meant for one. They shrink rather than overflow. */
.geogrid-box {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1px;
  font-size: 1.3rem;
  line-height: 1;
  overflow: hidden;
}

/* A box nobody filled is left empty rather than given a mark of its own, because the flag grid
   already carries the game's own cross in it and two marks for one miss reads as two misses. */

/* The rebuilt board in its modal, which is a drawing of somebody else's game and is styled to look
   like one rather than like this site. The colours are GeoGrid's own and are written out here as
   numbers, deliberately: they do not follow the light theme or the dark one, because what this is
   trying to say is "here is the board you played this morning", and a board that changes colour with
   the page is a board you have to look at twice to recognise.

   Four columns: the captions down the side get a little more room than a square, because the
   categories are sentences of wildly different lengths -- "Drives on the left" against "Has
   coastline, with length greater than 100 km" -- and any fixed width picked here would be wrong for
   one of them. */
.geogrid-board {
  display: grid;
  grid-template-columns: 1.3fr repeat(3, 1fr);
  gap: 8px;
  padding: 8px;
  background-color: #2B2E3C;
  border-radius: 14px;
}

.geogrid-cell {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 1px;
  overflow: hidden;
}

/* The categories, on cards of their own the way the game has them. Bold and light on a raised panel,
   because in the game these read as the labels of the thing rather than as part of the grid. */
.geogrid-board .geogrid-caption {
  background-color: #4B4F66;
  color: #F1F2F7;
  border-radius: 12px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.2;
  padding: 4px;
  text-align: center;
  hyphens: auto;
}

/* The corner, which in the game is where the board number lives and is the one part of the shape
   that is not a category or a square. Kept, because a board number is how anybody checks that this
   is the board they think it is. */
.geogrid-board .geogrid-corner {
  flex-direction: column;
  background-color: #4B4F66;
  color: #F1F2F7;
  border-radius: 12px;
  padding: 4px;
}

.geogrid-corner-label {
  font-size: 0.7rem;
}

.geogrid-corner-number {
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.2;
}

/* The nine squares. Dark, square and sunken, which is the game's own arrangement: the answers sit in
   holes in the board rather than on cards like the categories do.

   No padding on this, because what goes in it is meant to reach the walls: the answers cut the
   square up between them and each one is coloured, so any padding here would draw a dark frame round
   the colours and turn a divided square back into a set of tiles floating in a hole. The rounding is
   clipped instead, which keeps the game's soft corners without keeping the gap.

   The container line is what lets the flags inside size themselves against the square rather than
   against the page, so one board works on a phone and on a monitor without a media query. */
.geogrid-board .geogrid-box {
  aspect-ratio: 1;
  background-color: #22242F;
  border-radius: 12px;
  container-type: size;
  overflow: hidden;
}

/* Everybody's answers to one square, dividing it up between them: two people take half each, four
   take a quadrant each, nine take a ninth. All three counts are written on by whoever built this,
   because they depend on how many people played. The first two are needed because a wrapping layout
   could work out the rows for itself but could not stretch a part filled last row across the gap,
   which is what keeps the square full when the count does not divide evenly; the third is how the
   picked out layout below knows how many are sharing the bottom strip. */
.geogrid-guesses {
  display: grid;
  grid-template-columns: repeat(var(--geogrid-columns, 1), 1fr);
  grid-template-rows: repeat(var(--geogrid-rows, 1), 1fr);
  width: 100%;
  height: 100%;
}

/* One answer to one square, and the piece of it that answer gets. The colours of everybody who gave
   that answer are painted on by whoever built this, because how many of them there are is not
   something a stylesheet can know. The colour is worn here rather than on the flag inside, so that a
   square with four answers in it reads as four answers rather than as four flags with something
   behind them. */
.geogrid-guess {
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  overflow: hidden;
}

/* Sized against the square it is in and against how the square has been cut up, so a piece is always
   as full as the flag on it can make it. Both terms are needed and the smaller one wins: the width
   term is what stops three columns of flags running into each other and the height term is what
   stops four rows of them running out through the floor, and which of the two is the tight one
   depends on the number of people. The plain size on the first line is what a browser that has never
   heard of container units gets: small, even and legible, which is the right thing to fall back to.

   The line height is flat one because a flag emoji already draws taller than the size it is set at,
   and any leading on top of that is what pushed rows of flags out through the bottom of the square
   the first time this was tried. */
.geogrid-answer {
  font-size: 1.2rem;
  font-size: min(calc(72cqw / var(--geogrid-columns, 1)), calc(66cqh / var(--geogrid-rows, 1)));
  line-height: 1;
}

/* One person picked out of the board. Every square is given over to them: their answer takes the top
   of it at something like the size it would be if they had played alone, and everybody else is put
   in one strip along the bottom, still there and still in their own colours but small.

   This started as a fade, with everybody else dropped to almost nothing. Size does the job better.
   A faded flag is a flag you have been told not to look at, which is no use when the reason you
   picked somebody is to see what they put where everybody else put something different -- and the
   comparison is the entire point of the board. Small and legible keeps the answer and the argument.

   Laid out by wrapping rather than as a grid, because which seat is picked is not known until it is
   pressed, and order pulls whoever it is up to the top line from wherever they are in the square
   without the board having to be built again. The two heights add up to the whole square, so it
   stays as full picked out as it is at rest. */
.geogrid-picking .geogrid-guesses {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
}

/* A share each of the bottom strip. The basis has to be their real share rather than nought, and
   that is the whole trick of this layout: wrapping is decided on what an item asks for, not on what
   it can be squeezed to, so a strip of items asking for nothing sat up on the same line as the big
   one and was squeezed to nothing by it. Asking for a share each means they do not fit beside it,
   so they go to a line of their own -- and the share they asked for is the share they then get. */
.geogrid-picking .geogrid-guess {
  flex: 0 0 calc(100% / var(--geogrid-others, 1));
  height: 28%;
}

.geogrid-picking .geogrid-guess.picked {
  flex: 0 0 100%;
  height: 72%;
  order: -1;
}

/* A square everybody agreed on is one piece, and the person picked out is already in it. There is
   nothing to put in the strip along the bottom, so there is no strip: the piece keeps the whole
   square. Without this, agreeing with everybody is drawn as your answer shrinking to make room for a
   band of nothing, which is the opposite of what happened. */
.geogrid-picking .geogrid-guess.picked:only-child {
  height: 100%;
}

/* Sized the same way as at rest -- the smaller of what the width allows and what the height allows
   -- but against the two strips rather than against the grid, because the strips are a different
   shape to the pieces they replace. The one on top has the whole width to itself and rather less
   than the whole height, so the height is usually the tight one; the ones along the bottom have a
   sliver of height each and share the width, so which one binds depends on how many there are. */
.geogrid-picking .geogrid-guess.picked .geogrid-answer {
  font-size: min(70cqw, 48cqh);
}

.geogrid-picking .geogrid-guess:not(.picked) .geogrid-answer {
  font-size: min(calc(70cqw / var(--geogrid-others, 1)), 18cqh);
}

/* The names under the board, each in the colour it is wearing in the squares. Worn a shade more
   solidly than the pieces of the squares, so a name reads as the label for a colour rather than as
   one more answer that happens to have fallen out of the grid. */
#grid-modal-key {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.geogrid-name {
  background-color: var(--player-color);
  border: 2px solid transparent;
  border-radius: 999px;
  padding: 2px 12px;
  font-size: 0.9rem;
}

/* The one being followed. A ring rather than a brighter fill, because the fill is the colour and the
   colour is the thing that has to keep matching the squares. */
.geogrid-name.picked {
  border-color: currentColor;
  font-weight: 600;
}

/* The colour toggle, sitting under the names it is partly about. The words go beside the buttons
   rather than above them, because "None / Rarity / User" directly under a row of names reads as
   three more names, and directly under a Flags / Rarity toggle reads as the same toggle twice. */
.grid-color {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

.grid-color-label {
  font-weight: 600;
  opacity: 0.7;
}

/* What the six symbols mean. Laid out as two across and three down, filling the width of the modal,
   rather than as the six line list the game itself uses: six full lines under the board is most of a
   phone screen spent on a legend and pushes the board it explains off the top, and two columns
   halves that at no cost, because a band is three short words and never needed a line of its own.

   Fixed at two columns at every width rather than fitted to the room going spare. Three across is
   too narrow to hold "Legendary" and its number on one line on a phone, and one across is the six
   line list this is replacing. Two is the answer at both ends, so it is written as two. */
#grid-modal-rarity-key {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  font-size: 0.8rem;
}

/* One band, wearing its colour and filling its share of the row. The words go light and stay light,
   because the bands run from a mid green to a deep purple and the theme's own text colour is legible
   on at most half of them -- and which half depends on which theme is up, so there is no one answer
   that follows the page. The colours are the board's rather than the page's, so the writing on them
   is the board's too.

   The three parts stay on one line together, because a band that wraps between its name and its
   number reads as two bands. */
.geogrid-tier {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 4px;
  white-space: nowrap;
  border-radius: 999px;
  padding: 2px 8px;
  color: #F1F2F7;
}

/* Set a shade larger than the words beside it, because these are the things being looked up and the
   words are the answer. The line height is flat one for the same reason it is on the board: an emoji
   draws taller than the size it is set at and would otherwise push the row apart. */
.geogrid-tier-emoji {
  font-size: 1rem;
  line-height: 1;
}

/* The number is the definition and the name is the nickname, so the number is written quieter: you
   read down the names to find your symbol and only look at the number if you are arguing. */
.geogrid-tier-share {
  opacity: 0.6;
}

#submittedGame {
  text-transform: capitalize;
  font-weight: bolder;
}

#refreshPreview {
  padding-left: 5px;
}

.menu-bar {
  height: 25px;
}

.container {
  background-color: white;
  border-top-right-radius: 15px;
  border-top-left-radius: 1px;
}

footer {
  bottom: 0;
  position: absolute;
  width: 100%;
  z-index: 1500;
}

.bg-purple {
  background-color: #9932CC;
}

.bg-gray {
  background: rgb(74, 74, 74);
}

.bg-blue {
  background: rgb(50, 101, 196);
}

.bg-red {
  background: rgb(196, 50, 53);
}

.app-title {
  margin-left: 25px;
  font-weight: bold;
  color: #ffffff;
  font-size: 20px;
  white-space: nowrap;
}

/* The five icons in the footer need 250px and this was 250px wide, so they had grown to fit it
   exactly and the row's own padding was enough to wrap the last two onto a second line, which the
   fixed height then cut in half. It is a full width row in a centred column, so it can measure
   itself and the icons stay on one line however many there are. */
.social {
  transition-property: all;
  transition-timing-function: ease;
  transition-duration: 400ms;
}

.navbar {
  justify-content: start;
}

.gradient {
  display: none;
}

.gradient-start {
  background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.3));
}

.gradient-end {
  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0));
}

.fade-me {
  background-color: rgba(0, 0, 0, 0.3);
}

.cell {
  padding: 15px;
  background-color: yellow;
  width: 50px;
  margin: 1px;
  border-radius: 5px;
  text-align: center;
}

.password-input {
  position: relative;
  margin: auto;
  background-color: lightblue;
  padding: 25px;
  top: 50%;
  max-width: 300px;
  border-radius: 15px;
}

.file-list li span {
  padding-right: 10px;
}

li.hidden,
span.hidden {
  display: none;
}

.file-list li .feather {
  width: 20px;
  height: 24px;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}

.clickable {
  cursor: pointer;
}

.json-file {
  color: lightseagreen;
  text-decoration: underline;
}

.light-path {
  color: lightgray
}

.dir {
  color: black
}

.game {
  margin-top: 50px;
}

.scoreboard-container {
  margin-top: 10px;
}



/* Helper CSS */
.l-box {
  border: none;
  width: 100%;
  background: transparent;
  text-align: center;
  font-size: x-large;
}

td:has(> input.l-box) {
  width: 20%;
  height: 50px;
  max-width: 50px;
  max-height: 50px;
}

#graveyard {
  overflow-x: scroll;
  -ms-overflow-style: none;
  scrollbar-width: none;
}

#graveyard::-webkit-scrollbar {
  display: none;
}

div:has(>p.bank) {
  max-height: 80vh;
  overflow-y: scroll;
  font-size: larger;
}



/* The Submit button while a score is in the air, and after. The colours are written against .btn as
   well as the state class so they land on top of Bootstrap's btn-info, which is one class on its own
   and would otherwise win on nothing more than being further down the sheet. */
.btn.submit-working {
  pointer-events: none;
  opacity: 0.75;
}

.btn.submit-done {
  background-color: #198754;
  border-color: #198754;
  color: #fff;
}

/* Worn only while a button is walking from Submit to Update on its own, so the colour glides from
   blue through green to grey instead of stepping. Not written against the buttons themselves,
   because every one of them goes from blue to grey the moment the day lands and a page that loads
   with eighteen buttons all changing colour for a second is a page that looks like it is thinking
   about something.

   What is never touched is the opacity of the button itself: a button at a third of itself is a hole
   in the row and reads as the page breaking rather than as news arriving. Only the writing on it
   fades, and that is done a class further down. */
.btn.submit-settling {
  transition: background-color 0.5s ease, border-color 0.5s ease;
}

.btn.submit-crowned {
  background-color: #d4a017;
  border-color: #d4a017;
  color: #fff;
}

/* The picture on a tile, with the rank tab sat on the bottom edge of it. It exists only to be
   something for the tab to be positioned against: the link inside is made a block so the box is
   exactly the
   picture and not the picture plus the gap a line of text leaves under an inline image. */
.game-art {
  position: relative;
}

.game-art .link {
  display: block;
}

/* The folder tab on the corner of a game already played today. Two things at once: where you came,
   without anything having to be opened, and the handle for the drawer that says who you came there
   among. It carries a crown for a win, a place for anything else, and a minus while the drawer it
   opened is showing.

   Rounded along the top and square along the bottom so it reads as a tab on the front of something
   rather than a badge floating on the artwork. No shadow, for the same reason: a shadow would put
   it above the picture, and the idea is that it has been cut out of one.

   Fixed width so that 2ND and a crown are the same size and a row of games does not have tabs of
   nine different widths along it.

   A white border on three sides, and none along the bottom, where the tab meets the edge of the
   picture and there is nothing on the other side of it to be separated from. On a plain tab the
   border is the same white as the tab and does nothing but make it a pixel bigger; it is there for
   the gold one, where it is the white surround that sells the tab as a piece cut out of the
   artwork rather than a gold rectangle laid on top of it. */
.game-rank-tag {
  position: absolute;
  left: 8px;
  bottom: 0;
  z-index: 2;
  box-sizing: border-box;
  width: 44px;
  padding: 1px 0 2px 0;
  border: 1px solid #fff;
  border-bottom: none;
  border-radius: 6px 6px 0 0;
  background-color: #fff;
  color: #212529;
  font-size: 11px;
  font-weight: bold;
  line-height: 16px;
  text-align: center;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

/* The countdown, which is not a tab on the artwork any more. It says how long is left to file a
   score, and the thing you would do about that is press Submit, so it lives on Submit: a spine down
   the right-hand edge of the button, and pressing it is pressing the button. A deadline sitting in
   the corner of a picture is something you read and then have to go and find the button for.

   On its side, because the button is already a hundred pixels of writing and there is no room
   across it -- but there is the whole height of it going down. Written as a writing mode rather
   than a rotation, because a rotated box still takes up the room of an unrotated one and would have
   needed a height in pixels, and the tile's button and the row's button are nothing like the same
   height. In a vertical writing mode centring the text is still text-align, it just centres it down
   the button instead of across it.

   Black at a fifth rather than a colour of its own, so it reads as a shaded edge of whatever the
   button happens to be wearing. The button is blue, then grey, then green or gold for a moment as a
   score lands, and a spine with its own colour would have to know about all four. */
.game-reset-tag {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  box-sizing: border-box;
  padding: 2px;
  border-radius: 0 0.375rem 0.375rem 0;
  background-color: rgba(0, 0, 0, 0.2);
  color: #fff;
  writing-mode: vertical-rl;
  font-size: 9px;
  font-weight: bold;
  line-height: 12px;
  text-align: center;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}

/* What the spine is positioned against. The button group cannot be it, because a positioned button
   group is still a button group and the span would have had to go inside it.

   Laid out as a flex line and not simply left as a block, because a button group is inline-flex and
   an inline thing in a block sits on a text baseline with room left under it for the tails of
   letters. Four pixels of nothing, which nobody would ever notice -- until something is pinned to
   the bottom of the box and comes out four pixels below the button it is supposed to be part of. */
.game-buttons {
  position: relative;
  display: flex;
}

/* The last hour, in the same red the rank tab wears when a place has just been lost, because it is
   the same kind of news: something is being taken away and there is a short window left to do
   anything about it. Solid here rather than shaded, because the whole point of the last hour is
   that the edge of the button stops agreeing with the button. */
.game-reset-tag.reset-tag-soon {
  background-color: #dc3545;
  color: #fff;
}

/* The other thing a countdown was being asked to say, and it turned out not to be a countdown at
   all. Four of these games keep their clock somewhere else, so for part of every day the puzzle on
   the screen is not the day the calendar is showing: Redactle turns over at four in the afternoon
   UTC, so all morning in California the live puzzle is yesterday's, and Wordle turns over at nine
   in the evening there, so all evening the live puzzle is tomorrow's.

   Which day you are about to file is a fact about the game rather than a deadline, so it goes where
   the facts go -- the bottom corner of the picture -- and not on the button. It is also several
   words long, which is a sentence on a picture and would have been a smudge on a spine.

   Nothing to press. Clicks go straight through to the picture underneath, which is the link off to
   the game, because that is what the rest of the picture does and a small patch of it that quietly
   did nothing would only read as broken. */
.game-date-tag {
  position: absolute;
  right: 8px;
  bottom: 8px;
  z-index: 2;
  padding: 1px 6px 2px 6px;
  border: 1px solid #fff;
  border-radius: 6px;
  background-color: #fff;
  color: #6c757d;
  font-size: 11px;
  font-weight: bold;
  line-height: 16px;
  text-align: center;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* Absurdle before anybody has filed their Wordle. Greyed and desaturated so it plainly is not
   offering what the others are offering, but still perfectly clickable -- bootstrap's own disabled
   class would have set pointer-events to none and swallowed the press, and the press is the only
   way the person finds out why it is grey.

   Two rules rather than one because the three ways in are different things wearing the same class:
   a button, a box picture and a banner. Fading suits the pictures; a button wants its colour taken
   off it as well, or it is a bright blue button that merely looks slightly ill. */
.game-link-locked {
  opacity: 0.45;
  filter: grayscale(1);
  cursor: not-allowed;
}

.btn.game-link-locked {
  border-color: #6c757d;
  background-color: #6c757d;
}

/* The Wordle answer, spelled out a letter at a time. Deliberately not green: green in this family of
   games means a letter you have guessed and got in the right place, and this word has not been
   guessed at all -- it is the one you are about to type in. Grey says "a letter", which is all that
   is being said. */
.answer-tiles {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin: 4px 0 0 0;
}

.answer-tile {
  width: 42px;
  height: 42px;
  border: 2px solid #6c757d;
  border-radius: 4px;
  background-color: #6c757d;
  color: #fff;
  font-size: 24px;
  font-weight: bold;
  line-height: 40px;
  text-align: center;
  text-transform: uppercase;
}

/* Five letters, spaced out, so that the box you type the answer into looks like the thing it is
   asking for rather than like a search field. */
.word-ask-input {
  text-transform: uppercase;
  letter-spacing: 6px;
  font-size: 20px;
  font-weight: bold;
  text-align: center;
}

/* Gold for a win, the same gold the Submit button goes when the score that earned it lands, so the
   colour means one thing in both places. It stays gold while the drawer is open and the tab is
   showing a minus, because the game is still won and having it flick back to white for as long as
   you are reading the standings would be a strange thing to do.

   This one is bordered on all four sides, because the bottom of it is gold against the edge of a
   photograph and wants the same white line as the rest of the way round. */
.game-rank-tag.rank-crowned {
  border-bottom: 1px solid #fff;
  background-color: #d4a017;
  color: #fff;
}

/* A tab saying it has just been overtaken, for the two seconds it takes to say it. Red, because it is
   the one piece of bad news this page has to deliver, and bordered on all four sides like the gold
   one for the same reason: the bottom edge is red against a photograph and wants the white line the
   rest of the way round. */
.game-rank-tag.rank-tag-dropped {
  border-bottom: 1px solid #fff;
  background-color: #dc3545;
  color: #fff;
}

/* The transition is worn alongside the colours rather than by the tab itself, so the walk out of
   white or gold and back again glides, while a tab merely being redrawn -- which happens every time
   the day is read -- changes colour the instant it is told to. */
.game-rank-tag.rank-tag-settling {
  transition: background-color 0.5s ease, border-color 0.5s ease;
}

/* The writing on a button or a tab that is announcing something, and the only part of either that
   ever fades. Both of them are solid shapes sitting on something -- a button in a row, a tab on a
   photograph -- and a solid shape at a third of itself reads as the page breaking rather than as
   news arriving. The wording is the one part that has to change without being seen to swap, so it
   goes out, the colour underneath glides while it is away, and it comes back saying the new thing.

   It has to be a span rather than the element's own colour going transparent, because half the faces
   these two wear are the crown, and an emoji brings its own colours and pays no attention to what
   colour it has been told to be.

   Quicker than the colour behind it on purpose, so the word has gone before the colour it was
   written on has finished changing and there is never a moment of dark text on a red tab. */
.announce-label {
  display: inline-block;
  transition: opacity 0.35s ease;
}

.submit-blank .announce-label,
.rank-tag-blank .announce-label {
  opacity: 0;
}

/* A tab arriving on a picture it was not on a moment ago. It rises out of the bottom edge, which is
   the edge it is already attached to, so it reads as being pushed out from behind the photograph
   rather than dropped onto it.

   Slower and further than it was. Half a second and eight pixels is a real animation and it measured
   as one, but the tab is 44px wide and 20px tall on a phone at arm's length, and a small thing moving
   a small distance quickly is indistinguishable from a small thing appearing. It has to travel far
   enough and long enough to be seen travelling, or the whole point of it is lost and it reads as a
   flash -- which is exactly what it was reported as. */
.game-rank-tag.rank-tag-arriving {
  -webkit-animation: rank-tag-in 0.85s ease-out;
  animation: rank-tag-in 0.85s ease-out;
}

@-webkit-keyframes rank-tag-in {
  0% {
    opacity: 0;
    -webkit-transform: translateY(16px);
  }

  100% {
    opacity: 1;
    -webkit-transform: translateY(0);
  }
}

@keyframes rank-tag-in {
  0% {
    opacity: 0;
    transform: translateY(16px);
  }

  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* The drawer that opens under a game once its score is in. Deliberately quiet: it is an aside about
   a game you have finished with, sitting directly under the button you just pressed, and it should
   not be competing with the games you have not played yet. */
.game-drawer {
  font-size: 0.8em;
  padding: 6px 8px 2px 8px;
  overflow: hidden;
  /* Held to the same width as the row above it and centred the same way, so the line it draws across
     the top lines up with the picture it is an aside about rather than running past both ends of it.
     520 is the widest that row is ever allowed to be: the banner at 410, the gap, and the button. */
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}

.drawer-inner {
  display: flex;
  align-items: center;
  gap: 6px;
  border-top: 1px solid #dee2e6;
  padding-top: 6px;
}

/* The standings scroll sideways rather than wrapping, because the drawer sits inside a tile in a
   grid and a second line in one of them pushes every tile in that row down. */
.drawer-order {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow-x: auto;
  color: #6c757d;
}

.drawer-arrow {
  margin: 0 4px;
  color: #adb5bd;
}

.drawer-you {
  font-weight: 600;
  color: #212529;
}

.drawer-crown {
  margin-right: 4px;
}

.drawer-link {
  flex: 0 0 auto;
  color: #0d6efd;
  text-decoration: none;
  white-space: nowrap;
}

.drawer-link:hover {
  text-decoration: underline;
}

/* The stats page. Every chart on it is drawn into a fixed box rather than left to work its own
   height out, because Chart.js grows a canvas to fit its parent and a parent with no height of its
   own grows to fit the canvas, which between them is a chart that gets taller every time it draws. */
.stats-chart-box {
  position: relative;
  height: 45vh;
  min-height: 260px;
  margin-bottom: 1rem;
}

.stats-hint {
  color: #6c757d;
  font-size: 0.9em;
  margin-bottom: 0.5rem;
}

.stats-empty {
  color: #6c757d;
  font-style: italic;
}

.stats-range {
  width: 100%;
}

.crown-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 3px 0;
}

.crown-name {
  width: 6.5em;
  flex: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.crown-bar-box {
  flex: 1;
  min-width: 0;
  background: #e9ecef;
  border-radius: 4px;
}

/* A bar has to be visible even when it is nearly nothing, or somebody on half a crown next to
   somebody on forty looks like somebody on none at all. */
.crown-bar {
  display: block;
  min-width: 3px;
  height: 18px;
  border-radius: 4px;
}

.crown-count {
  width: 4.5em;
  flex: none;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

/* The info symbol by the Crowns heading. Feather draws at 24px by default, which next to a heading
   is not a little symbol, it is a second heading. */
.stats-info {
  cursor: pointer;
  color: #6c757d;
  vertical-align: middle;
}

.stats-info svg {
  width: 18px;
  height: 18px;
}

/* The day board at the top. The same bars as the board below, so the two read the same way, with a
   gap under them because the range buttons that follow do not govern this one. */
.stats-day-crowns {
  margin-top: 0.5rem;
  margin-bottom: 1rem;
}

/* The range board has no heading of its own: it is the same crowns as the board above, counted over
   whatever the buttons between them say, and calling it Crowns a second time only made the page
   look as though it had two different things on it. So the spacing has to do the separating. */
.stats-crowns {
  margin-top: 0.5rem;
  margin-bottom: 1.5rem;
}

/* The grid is as wide as the number of people playing, which on the all time view is more than a
   phone has room for, so it scrolls sideways inside its own box rather than pushing the page out. */
.head-to-head-scroll {
  overflow-x: auto;
  margin-bottom: 1rem;
}

.head-to-head {
  border-collapse: collapse;
  font-size: 0.85em;
}

.head-to-head th {
  padding: 4px 8px;
  font-weight: 600;
  white-space: nowrap;
}

.head-to-head tbody th {
  text-align: left;
  position: sticky;
  left: 0;
  background: #fff;
}

.head-to-head td {
  border: 1px solid #dee2e6;
  padding: 4px 8px;
  text-align: center;
  min-width: 4.5em;
}

.head-to-head-self {
  background: #f8f9fa;
}

.head-to-head-rate {
  display: block;
  font-weight: 600;
}

.head-to-head-record {
  display: block;
  font-size: 0.85em;
  opacity: 0.8;
}

.calendar-month {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 2px;
}

.calendar-label {
  width: 5.5em;
  flex: none;
  font-size: 0.8em;
  color: #6c757d;
}

.calendar-days {
  display: flex;
  gap: 2px;
}

.calendar-day {
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: #ebedf0;
}

.calendar-level-1 {
  background: #9be9a8;
}

.calendar-level-2 {
  background: #40c463;
}

.calendar-level-3 {
  background: #30a14e;
}

.calendar-level-4 {
  background: #216e39;
}


/* Extra small devices (phones, 600px and down) */
@media only screen and (max-width: 600px) {}

/* Small devices (portrait tablets and large phones, 600px and up) */
@media only screen and (min-width: 600px) {}

/* Medium devices (landscape tablets, 768px and up) */
@media only screen and (min-width: 768px) {}

/* Large devices (laptops/desktops, 992px and up) */
@media only screen and (min-width: 992px) {}

/* Extra large devices (large laptops and desktops, 1200px and up) */
@media only screen and (min-width: 1200px) {}

/* The line that appears under a modal's boxes when something will not do. Worn by both the sign up
   and the rename modals, which were saying the same kind of thing in two different voices: one red,
   one the ordinary body black with a warning sign in front of it standing in for the colour. */
.form-error {
  color: var(--error-red);
}

/* Worn by any box holding something that will not do, and by the login arrow when the box above it
   is. A class rather than a colour set from the script, so the red lives in one place with the rest
   of them and cannot drift. Both selectors carry two classes on purpose: a lone .field-error ties
   with Bootstrap's .form-control and loses to .input-group div, so it would have been quietly
   ignored on the very boxes it was written for. */
.form-control.field-error,
.login-button.field-error {
  color: var(--error-red);
}


/* The settings modal. Three switches and their names, and the space between them is doing the work
   the explanations under them used to: a list of three short things reads at a glance, and the same
   list with a sentence under every line has to be read. */
.setting-row {
  margin-bottom: 14px;
}

.setting-row .form-check-label,
.setting-label {
  font-weight: 600;
}

/* The turnover list. A time on the left and the games that go at it on the right, laid out as a
   line each rather than a table, because a table wants borders and headings to explain itself and
   this needs neither: the left column is a time and the right column is games, and nobody has to be
   told that.

   The time column is fixed and the games column takes what is left and wraps inside it, so the
   thirteen games that all go at midnight come out as a paragraph beside one time rather than
   thirteen times repeated down the page. */
.reset-times {
  margin-top: 4px;
  font-size: 14px;
}

.reset-time-row {
  display: flex;
  gap: 10px;
  margin-bottom: 8px;
}

.reset-time-when {
  flex: 0 0 122px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* What kind of time the time is -- your own, or a named zone. Lighter and smaller than the reading
   it follows, because the reading is the answer and this is only what the answer is measured in,
   and a column where both halves shout leaves the eye landing on the wrong one. */
.reset-time-zone {
  font-weight: normal;
  font-size: 12px;
  opacity: 0.75;
}

/* Capitalised the same way the tiles capitalise, so a game is spelled one way on this site. */
.reset-time-games {
  flex: 1;
  min-width: 0;
  text-transform: capitalize;
  color: #6c757d;
}

/* Why an odd time is odd. Never capitalised, unlike the names it follows, because it is a sentence
   and not a name -- which is also why it has to say so, sitting as it does inside a line that
   capitalises everything by default. */
.reset-time-aside {
  text-transform: none;
  font-size: 12px;
  opacity: 0.8;
}

/* Full width, like the range buttons on the stats page it is borrowed from. A three button group
   left to size itself sits in a huddle against the left of a modal that is wider than it, which
   reads as three buttons that happen to be near each other rather than as one question with three
   answers. */
.theme-choice {
  width: 100%;
  margin-top: 4px;
}

/* ---------------------------------------------------------------------------------------------
   Dark mode.

   Bootstrap 5.3 brings its own dark theme and it is switched on by data-bs-theme on the html
   element, which is what games.js sets. That covers everything bootstrap draws: cards, modals,
   tables, form controls, buttons, dropdowns. What is left is the colours this file writes down by
   hand, and this is the whole of that list. It is deliberately one block at the bottom rather than
   a second colour beside each rule, so that there is one place to look when something comes out
   the wrong shade.

   The palette is Matt's: #0A0E15 for the very back, #212631 for the page itself, #373F4E for the
   things that sit on top of it, #4E576A for lines, and #667085 for anything quiet. The blue is the
   same #1897FF the site has always used, which is why the highlights are blue -- there was never
   another colour to choose.

   Everything here is scoped to [data-bs-theme="dark"] on the html element, so light mode is
   untouched by all of it and nothing above this line had to change. --------------------------- */

[data-bs-theme="dark"] {
  /* The one red for things that have gone wrong is a dark red chosen to be read on white, and on
     #212631 it very nearly is not there. This is bootstrap's own dark-mode red, doing the same job
     at the other end. The pink for the sidebar is already pale and stays as it is. */
  --error-red: #ea868f;
}

/* The gradient behind the page. The light one runs blue into green; this one runs the darkest
   colour in the palette into a deep blue, so the edges of the page still read as blue rather than
   as a black border nobody asked for. */
html[data-bs-theme="dark"],
html[data-bs-theme="dark"] body {
  background-color: #0A0E15;
  background: linear-gradient(180deg, #0A0E15 0%, #123252 100%);
}

/* The page itself, which is the white sheet everything else is printed on. */
[data-bs-theme="dark"] .container {
  background-color: #212631;
}

/* The things that sit on the page: a score before it is read, the box standing in for a picture,
   and the preview under the paste box. One step lighter than the page so they are visible without
   a line drawn round them. */
[data-bs-theme="dark"] .pre-score-blob,
[data-bs-theme="dark"] .other-img,
[data-bs-theme="dark"] .geogrid-grid,
[data-bs-theme="dark"] #score-preview {
  background-color: #373F4E;
}

/* Everything quiet. These are all #6c757d in the light theme, which is a grey picked to be read on
   white and is far too dark to be read on #212631. */
[data-bs-theme="dark"] #modal-submit:disabled,
[data-bs-theme="dark"] #manual-score-hint,
[data-bs-theme="dark"] #other-url-hint,
[data-bs-theme="dark"] #wordle-answer-hint,
[data-bs-theme="dark"] .game-divider h5,
[data-bs-theme="dark"] #score-date-label.muted,
[data-bs-theme="dark"] #profile-games-hint,
[data-bs-theme="dark"] .other-img svg,
[data-bs-theme="dark"] .other-name,
[data-bs-theme="dark"] .stats-hint,
[data-bs-theme="dark"] .stats-empty,
[data-bs-theme="dark"] .stats-info,
[data-bs-theme="dark"] .calendar-label {
  color: #9AA4B5;
}

[data-bs-theme="dark"] #modal-submit:disabled {
  background-color: #4E576A;
  border-color: #4E576A;
}

/* The star on the profile page when it is not filled in. Filled it is amber, which is fine on
   either background, so :not keeps this off it -- a plain .game-count-star rule here would be the
   same weight as .game-count-star.favorite and later in the file, and would quietly take the amber
   off every favourite in the dark theme. */
[data-bs-theme="dark"] .game-count-star:not(.favorite) {
  color: #4E576A;
}

/* The tab on the corner of a finished game. Its whole trick is being the same colour as the card it
   is cut out of, so it follows the page rather than staying white. The gold one stays gold -- gold
   on a photograph reads the same either way -- but its surround follows too, because a white line
   round it in a dark theme would put it back on top of the picture instead of in it. */
[data-bs-theme="dark"] .game-rank-tag {
  border-color: #212631;
  background-color: #212631;
  color: #E6E9EF;
}

/* The gold has to be said again. The rule above it is the same weight and comes later in the file,
   so without this a crown tab in the dark theme comes out the same colour as a 2ND. */
[data-bs-theme="dark"] .game-rank-tag.rank-crowned {
  border-bottom-color: #212631;
  background-color: #d4a017;
  color: #fff;
}

/* And so does the red, for the same reason: a tab announcing that it has just been overtaken was
   coming out the colour of the card in the dark theme, which is to say it was not announcing
   anything. Same red either way -- it is a warning colour and it has to read as one on both themes.
   Its surround follows the card, like the gold one, so the tab still sits in the picture rather than
   on it. */
[data-bs-theme="dark"] .game-rank-tag.rank-tag-dropped {
  border-color: #dc3545;
  border-bottom-color: #212631;
  background-color: #dc3545;
  color: #fff;
}

/* The day tab, which is white on a picture for the same reason the rank tab is and follows the page
   for the same reason too. It has no coloured variants to be undone by this, so unlike the rank tab
   it only has to be said once. */
[data-bs-theme="dark"] .game-date-tag {
  border-color: #212631;
  background-color: #212631;
  color: #E6E9EF;
}

/* The drawer under a finished game. */
[data-bs-theme="dark"] .drawer-inner {
  border-top-color: #373F4E;
}

[data-bs-theme="dark"] .drawer-order,
[data-bs-theme="dark"] .drawer-arrow {
  color: #9AA4B5;
}

[data-bs-theme="dark"] .drawer-you {
  color: #E6E9EF;
}

/* The link through to the stats page, and the site blue generally: this is the highlight colour and
   it is the one thing that gets brighter in the dark rather than duller. */
[data-bs-theme="dark"] .drawer-link {
  color: #1897FF;
}

/* The stats page. The crown bars, the head to head grid and the calendar all draw their own
   backgrounds and lines, none of which bootstrap knows about. */
[data-bs-theme="dark"] .crown-bar-box {
  background: #373F4E;
}

[data-bs-theme="dark"] .head-to-head th,
[data-bs-theme="dark"] .head-to-head tbody th {
  background: #212631;
}

[data-bs-theme="dark"] .head-to-head td,
[data-bs-theme="dark"] .head-to-head th {
  border-color: #4E576A;
}

[data-bs-theme="dark"] .head-to-head-self {
  background: #2A3140;
}

/* A day nobody played. The four shades above it are the github greens and they hold up on a dark
   background exactly as they do on github's own, so only the empty square needs saying -- and it
   has to say it with :not, because otherwise this rule outweighs .calendar-level-1 and paints the
   whole calendar the colour of an empty day. */
[data-bs-theme="dark"] .calendar-day:not([class*="calendar-level-"]) {
  background: #2A3140;
}

/* The three Chart.js drawings. A canvas is transparent, so left alone they are bars floating on the
   page with no edge anywhere to say where the chart stops and the page starts. A shade up from the
   page is enough to put a box round them without having to draw a border as well. Nothing like it
   in the light theme, where the page is white and a white box on white is exactly what it looks
   like.

   On the canvas rather than on the box it sits in, because the box is a bootstrap column and a
   column is twelve pixels wider than its own contents on each side. Painting the column would put
   the edge of every chart twelve pixels out from the heading above it, and three boxes each hanging
   past the words that name them reads as a mistake. The canvas is the chart itself, so it lines up. */
[data-bs-theme="dark"] .stats-chart-box canvas {
  background: #2A3140;
  border-radius: 6px;
}

/* The unreadable-paste pink, said again for the dark theme. Dark enough to sit in the modal without
   glowing and red enough to still read as a complaint. The text has to be named too: the pale rule
   above sets a near-black to go on the pink, and inheriting that here would swap one unreadable
   combination for another. */
[data-bs-theme="dark"] #paste-score.paste-unreadable {
  background-color: #3A2429;
  color: #E6E9EF;
}

/* The footer sits at the end of the page, which on a short page is halfway up the screen with a
   field of white underneath it. Pushed to the bottom instead, and by sticky rather than by anything
   fixed, because a footer that is fixed is a footer that rides along over the scoreboard all day.

   How it works is worth writing down, because it does not look like it does anything. Sticky can
   only shift an element as far as the bottom of the thing it sits in, and this is the last thing in
   .main, so the room it has to move is however much of .main is empty. A long page has none, and the
   rule does nothing at all. A short page has the difference between the content and the minimum
   height above, and it takes all of it. Which is the whole behaviour, in one property, without the
   page having to be measured or told anything. */
#footer-row {
  position: sticky;
  top: 100vh;
}
