/* Catch Strategy: one EGA text screen, 8 x 16 px cells, 16 colours, nothing off the grid.
   Everything that looks like a frame, a rule or a plate is a real character in the IBM VGA face,
   never a CSS border: the reference is a text-mode screen, so its lines ARE type. */

@font-face {
  font-family: "VGA";
  src: url("/fonts/ibmvga8x16.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

:root {
  /* The IBM EGA 16, measured off his screen. Only these inks, with one exception noted below. */
  --black: #000000;
  --blue: #0000aa;          /* the field: 54% of his picture */
  --green: #00aa00;
  --cyan: #00aaaa;          /* frames, plates, the selection bar */
  --grey: #aaaaaa;          /* secondary text, 5.7:1 on the field */
  --dark: #555555;
  --lcyan: #55ffff;         /* a live reading */
  --lgreen: #55ff55;        /* caught, passed */
  --yellow: #ffff55;        /* the numbers that are money */
  --white: #ffffff;         /* primary text */
  /* EGA light red is #ff5555, which measures 4.2:1 on this blue. Text red is lifted to clear 4.5:1;
     the sprites keep the true EGA red. */
  --red: #ff6e6e;

  --cell: 8px;              /* the face's advance at 16px: every width is a whole number of these */
  --row: 16px;              /* its line box */
  --gap: 8px;
  --pad: 16px;              /* one em: the inner padding every filled box keeps */
}

/* THE INK RULE. One colour per job, the same on every screen of this site:
     label, column head, section head        light cyan   #55ffff   10.8:1
     value, prose, a line of the mechanic     white        #ffffff   15.9:1
     money (SOL, $CATCH, a price)             yellow       #ffff55   13.9:1
     passed, caught                           light green  #55ff55   12.3:1
     failed, fled                             red          #ff6e6e    4.9:1
     secondary, waiting, not yet              grey         #aaaaaa    5.7:1
     structure (frame, rule, divider, plate)  cyan         #00aaaa    4.6:1, carries no words
   Nothing on the page picks an ink for emphasis: it picks the one its job is given here. */

* { box-sizing: border-box; }

html {
  background: var(--blue);
  /* the screen's own grain: a scan row every 4th pixel, even and regular across the whole field */
  background-image: repeating-linear-gradient(
    to bottom, rgba(0, 0, 0, 0.17) 0 1px, rgba(0, 0, 0, 0) 1px 4px);
}

body {
  margin: 0;
  padding: var(--gap) 0 var(--row);
  color: var(--white);
  font: 16px/16px "VGA", "Courier New", monospace;
  font-synthesis: none;
  text-rendering: geometricPrecision;
  -webkit-font-smoothing: none;
  /* Chrome on Windows ignores font-smoothing and paints colour fringes on a bitmap face; text in
     its own compositing layer is drawn with grey antialiasing instead */
  will-change: transform;
}

img { image-rendering: pixelated; display: block; }

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

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 10;
  background: var(--cyan); color: var(--black); padding: 12px var(--pad);
}
.skip:focus { left: 8px; top: 8px; }

:focus-visible { outline: 2px solid var(--yellow); outline-offset: 2px; }

/* ---------------------------------------------------------------- the screen */

.screen {
  width: min(1120px, calc(100% - 32px));
  margin: 0 auto;
  min-height: calc(100vh - 32px);
  display: flex;
  flex-direction: column;
}
@supports (width: round(down, 11px, 8px)) {
  body { --sw: round(down, min(1120px, calc(100% - 32px)), var(--cell)); }
  .screen {
    width: var(--sw);
    margin-left: round(down, calc((100% - var(--sw)) / 2), 1px);
    margin-right: auto;
  }
}

/* ---------------------------------------------------------------- menu bar */

.menubar {
  display: flex;
  align-items: center;
  gap: var(--pad);
  height: 40px;
  padding: 0 var(--pad);
  background: var(--black);
  color: var(--lcyan);
}
.menubar__name { color: var(--white); }
.menubar__mid { flex: 1; color: var(--grey); overflow: hidden; white-space: nowrap; text-overflow: clip; }
.menubar__clock { color: var(--lcyan); }

/* ---------------------------------------------------------------- the shelf */

.shelf {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--gap);
  margin: var(--gap) 0;
}
/* whole cells, so a centred reading never starts on half a pixel and never blurs */
@supports (width: round(down, 11px, 8px)) {
  .shelf {
    grid-template-columns: repeat(5, round(down, calc((100% - var(--gap) * 4) / 5), var(--cell)));
    justify-content: space-between;
  }
}
.tile {
  display: flex;
  flex-direction: column;
  /* stretch, not centre: the five readings are one row of equal fields, not five labels of
     whatever width their number happens to be */
  align-items: stretch;
  gap: var(--gap);
  padding: 0 var(--pad) 8px;
  background: none;
  border: 0;
  font: inherit;
  color: var(--grey);
  cursor: pointer;
}
.tile__icon { width: 64px; height: 64px; align-self: center; }
.tile__value {
  width: 100%;
  padding: 8px var(--pad);
  background: var(--cyan);
  color: var(--black);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
}
.tile__label { color: var(--lcyan); text-align: center; }
.tile:hover .tile__value, .tile:focus-visible .tile__value { background: var(--lcyan); }
.tile:active .tile__value { background: var(--white); }
.tile:hover .tile__icon { transform: translateY(-2px); }
.tile__icon { transition: transform 0.08s steps(2); }

