/* CHESS v0.3 — bitmap.
   Everything on this page is drawn on a 4px grid in six colours. Two rules keep
   it honest: nothing is anti-aliased, and nothing is a fraction of a pixel. */

@font-face {
  font-family: Pixelify;
  src: url("/fonts/pixelify-400-700.woff2") format("woff2");
  font-weight: 400 700;
  font-display: swap;
}
@font-face {
  font-family: Silkscreen;
  src: url("/fonts/silkscreen-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: Silkscreen;
  src: url("/fonts/silkscreen-700.woff2") format("woff2");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-400.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url("/fonts/jetbrains-500.woff2") format("woff2");
  font-weight: 500;
  font-display: swap;
}

:root {
  /* Six values, and no seventh. Two of them are the squares of a wooden board
     taken down to surface luminance; the ink is that board's light square at
     full strength. Amber is off an analogue chess clock face and is the only
     colour on the page that is allowed to mean something. */
  --ground: #0e0d0b;
  --dark-sq: #1e1a15;
  --light-sq: #322a20;
  --chalk: #f0d9b5;
  --amber: #f0a202;
  --muted: #9d9280;

  /* The grid. Every margin, padding, gap and box on the page is a whole
     number of these. Bitmap layouts step; they do not scale. */
  --u: 4px;

  --display: Pixelify, ui-sans-serif, system-ui, sans-serif;
  --ui: Silkscreen, ui-monospace, monospace;
  --data: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace;

  --pad: calc(var(--u) * 4);
  --hop: calc(var(--u) * 8);
}

* {
  box-sizing: border-box;
}

html {
  background: var(--ground);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--chalk);
  font-family: var(--data);
  font-size: 14px;
  line-height: 20px;
  /* Bitmap faces are drawn, not rendered. Smoothing them is vandalism. */
  -webkit-font-smoothing: none;
  font-smooth: never;
}

img,
svg {
  image-rendering: pixelated;
}

/* Nothing on this page has a radius. Pixels are square. */

h1,
h2,
h3 {
  font-family: var(--display);
  font-weight: 700;
  margin: 0;
  line-height: 1;
  letter-spacing: 0;
}

/* Pixelify's descenders live below the em box, so display type is set on a
   line that leaves room for them rather than on its own height. */
h2 {
  font-size: 32px;
  line-height: 40px;
  margin-bottom: calc(var(--u) * 6);
}

h3 {
  font-size: 16px;
  line-height: 20px;
  color: var(--chalk);
  margin-bottom: calc(var(--u) * 4);
}

/* Silkscreen only ever runs short and uppercase, which is where it is crisp. */
.tag {
  font-family: var(--ui);
  font-size: 8px;
  font-weight: 400;
  line-height: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0;
}

.note {
  margin-top: calc(var(--u) * -4);
  margin-bottom: calc(var(--u) * 5);
  max-width: 88ch;
}

a {
  color: inherit;
  text-decoration: none;
}

:focus-visible {
  outline: var(--u) solid var(--amber);
  outline-offset: 0;
}

.sr-only {
  position: absolute;
  margin: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

#app {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--pad) calc(var(--u) * 16);
}

.loading,
.error {
  padding: calc(var(--u) * 24) 0;
}
.error {
  color: var(--amber);
}

.empty {
  color: var(--muted);
  font-size: 12px;
  margin: 0;
  padding: calc(var(--u) * 3) 0;
}

/* ---------------------------------------------------------------- hero */

.hero-frame {
  container-type: inline-size;
  margin: 0 calc(var(--pad) * -1);
}

.hero {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: calc(var(--u) * 4);
  padding: var(--pad);
  border-bottom: 1px solid var(--light-sq);
  background: var(--ground);
}

.hero-top,
.hero-bottom {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: var(--u) calc(var(--u) * 4);
}

/* The knight sits on the left and the rating comes out from behind it. The
   stage is the only place on the page where things overlap. */
.hero-stage {
  position: relative;
  height: calc(var(--u) * 32);
}

.knight {
  position: absolute;
  left: 0;
  bottom: 0;
  width: calc(var(--u) * 32);
  height: calc(var(--u) * 32);
  z-index: 2;
}

.knight svg {
  display: block;
  width: 100%;
  height: 100%;
}

.hero-rating {
  position: absolute;
  left: calc(var(--u) * 26);
  bottom: calc(var(--u) * 5);
  margin: 0;
  z-index: 1;
  font-family: var(--display);
  font-weight: 700;
  font-size: 64px;
  line-height: 1;
  color: var(--chalk);
}

