/* Bamboozle - teletext presentation layer.
   The screen is a strict 40 x 25 character grid. Every row is absolutely
   positioned on the grid; the whole grid is stretched horizontally so the
   cells end up wider than they are in a normal monospace font, which is
   what gives teletext its squat, chunky proportions. */

:root {
  /* Set by JS in fit(). Row pitch is deliberately independent of the text
     size: teletext characters sit inside a taller cell with space around
     them, which is what keeps lines from crowding each other. */
  --cell-h: 16px;
  --fs-cell: 16px;
  --fs-text: 13px;
  --stretch: 1.34;
  --stretch-text: 1.6;
}

:root {
  /* The eight teletext colours. Nothing else is allowed on the screen. */
  --tt-black:   #000000;
  --tt-red:     #ff0000;
  --tt-green:   #00ff00;
  --tt-yellow:  #ffff00;
  --tt-blue:    #0000ff;
  --tt-magenta: #ff00ff;
  --tt-cyan:    #00ffff;
  --tt-white:   #ffffff;

  /* Off-screen furniture: the telly the page is displayed on. */
  --set-shell:  #16171a;
  --set-bezel:  #26282d;
  --set-edge:   #0a0b0c;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
  background: var(--set-shell);
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
  color: var(--tt-white);
  font-family: "DejaVu Sans Mono", "Liberation Mono", "Menlo", "Consolas", "Courier New", monospace;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}

#set {
  padding: clamp(6px, 1.6vmin, 18px);
  background: linear-gradient(180deg, var(--set-bezel), var(--set-edge));
  border-radius: clamp(6px, 1.4vmin, 16px);
  box-shadow: 0 0 0 1px #000, 0 18px 50px rgba(0, 0, 0, .7);
}

#frame {
  position: relative;
  overflow: hidden;
  background: var(--tt-black);
  border-radius: 3px;
}

/* The grid itself. Width/height are set in em so one font-size change
   rescales the entire page; JS sets that font-size to fit the viewport. */
#screen {
  position: relative;
  width: 32.16em;       /* replaced by JS in px */
  height: 25em;
  font-size: 16px;      /* replaced by JS */
  font-weight: 700;
}

.row {
  position: absolute;
  left: 0;
  top: 0;
  width: max-content;
  height: var(--cell-h);
  line-height: var(--cell-h);
  font-size: var(--fs-text);
  white-space: pre;
  transform-origin: 0 0;
  transform: scaleX(var(--stretch-text));
}

/* Rows made of block graphics fill their cells completely, so that
   mosaics stay contiguous with no seams between rows. */
.row.gfx {
  font-size: var(--fs-cell);
  transform: scaleX(var(--stretch));
}

/* Double-height rows sit over the row beneath them, as on real teletext. */
.row.dh {
  transform: scaleX(var(--stretch-text)) scaleY(2);
  z-index: 2;
}
.row.gfx.dh {
  transform: scaleX(var(--stretch)) scaleY(2);
}

/* Rows painting in one after another as the page loads. */
.row.pending { visibility: hidden; }

/* Spans are boxed to the full cell height so a coloured background fills
   its cells edge to edge, the way teletext paper colour does, even though
   the text inside sits smaller than the cell. */
.row > span {
  display: inline-block;
  vertical-align: top;
  height: var(--cell-h);
  line-height: var(--cell-h);
}

/* The host. Drawn as flat mosaic blocks over the character grid rather
   than as glyphs, so he lines up with the cells without depending on any
   particular font having block characters. */
#host {
  position: absolute;
  display: none;
  pointer-events: none;
  z-index: 4;
}
#host svg {
  display: block;
  width: 100%;
  height: 100%;
}

/* Foreground colours */
.fg-k { color: var(--tt-black); }
.fg-r { color: var(--tt-red); }
.fg-g { color: var(--tt-green); }
.fg-y { color: var(--tt-yellow); }
.fg-b { color: var(--tt-blue); }
.fg-m { color: var(--tt-magenta); }
.fg-c { color: var(--tt-cyan); }
.fg-w { color: var(--tt-white); }

/* Background colours */
.bg-k { background: var(--tt-black); }
.bg-r { background: var(--tt-red); }
.bg-g { background: var(--tt-green); }
.bg-y { background: var(--tt-yellow); }
.bg-b { background: var(--tt-blue); }
.bg-m { background: var(--tt-magenta); }
.bg-c { background: var(--tt-cyan); }
.bg-w { background: var(--tt-white); }

/* Selectable rows: answers and menu items. */
.row.hot {
  cursor: pointer;
  z-index: 3;
}

/* Rows are one character cell tall, which is a mean target on a phone.
   Extend the hit area into the blank rows either side without moving
   anything on screen. */
.row.hot::before {
  content: "";
  position: absolute;
  left: 0;
  top: calc(var(--cell-h) * -0.45);
  width: 100%;
  height: calc(var(--cell-h) * 1.9);
}
.row.hot:focus-visible {
  outline: 2px solid var(--tt-white);
  outline-offset: -2px;
}
.hotspot { cursor: pointer; }

.flash { animation: tt-flash .55s steps(1, end) infinite; }
@keyframes tt-flash { 50% { opacity: 0; } }

/* Scanlines and a faint phosphor bloom. Cosmetic only. */
#crt {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    repeating-linear-gradient(180deg,
      rgba(0, 0, 0, .28) 0, rgba(0, 0, 0, .28) 1px,
      rgba(0, 0, 0, 0) 1px, rgba(0, 0, 0, 0) 3px);
  mix-blend-mode: multiply;
}
#frame::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 45%, rgba(255, 255, 255, .05), rgba(0, 0, 0, .35) 78%);
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

@media (prefers-reduced-motion: reduce) {
  .flash { animation: none; }
  #crt { display: none; }
}