/* ---------------------------------------------------------------- path line */

/* The path above the panels and the prompt on the command line say the same thing and do the same
   job, so they are one object drawn twice: the same plate, the same height, the same inset. */
.path { margin: 0 0 var(--gap); height: 32px; }
.path__plate, .cmd__prompt {
  display: inline-flex;
  align-items: center;
  height: 32px;
  padding: 0 var(--pad);
  background: var(--cyan);
  color: var(--black);
  white-space: nowrap;
}

/* ---------------------------------------------------------------- the panes */

.panes {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--pad);
  align-items: stretch;
}
@supports (width: round(down, 11px, 8px)) {
  /* both panels the same whole number of cells wide, like the two halves of his screen */
  .panes { grid-template-columns: round(down, calc((100% - var(--pad)) / 2), var(--cell)) minmax(0, 1fr); }
}

.pane {
  position: relative;
  display: grid;
  grid-template-columns: var(--cell) minmax(0, 1fr) var(--cell);
  grid-template-rows: var(--row) minmax(0, 1fr) var(--row);
  color: var(--cyan);
  min-width: 0;
}
.frame__top { grid-area: 1 / 1 / 2 / 4; }
.frame__bottom { grid-area: 3 / 1 / 4 / 4; }
.pane__body { grid-area: 2 / 2 / 3 / 3; }
/* the two vertical runs are drawn, not laid out: 260 rows of glyph would otherwise make the
   grid row 4 000 px tall, because overflow does not shrink intrinsic height */
.frame__edge { position: absolute; top: var(--row); bottom: var(--row); width: var(--cell); }
.frame__edge--l { left: 0; }
.frame__edge--r { right: 0; }

.frame__top, .frame__bottom {
  display: flex;
  align-items: flex-start;
  height: var(--row);
  overflow: hidden;
  white-space: nowrap;
  color: var(--cyan);
}
.frame__corner { flex: none; }
/* a run of double rule, long enough for any width and clipped mid-glyph without a seam */
.rule { flex: 1; overflow: hidden; min-width: 0; }
.frame__title { flex: none; color: var(--white); padding: 0 var(--cell); }
/* the plate is 32 px and sits centred on the 16 px rule, so its words keep 8 px of air */
.frame__title--plate {
  display: flex;
  align-items: center;
  height: 32px;
  margin: -8px 0;
  background: var(--cyan);
  color: var(--black);
  padding: 0 var(--pad);
}
.frame__foot { flex: none; color: var(--lcyan); padding: 0 var(--cell); }
.frame__edge { overflow: hidden; white-space: pre; color: var(--cyan); }