/* Sits above the number and clear of the knight — the one label that must
   never be the thing the sprite happens to be standing on. */
.hero-class {
  position: absolute;
  left: calc(var(--u) * 34);
  bottom: calc(var(--u) * 22);
  z-index: 3;
  color: var(--amber);
}

/* Three steps, no fluid scale: the sprite is only ever multiplied by a whole
   number, so its pixels stay square at every width. */
@container (min-width: 720px) {
  .hero {
    padding: calc(var(--u) * 8);
    gap: calc(var(--u) * 6);
  }
  .hero-stage {
    height: calc(var(--u) * 64);
  }
  /* --hop is declared on the knight itself at every step: it is the animated
     element, so a value set on an ancestor would be shadowed by this one. */
  .knight {
    --hop: calc(var(--u) * 16);
    width: calc(var(--u) * 64);
    height: calc(var(--u) * 64);
  }
  .hero-rating {
    left: calc(var(--u) * 52);
    bottom: calc(var(--u) * 10);
    font-size: 128px;
  }
  .hero-class {
    left: calc(var(--u) * 60);
    bottom: calc(var(--u) * 43);
    font-size: 12px;
    line-height: 16px;
  }
  .hero .tag {
    font-size: 12px;
    line-height: 16px;
  }
}

@container (min-width: 1100px) {
  .hero {
    aspect-ratio: 1200 / 630;
    padding: calc(var(--u) * 12);
  }
  .hero-stage {
    height: calc(var(--u) * 112);
  }
  .knight {
    --hop: calc(var(--u) * 28);
    width: calc(var(--u) * 112);
    height: calc(var(--u) * 112);
  }
  /* The first digit runs behind the muzzle, far enough to read as emerging
     and not so far that the number stops being legible. */
  .hero-rating {
    left: calc(var(--u) * 102);
    bottom: calc(var(--u) * 18);
    font-size: 320px;
  }
  .hero-class {
    left: calc(var(--u) * 118);
    bottom: calc(var(--u) * 84);
  }
}

/* ---------------------------------------------------------------- sections */

section {
  padding: calc(var(--u) * 12) 0;
  border-bottom: 1px solid var(--light-sq);
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 3) calc(var(--u) * 6);
  align-items: center;
  justify-content: space-between;
  margin-bottom: calc(var(--u) * 6);
}
.section-head h2 {
  margin: 0;
}

/* ---------------------------------------------------------------- classes */

.class-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--u);
}

/* The selected class is a light square; the rest are dark ones. Board colours
   doing the work of a selected state. */
.class {
  appearance: none;
  text-align: left;
  font: inherit;
  color: var(--muted);
  background: var(--dark-sq);
  border: 1px solid var(--light-sq);
  padding: calc(var(--u) * 4);
  cursor: pointer;
  display: block;
}

.class:hover {
  border-color: var(--muted);
}

.class[data-on="true"] {
  background: var(--light-sq);
  border-color: var(--amber);
  color: var(--chalk);
}

/* A dormant rating is still information: state it, do not fade it out. */
.class[data-dormant="true"] .class-note {
  display: block;
}

.class-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: calc(var(--u) * 2);
}

.class[data-on="true"] .class-name {
  color: var(--amber);
}

.class-rating {
  display: block;
  font-family: var(--display);
  font-weight: 700;
  font-size: 48px;
  line-height: 1;
  margin: calc(var(--u) * 3) 0 calc(var(--u) * 4);
  color: inherit;
}

.class-rows {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: calc(var(--u) * 2);
  margin: 0;
  font-size: 12px;
  line-height: 16px;
}
.class-rows dd {
  margin: var(--u) 0 0;
  color: var(--chalk);
}
.class[data-on="false"] .class-rows dd {
  color: var(--muted);
}

.class-note {
  display: none;
  margin-top: calc(var(--u) * 3);
}

/* ---------------------------------------------------------------- windows */

.windows {
  display: flex;
  gap: var(--u);
}

.window {
  appearance: none;
  font-family: var(--ui);
  font-size: 8px;
  line-height: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--dark-sq);
  border: 1px solid var(--light-sq);
  padding: calc(var(--u) * 2) calc(var(--u) * 3);
  cursor: pointer;
}

.window:hover {
  color: var(--chalk);
  border-color: var(--muted);
}

.window[data-on="true"] {
  color: var(--ground);
  background: var(--amber);
  border-color: var(--amber);
}

