/* ==========================================================================
   screens.css -- how RetroServe's text is laid out inside the tube.

   The JS prints lines carrying these class names. Think of them as the
   web equivalent of ANSI escape codes: .c-cyan is "\033[36m".
   ========================================================================== */

/* --- Lines ---------------------------------------------------------------- */

.line {
  white-space: pre;          /* ASCII art lives or dies by this */
  min-height: calc(1em * var(--lh));   /* keeps blank lines blank, not collapsed */
}

.center { text-align: center; }

/* A run of art centered as one unit, so its internal columns stay square
   no matter how ragged the individual line lengths are. */
.blockwrap { text-align: center; }
.block {
  display: inline-block;
  text-align: left;
  white-space: pre;
}
/* Art lines are never blank, so drop the blank-line min-height -- left in,
   it would force rows taller than their line-height and pull the block
   characters of the wordmark apart. */
.block .line { min-height: 0; }

/* --- Color ---------------------------------------------------------------- */

.c-red        { color: var(--ansi-red); }
.c-green      { color: var(--ansi-green); }
.c-yellow     { color: var(--ansi-yellow); }
.c-blue       { color: var(--ansi-blue); }
.c-magenta    { color: var(--ansi-magenta); }
.c-cyan       { color: var(--ansi-cyan); }
.c-white      { color: var(--ansi-white); }

.c-br-red     { color: var(--ansi-br-red); }
.c-br-green   { color: var(--ansi-br-green); }
.c-br-yellow  { color: var(--ansi-br-yellow); }
.c-br-blue    { color: var(--ansi-br-blue); }
.c-br-magenta { color: var(--ansi-br-magenta); }
.c-br-cyan    { color: var(--ansi-br-cyan); }
.c-br-white   { color: var(--ansi-br-white); }

.dim  { opacity: .52; }
.bold { font-weight: 700; }

/* The landmass on the national map: present enough to read as a country,
   faint enough that the temperatures written over it stay the subject. */
.map-land { color: var(--ansi-cyan); opacity: .38; }

/* The reading under the cursor. A temperature is only two or three
   characters, so the highlight used on list rows -- a rail and a caret in
   the gutter -- has nothing to attach to. Reverse video does: it survives
   the scanlines, it needs no extra columns, and it is unmistakably "this
   one". The white is deliberate rather than the band colour, so the pin
   never reads as a temperature reading of its own. */
.map-pin {
  background: var(--ansi-br-white);
  color: #04161a;
  text-shadow: none;
  font-weight: 700;
}

/* Reverse video. */
.rev {
  background: var(--ansi-cyan);
  color: #04161a;
  text-shadow: none;
}

/* A chart bar is a box, not a character.

   It was stacked block glyphs, and that cannot be made to work. Whether two
   █ in consecutive rows touch depends on whether the loaded font draws that
   glyph to the full height of its em box -- DejaVu does, SF Mono does not,
   and there is no line-height that fixes a font. Every bar came out as a
   stack of floating bricks on exactly the machines we could not test on.

   So the bar is a span sized in fractions of a row. It sits on the character
   grid like everything else -- one column wide, one row tall -- but its
   height is arithmetic rather than typography, which no font can spoil. It
   also drops the eight-step quantisation that block glyphs forced: a bar is
   now exactly as tall as its value. */
.bar {
  display: inline-block;
  width: calc(var(--w, 1) * 1ch);
  height: calc(1em * var(--lh));
  vertical-align: top;
  background: linear-gradient(to top,
    currentColor 0, currentColor var(--f), transparent var(--f));
}

/* --- Screen furniture ----------------------------------------------------- */

.logo {
  /* The wordmark is wider than the screen, so it renders smaller than the
     body text. --logo-scale is measured from the art itself in art.js. */
  font-size: calc(1em * var(--logo-scale));
  line-height: 1;            /* exactly 1 so the █ rows touch and the
                                letterforms read as solid shapes */
  color: var(--ansi-br-cyan);
}

/* The key guide is chrome, so it is drawn like chrome: a solid bar across
   the full width with dark text on it. Reverse video was how a terminal
   marked "this row is the machine talking, not the content", and that is
   exactly what this row is. */
.guide {
  display: flex;
  justify-content: space-between;
  padding: 0 1ch;
  background: var(--ansi-cyan);
  color: #04161a;
  text-shadow: none;
}

/* On the bar the key is the darkest, heaviest thing and the word explaining
   it recedes -- the same hierarchy as before, inverted for the background. */
.guide .k { font-weight: 700; color: #02282e; }
.guide .l { color: #04161a; opacity: .68; }

/* --- Menu ----------------------------------------------------------------- */

.menu-item {
  white-space: pre;
  cursor: pointer;
  border-radius: 2px;
  position: relative;        /* anchors the selection caret */
}

/*
   The selection highlight.

   Arrow keys and the pointer drive the same class, so there is only ever
   one "you are here" on the glass.

   A solid reverse-video block is what a terminal would have used, but on
   this screen it fights the scanlines and the phosphor bloom and comes out
   muddy. So the highlight leans on the two things that stay legible through
   those layers: a bright rail down the left edge and a caret in the gutter.
   The wash behind the text falls away to the right, which reads as a beam
   catching the row rather than a rectangle stamped on top of it -- and
   because it fades, it never competes with the text it is pointing at.

   The caret sits in the two blank columns every row already starts with, so
   nothing shifts when a row becomes selected.
*/
.menu-item.is-selected,
.menu-item:focus-visible {
  background: linear-gradient(90deg,
    rgba(124, 250, 250, .20) 0%,
    rgba(124, 250, 250, .08) 42%,
    rgba(124, 250, 250, 0) 88%);
  box-shadow: inset 2px 0 0 0 var(--ansi-br-cyan);
  outline: none;
}
.menu-item.is-selected::before,
.menu-item:focus-visible::before {
  content: '\25B8';                 /* a small right-pointing triangle */
  position: absolute;
  left: .35ch;
  color: var(--ansi-br-cyan);
  text-shadow: 0 0 6px rgba(124, 250, 250, .8);
  pointer-events: none;
}

.menu-item .key   { color: var(--ansi-br-yellow); }
.menu-item .label { color: var(--ansi-br-white); }
.menu-item .hint  { color: var(--ansi-white); opacity: .5; }

/* Selection lifts the dimmed hint rather than recolouring the whole row --
   enough to register as "live", not enough to shout. */
.menu-item.is-selected .hint { opacity: .78; }

/* Item labels and hints are padded to a fixed column in JS, so the
   highlight lines up across the whole block. */

/* --- Input ---------------------------------------------------------------- */

.prompt-line { white-space: pre; }

.typed { color: var(--ansi-br-white); }

.cursor {
  color: var(--ansi-br-white);
  animation: blink 1.05s steps(1) infinite;
}
@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* The real <input> that captures keystrokes -- kept in the DOM (and only
   1px wide) so phones raise their on-screen keyboard, but visually the
   .typed span and .cursor above are what the user sees. */
.capture {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  border: 0;
  padding: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .cursor { animation: none; }
}