.pane__body {
  padding: var(--pad) 0;
  color: var(--white);
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* ---------------------------------------------------------------- list rows */

/* the rows and the card under them follow one another: the panel keeps its slack at the foot,
   never as a hole in the middle */
.list { display: flex; flex-direction: column; }
.head, .row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--cell) 96px var(--cell) 120px;
  align-items: center;
  height: 32px;
  padding: 0 var(--pad);
  gap: 0;
}
.head { color: var(--lcyan); height: var(--row); margin-bottom: 8px; }
.head > span, .row > span { overflow: hidden; white-space: nowrap; text-overflow: clip; }
.row { background: none; border: 0; font: inherit; color: var(--white); text-align: left; cursor: pointer; width: 100%; }
.row .bar { color: var(--cyan); text-align: center; }
.row__num { color: var(--yellow); text-align: right; }   /* a reading in money */
.row__val { color: var(--white); text-align: right; }    /* a reading that is not money */
.row__date { color: var(--grey); text-align: right; }
.row[aria-current="true"] { background: var(--cyan); color: var(--black); }
.row[aria-current="true"] .row__num,
.row[aria-current="true"] .row__val,
.row[aria-current="true"] .row__date,
.row[aria-current="true"] .bar { color: var(--black); }
.row:hover:not([aria-current="true"]) { color: var(--lcyan); }
.row--empty { color: var(--grey); cursor: default; display: block; line-height: 32px; }

/* A panel's table: the label at the left edge of the panel, the reading at its right edge, one
   row height for every row, one gap between every group. A value never stands against its label. */
.tbl { display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: var(--pad); }
.tbl__k { color: var(--lcyan); line-height: var(--row); overflow: hidden; white-space: nowrap; }
.tbl__v { color: var(--white); line-height: var(--row); text-align: right; min-width: 0; overflow-wrap: anywhere; }
.tbl__v.money { color: var(--yellow); }
.tbl__v.muted { color: var(--grey); }
.tbl__head { color: var(--lcyan); line-height: var(--row); margin: 0; }

.pad { padding: 0 var(--pad); }
.right { display: flex; flex-direction: column; gap: var(--gap); }
.right__head p { margin: 0; line-height: var(--row); }
.right .key { margin-top: 0; }
.muted { color: var(--grey); }
.live { color: var(--lcyan); }
.good { color: var(--lgreen); }
.bad { color: var(--red); }
.money { color: var(--yellow); }
.sep { color: var(--cyan); overflow: hidden; white-space: nowrap; height: var(--row); }

/* ---------------------------------------------------------------- the grass view */

.grass {
  display: flex;
  flex-direction: column;
  flex: 1;
  gap: var(--gap);
  padding: 0 var(--pad);
}
.grass__scene {
  position: relative;
  width: 320px;
  height: 144px;
  margin: 0 auto;
  flex: none;
  overflow: hidden;
}
/* the picture keeps its own 2x scale and is cropped from the top: a resized pixel is a blurred one */
.grass__sky { width: 320px; height: 180px; margin-top: -36px; }
.grass__actor {
  position: absolute;
  left: 50%;
  bottom: 32px;
  width: 128px;
  height: 128px;
  margin-left: -64px;
}
.grass__lines { display: flex; flex-direction: column; gap: 8px; }
.kv { display: flex; gap: var(--cell); align-items: baseline; }
.kv__k { color: var(--lcyan); flex: none; width: 136px; }
.kv__v { color: var(--white); overflow: hidden; white-space: nowrap; min-width: 0; }
.grass .kv { align-items: flex-start; }
.grass .kv__v { white-space: normal; line-height: 24px; }
.step { display: flex; gap: var(--cell); align-items: flex-start; height: auto; padding: 4px var(--pad); margin: 0 calc(var(--pad) * -1); }
.step__n { color: var(--lcyan); flex: none; width: 24px; }
.step__text { color: var(--white); line-height: 24px; min-width: 0; }
.kv__v.money { color: var(--yellow); }