/* ---------------------------------------------------------------- curve */

.curve figure {
  margin: 0;
}

.chart-note {
  margin-bottom: calc(var(--u) * 3);
}

.curve svg {
  display: block;
  width: 100%;
  height: auto;
  shape-rendering: crispEdges;
}

.curve text {
  font-family: var(--ui);
  font-size: 8px;
  letter-spacing: 0.08em;
  fill: var(--muted);
}
.curve .label-next {
  fill: var(--amber);
}

/* ---------------------------------------------------------------- strip */

.strip dl {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: calc(var(--u) * 8) calc(var(--u) * 4);
  margin: 0;
}

.strip dt,
.strip dd {
  margin: 0;
}

.strip dt {
  font-family: var(--ui);
  font-size: 8px;
  line-height: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.strip dd {
  margin-top: calc(var(--u) * 2);
  font-family: var(--display);
  font-weight: 700;
  font-size: 32px;
  line-height: 1;
}

.strip dd small {
  display: block;
  margin-top: calc(var(--u) * 2);
  font-family: var(--data);
  font-size: 12px;
  line-height: 16px;
  font-weight: 400;
  color: var(--muted);
}

/* ---------------------------------------------------------------- patterns */

.pattern-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: calc(var(--u) * 8);
}

@media (min-width: 800px) {
  .pattern-grid {
    grid-template-columns: 1fr 1fr 0.8fr;
  }
}

.openings {
  list-style: none;
  margin: 0;
  padding: 0;
}

.openings li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: calc(var(--u) * 3);
  padding: calc(var(--u) * 2) 0;
  border-top: 1px solid var(--light-sq);
  font-size: 12px;
  line-height: 16px;
}
.openings li:first-child {
  border-top: 0;
}

.openings .name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Ten cells, one per ten points of win rate. A bar that reads four or five,
   never 4.3. */
.meter {
  display: flex;
  gap: 1px;
}
.meter i {
  width: var(--u);
  height: calc(var(--u) * 3);
  background: var(--light-sq);
}
.meter i[data-on="true"] {
  background: var(--amber);
}

.openings .rate {
  color: var(--muted);
  text-align: right;
  min-width: calc(var(--u) * 12);
}

.lengths {
  margin: 0;
}
.lengths > div {
  display: flex;
  justify-content: space-between;
  gap: calc(var(--u) * 3);
  padding: calc(var(--u) * 2) 0;
  border-top: 1px solid var(--light-sq);
  font-size: 12px;
  line-height: 16px;
}
.lengths > div:first-child {
  border-top: 0;
}
.lengths dt {
  color: var(--muted);
}
.lengths dd {
  margin: 0;
}

.timing {
  margin-top: calc(var(--u) * 12);
}

.bar-scale {
  margin-bottom: calc(var(--u) * 3);
}
.bars + .bar-scale {
  margin-top: calc(var(--u) * 8);
}

.bars {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: var(--u);
  --cell-w: calc(var(--u) * 12);
}
.bars.hours {
  grid-template-columns: repeat(24, 1fr);
  gap: 1px;
  --cell-w: calc(var(--u) * 6);
}

.bar-cell {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  min-width: 0;
}

/* His own average is the zero line and the bars are counted off it in whole
   cells: above it in amber, below it in the neutral. */
.bar-up,
.bar-down {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--u);
  height: calc(var(--u) * 14);
}
.bar-up {
  justify-content: flex-end;
}

.bar-mid {
  height: 1px;
  background: var(--light-sq);
}

.bar-up i,
.bar-down i {
  display: block;
  width: var(--cell-w);
  max-width: 100%;
  height: calc(var(--u) * 2);
  background: var(--muted);
}
.bar-up i {
  background: var(--amber);
}

/* Hollow means the same shape with fewer games behind it. */
.bar-cell[data-thin="true"] .bar-up i,
.bar-cell[data-thin="true"] .bar-down i {
  background: var(--ground);
  box-shadow: inset 0 0 0 1px var(--amber);
}
.bar-cell[data-thin="true"] .bar-down i {
  box-shadow: inset 0 0 0 1px var(--muted);
}

.bar-label {
  margin-top: calc(var(--u) * 2);
  font-family: var(--ui);
  font-size: 8px;
  line-height: 12px;
  color: var(--muted);
  text-align: center;
  min-height: 12px;
  overflow: hidden;
}

/* ---------------------------------------------------------------- ladder */