.gauge { display: flex; gap: var(--cell); align-items: center; height: var(--row); }
.gauge__label { color: var(--lcyan); flex: none; width: 112px; }
.gauge__track { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; color: var(--grey); position: relative; }
.gauge__fill { color: var(--lgreen); position: absolute; left: 0; top: 0; overflow: hidden; white-space: nowrap; }
.gauge__fill--amber { color: var(--yellow); }
.gauge__fill--low { color: var(--red); }
.gauge__value { flex: none; color: var(--white); width: 64px; text-align: right; }

.shakes { display: flex; flex-direction: column; gap: 8px; }
.shake { display: flex; gap: var(--cell); align-items: center; height: var(--row); }
.shake__n { color: var(--lcyan); flex: none; width: 136px; }
.shake__hash { color: var(--grey); flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; }
.shake__out { flex: none; width: 96px; text-align: right; }
.shake--waiting .shake__out { color: var(--grey); }

/* ---------------------------------------------------------------- command line */

.cmd {
  display: flex;
  align-items: center;
  gap: var(--cell);
  height: 32px;
  margin-top: var(--gap);
  padding: 0;
}
.cmd__prompt { flex: none; }
.cmd__input {
  flex: 0 1 auto;
  min-width: 0;
  background: none;
  border: 0;
  color: var(--lcyan);
  font: inherit;
  padding: 0;
  caret-color: transparent;
}
.cmd__input:focus { outline: none; }
.cmd__caret { color: var(--lcyan); flex: none; }
.cmd:focus-within .cmd__caret { animation: blink 1s steps(1) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.cmd__echo { margin-left: auto; padding-left: var(--pad); color: var(--grey); overflow: hidden; white-space: nowrap; text-align: right; min-width: 0; }

/* ---------------------------------------------------------------- function keys */

.fkeys {
  display: grid;
  grid-template-columns: repeat(10, minmax(0, 1fr));
  gap: 0;
  margin-top: 0;
  background: var(--black);
}
/* ten equal keys of a whole number of cells. A 1fr column lands on a fraction of a pixel at most
   window widths (1024 gave 198.4), and every word inside it is then drawn half a pixel off its
   grid and goes soft. The leftover is always a multiple of 8, so centring stays on whole pixels. */
@supports (width: round(down, 11px, 8px)) {
  .fkeys { grid-template-columns: repeat(10, round(down, calc(100% / 10), var(--cell))); justify-content: center; }
}
.fkey {
  display: flex;
  align-items: center;
  gap: 0;
  height: 48px;
  padding: 0;
  background: none;
  border: 0;
  font: inherit;
  color: var(--white);
  cursor: pointer;
  min-width: 0;
}
.fkey__n { flex: none; width: 24px; padding-left: var(--gap); text-align: right; color: var(--white); }
.fkey__label {
  flex: 1;
  min-width: 0;
  height: 32px;
  margin: 8px;
  padding: 8px var(--pad);
  text-align: center;
  background: var(--cyan);
  color: var(--black);
  overflow: hidden;
  white-space: nowrap;
}
.fkey[aria-current="true"] .fkey__label { background: var(--lcyan); }
.fkey:hover .fkey__label, .fkey:focus-visible .fkey__label { background: var(--white); }
.fkey:active .fkey__label { background: var(--white); color: var(--blue); }

/* ---------------------------------------------------------------- dialogs */

.dialogs { position: fixed; inset: 0; pointer-events: none; z-index: 20; }
.dlg {
  position: fixed;
  inset: 0;
  margin: auto;
  border: 0;
  padding: 0;
  width: min(696px, calc(100vw - 32px));
  height: fit-content;
  max-height: calc(100vh - 32px);
  background: var(--blue);
  color: var(--white);
  pointer-events: auto;
  display: grid;
  grid-template-columns: var(--cell) minmax(0, 1fr) var(--cell);
  grid-template-rows: var(--row) minmax(0, 1fr) var(--row);
  /* the drop shadow a Norton dialog casts on the screen behind it */
  box-shadow: 8px 16px 0 rgba(0, 0, 0, 0.55);
}
@supports (width: round(down, 11px, 8px)) {
  .dlg {
    --dw: round(down, min(696px, calc(100vw - 32px)), var(--cell));
    width: var(--dw);
    margin-left: round(down, calc((100vw - var(--dw)) / 2), 1px);
    margin-right: auto;
  }
}
.dlg::backdrop { background: rgba(0, 0, 0, 0.55); }
.dlg .kv__v { white-space: normal; overflow-wrap: anywhere; line-height: 24px; }
.dlg .kv { align-items: flex-start; }
.dlg__body {
  grid-area: 2 / 2 / 3 / 3;
  padding: var(--row) var(--pad);
  overflow: auto;
  display: flex;
  flex-direction: column;
  gap: var(--row);
  overscroll-behavior: contain;
}
.dlg h2 { font: inherit; margin: 0; color: var(--lcyan); }
.dlg h3 { font: inherit; margin: 0; color: var(--lcyan); }
.dlg p { margin: 0; color: var(--white); line-height: 24px; }
.dlg p + p { margin-top: 8px; }
.dlg ol, .dlg ul { margin: 0; padding-left: 24px; line-height: 24px; }
.dlg li + li { margin-top: 8px; }
.dlg__keys { display: flex; flex-wrap: wrap; gap: var(--gap); }

.key {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cell);
  min-height: 40px;
  padding: 12px var(--pad);
  background: var(--cyan);
  color: var(--black);
  border: 0;
  font: inherit;
  text-decoration: none;
  cursor: pointer;
}
.key:hover, .key:focus-visible { background: var(--lcyan); }
.key:active { background: var(--white); }
.key[aria-disabled="true"]:hover, .key[aria-disabled="true"]:active { background: var(--black); }
.key[aria-disabled="true"] { background: var(--black); color: var(--grey); cursor: not-allowed; }
.key__mark { width: 16px; height: 16px; flex: none; }
.key--wide { min-width: 200px; }