.projection {
  margin: 0 0 calc(var(--u) * 8);
  font-size: 14px;
  line-height: 20px;
  color: var(--muted);
  max-width: 68ch;
}
.projection b {
  color: var(--chalk);
  font-weight: 500;
  /* A date is one thing; it does not get to break across two lines. */
  white-space: nowrap;
}
.projection small {
  display: block;
  margin-top: calc(var(--u) * 2);
  font-size: 12px;
  line-height: 16px;
}

.ladder ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column-reverse;
}

/* The ladder is a tower of blocks, not a list with rules through it: the left
   column stacks into one column of squares you read from the bottom up. */
.ladder li {
  display: grid;
  grid-template-columns: calc(var(--u) * 6) calc(var(--u) * 16) 1fr;
  align-items: center;
  gap: calc(var(--u) * 4);
  padding: var(--u) 0;
  color: var(--muted);
}

/* Filled: he is at or above it now. Hollow amber: reached once, not now.
   Hollow neutral: still ahead of him. */
.ladder .mark {
  width: calc(var(--u) * 6);
  height: calc(var(--u) * 6);
  box-shadow: inset 0 0 0 1px var(--light-sq);
}
.ladder li[data-hit="true"] {
  color: var(--chalk);
}
.ladder li[data-hit="true"] .mark {
  box-shadow: inset 0 0 0 1px var(--amber);
}
.ladder li[data-current="true"] .mark {
  background: var(--amber);
  box-shadow: none;
}
.ladder li[data-next="true"] {
  color: var(--amber);
}
.ladder li[data-next="true"] .mark {
  box-shadow: inset 0 0 0 1px var(--amber);
}

.ladder .rating-cell {
  font-family: var(--display);
  font-weight: 700;
  font-size: 20px;
  line-height: 20px;
  color: inherit;
}

.ladder .when {
  font-size: 12px;
  line-height: 16px;
}
.ladder .when .again {
  color: var(--muted);
}
.ladder .when .next-tag {
  color: var(--amber);
}

/* ---------------------------------------------------------------- recent */

.recent ol {
  list-style: none;
  margin: 0;
  padding: 0;
}

.recent li {
  border-top: 1px solid var(--light-sq);
}
.recent li:first-child {
  border-top: 0;
}

.recent a {
  display: grid;
  grid-template-columns: calc(var(--u) * 3) 1fr auto;
  align-items: center;
  gap: var(--u) calc(var(--u) * 3);
  padding: calc(var(--u) * 3) 0;
  color: var(--muted);
}

.recent li[data-outcome="win"] a {
  color: var(--chalk);
}

.recent a:hover,
.recent a:focus-visible {
  background: var(--dark-sq);
}

/* One block per game, in the same vocabulary as the ladder: filled is a win,
   hollow is a loss, hollow-neutral is a draw. */
.recent .o {
  width: calc(var(--u) * 3);
  height: calc(var(--u) * 3);
  box-shadow: inset 0 0 0 1px var(--light-sq);
}
.recent li[data-outcome="win"] .o {
  background: var(--amber);
  box-shadow: none;
}
.recent li[data-outcome="draw"] .o {
  box-shadow: inset 0 0 0 1px var(--muted);
}

.recent .who {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12px;
}
.recent .who span {
  color: var(--muted);
  font-size: 12px;
}

.recent .num {
  text-align: right;
  font-size: 12px;
}
.recent .num .when {
  display: block;
  color: var(--muted);
  font-family: var(--ui);
  font-size: 8px;
  line-height: 12px;
}

/* ---------------------------------------------------------------- foot */

.foot {
  padding-top: calc(var(--u) * 10);
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--u) * 2) calc(var(--u) * 6);
  justify-content: space-between;
}
.foot a {
  color: var(--muted);
  border-bottom: 1px solid var(--light-sq);
}
.foot a:hover {
  color: var(--amber);
}

/* ---------------------------------------------------------------- motion */

/* The one moving thing on the page. The knight arrives the only way a knight
   can: two squares, then one, twice over — and it snaps, because a piece does
   not glide to its square. */
@keyframes knight-arrives {
  0%,
  28% {
    transform: translate(calc(var(--hop) * -3), calc(var(--hop) * 3));
  }
  29%,
  62% {
    transform: translate(calc(var(--hop) * -1), calc(var(--hop) * 2));
  }
  63%,
  100% {
    transform: translate(0, 0);
  }
}

.knight {
  animation: knight-arrives 900ms steps(1, end) both;
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
  .knight {
    transform: none;
  }
}