.ca {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap);
}
.ca__value {
  flex: 1;
  min-width: 0;
  padding: 12px var(--pad);
  background: var(--black);
  color: var(--lgreen);
  overflow-wrap: anywhere;
  line-height: 24px;
  user-select: all;
}
.ca__value--empty { color: var(--grey); user-select: auto; }

/* ---------------------------------------------------------------- phone */

@media (max-width: 1135px) {
  .fkeys { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  @supports (width: round(down, 11px, 8px)) {
    .fkeys { grid-template-columns: repeat(5, round(down, calc(100% / 5), var(--cell))); }
  }
}

/* under about 700 px five keys in a row cannot hold a word, so the strip stands up in two */
@media (max-width: 719px) {
  .fkeys { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  @supports (width: round(down, 11px, 8px)) {
    .fkeys { grid-template-columns: repeat(2, round(down, calc(100% / 2), var(--cell))); }
  }
}

@media (max-width: 899px) {
  body { padding: 8px 0 var(--row); }
  .screen { width: calc(100% - 16px); min-height: 0; }
  @supports (width: round(down, 11px, 8px)) {
    .screen { width: round(down, calc(100% - 16px), var(--cell)); }
  }
  .menubar { height: 40px; padding: 0 var(--pad); gap: var(--gap); }
  .menubar__mid { display: none; }
  .shelf { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0; margin: var(--gap) 0; }
  @supports (width: round(down, 11px, 8px)) {
    .shelf { grid-template-columns: repeat(5, round(down, calc(100% / 5), var(--cell))); justify-content: space-between; }
  }
  .tile { padding: 8px 0; gap: 4px; }
  .tile__value { min-width: 0; width: 100%; padding: 0; background: none; color: var(--yellow); }
  .tile__label { display: none; }
  .panes { grid-template-columns: minmax(0, 1fr); gap: var(--row); }
  @supports (width: round(down, 11px, 8px)) {
    .panes { grid-template-columns: minmax(0, 1fr); }
  }
  .kv { align-items: flex-start; }
  .kv__v { white-space: normal; line-height: 24px; }
  .head, .row { grid-template-columns: minmax(0, 1fr) var(--cell) 88px; padding: 0 var(--pad); }
  .head > span:nth-child(4), .head > span:nth-child(5),
  .row > span:nth-child(4), .row > span:nth-child(5) { display: none; }
  .grass { padding: 0 var(--gap); }
  .gauge__label { width: 96px; }
  .shake__hash { font-size: 16px; }
  .fkey { height: 56px; }
  .fkey__label { margin: 12px 4px; }
  .cmd { padding: 0; }
  .dlg { width: calc(100vw - 16px); }
  .dlg__body { padding: var(--row) var(--gap); }
}

@media (max-width: 359px) {
  .shelf .tile:nth-child(n + 4) { display: none; }
  .shelf { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  @supports (width: round(down, 11px, 8px)) {
    .shelf { grid-template-columns: repeat(3, round(down, calc(100% / 3), var(--cell))); justify-content: space-between; }
  }
}

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------- the card at the panel's foot */

.card {
  display: flex;
  gap: var(--pad);
  align-items: flex-start;
  padding: var(--row) var(--pad) 0;
}
.card__art { width: 96px; height: 96px; flex: none; }
.card__lines { display: flex; flex-direction: column; gap: 8px; min-width: 0; flex: 1; }
.card .kv__k { width: 120px; }
.card .kv { align-items: flex-start; }
.card .kv__v { white-space: normal; line-height: 24px; }

/* ---------------------------------------------------------------- the tape under the grass */

.tape { display: flex; flex-direction: column; }
.tape__head { margin: 8px 0; color: var(--lcyan); }
.tape__row {
  display: grid;
  grid-template-columns: 48px minmax(0, 1fr) 48px 88px;
  align-items: center;
  gap: var(--cell);
  height: 32px;
  padding: 0 var(--pad);
  margin: 0 calc(var(--pad) * -1);
  background: none;
  border: 0;
  font: inherit;
  color: var(--white);
  text-align: left;
  cursor: pointer;
}
.tape__row > span { overflow: hidden; white-space: nowrap; }
.tape__n { color: var(--lcyan); }
.tape__score { color: var(--white); text-align: right; }
.tape__out { text-align: right; }
.tape__row:hover { background: var(--cyan); color: var(--black); }
.tape__row:hover .tape__n, .tape__row:hover .tape__score,
.tape__row:hover .tape__out { color: var(--black); }

/* ---------------------------------------------------------------- the narrowest screens */
/* Kept last on purpose: a media query written above the rule it overrides loses to it. */

@media (max-width: 479px) {
  /* the scene drops to its own 1x size rather than being cut by the panel's frame */
  .grass__scene { width: 160px; height: 72px; }
  .grass__sky { width: 160px; height: 90px; margin-top: -18px; }
  .grass__actor { width: 64px; height: 64px; margin-left: -32px; bottom: 16px; }
  .kv__k, .shake__n { width: 88px; }
  .card__art { width: 48px; height: 48px; }
  .card .kv__k { width: 80px; }
  .gauge__label { width: 72px; }
  .tape__row { grid-template-columns: 40px minmax(0, 1fr) 40px 72px; gap: 4px; }
  .shake { flex-wrap: wrap; height: auto; }
  .shake__hash { flex-basis: 100%; order: 3; padding-left: 96px; }
  .shake__out { margin-left: auto; }
}
