/* STEALTHCOPTER - the shell.

   The room is a painting (img/room.webp) with a hole in it where the monitor's
   screen should be. The live 256x192 canvas is parked behind that hole, so the
   game appears to be playing on the set. Everything is positioned by main.js
   from the fractions in js/art.js, so the picture and the PLAY key stay
   registered to the artwork at any window size.

   --u is one hundredth of the picture width, so the tube dressing (scanlines,
   glare, border bloom) scales with the picture. */

:root {
  --ink: #d7d7d7;
  --cyan: #00d7d7;
  --yellow: #d7d700;
  --red: #d70000;
  --bg: #050507;
  --u: 6px;               /* picture width / 100, updated by layout() */
  --ovs: #000;            /* Spectrum border colour, updated by Shell.border() */
  --tmul: 1;              /* animation stretch: 1 = production, 5 = debug slow-mo */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: "Courier New", ui-monospace, monospace;
  overflow: hidden;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

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

/* --------------------------------------------------------------- the set */
/* #stage holds the painting, the tube and the PLAY key. Pushing the camera in
   is a single transform on this element, so everything moves together. */
.stage {
  position: fixed;
  inset: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.scene {
  position: absolute;
  inset: 0;
  background: var(--scene, none) center center / cover no-repeat;
  background-color: #050507;
  transition: opacity calc(.9s * var(--tmul)) ease-in calc(.6s * var(--tmul)),
              filter calc(.9s * var(--tmul)) ease-in calc(.6s * var(--tmul));
}
.stage-tv .scene { opacity: 0; }

/* the picture, sat behind the hole in the painting */
.tube {
  position: absolute;
  left: 0; top: 0;
  width: 10px; height: 10px;      /* real numbers come from layout() */
  background: #000;
}

/* the Spectrum border. In the room it stays black - the painted bezel is the
   frame - and it only blooms once the picture fills the window. */
.overscan {
  position: absolute;
  inset: 0;
  background-color: var(--ovs);
  overflow: hidden;
  transition: background-color .12s linear, box-shadow .12s linear, padding .3s linear;
}
.stage-tv .overscan {
  box-shadow: 0 0 calc(var(--u) * 3.5) var(--ovs), 0 0 calc(var(--u) * 8) var(--ovs);
}
.overscan.tape {
  padding: calc(var(--u) * 1.4) calc(var(--u) * 1.8);
  background-image: repeating-linear-gradient(
    180deg,
    #d70000 0 4px, #00d7d7 4px 8px, #d7d700 8px 12px,
    #0000d7 12px 16px, #00d700 16px 20px, #d700d7 20px 24px);
  animation: tapescroll calc(.5s * var(--tmul)) linear infinite;
}
@keyframes tapescroll { to { background-position: 0 -24px; } }

canvas#scr {
  display: block;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
  image-rendering: crisp-edges;
  background: #000;
}

/* scanlines + phosphor flicker, scaled to the picture */
.crt {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0,0,0,0) 0,
    rgba(0,0,0,0) calc(var(--u) * .5),
    rgba(0,0,0,.26) calc(var(--u) * .5),
    rgba(0,0,0,.26) calc(var(--u) * .82));
  mix-blend-mode: multiply;
  animation: flicker calc(5.5s * var(--tmul)) infinite steps(60);
}

/* glass: corner darkening, a soft reflection, and the glow of a live tube */
.glare {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse at 26% 14%, rgba(255,255,255,.10) 0%, rgba(255,255,255,0) 44%),
    radial-gradient(ellipse at 50% 50%, rgba(0,0,0,0) 55%, rgba(0,0,0,.5) 100%);
  transition: box-shadow calc(.5s * var(--tmul)) ease-out;
}
/* the tube coming on: light spills out of the glass and onto the room */
.stage-warm .glare {
  box-shadow: 0 0 calc(var(--u) * 6) rgba(150, 200, 255, .5),
              0 0 calc(var(--u) * 18) rgba(90, 150, 255, .35);
}

@keyframes flicker {
  0%, 97%, 100% { opacity: 1; }
  98% { opacity: .86; }
  99% { opacity: .95; }
}

/* ------------------------------------------------------- the PLAY key */
/* An invisible hit area over the PLAY key painted on the data recorder, with a
   slight glow so it is obvious what to press. */
.playkey {
  position: absolute;
  left: 0; top: 0;
  width: 10px; height: 10px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  z-index: 5;
}
/* Just the glow - no lit key top, no outline round the key - and 30% softer
   than it was, so it reads as a hint rather than a light show. */
.playkey-glow {
  position: absolute;
  inset: -55% -70%;
  border-radius: 50%;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(120, 255, 255, .21) 0%,
    rgba(0, 220, 255, .112) 34%,
    rgba(0, 180, 255, 0) 70%);
  animation: keyglow calc(2.1s * var(--tmul)) ease-in-out infinite;
}
.playkey:hover .playkey-glow { animation-duration: calc(.9s * var(--tmul)); }
/* keyboard focus lifts the glow instead of drawing a box round the key */
.playkey:focus-visible { outline: none; }
.playkey:focus-visible .playkey-glow { animation-duration: calc(.9s * var(--tmul)); filter: brightness(1.6); }
.playkey.down .playkey-glow { animation: none; opacity: .1; transform: scale(.9); }
.stage-tv .playkey { opacity: 0; pointer-events: none; transition: opacity .3s linear; }

@keyframes keyglow {
  0%, 100% { opacity: .32; transform: scale(.94); }
  50%      { opacity: .7;  transform: scale(1.06); }
}

/* ------------------------------------------------------------ the prompt */
.tape-hint {
  position: fixed;
  left: 0; right: 0;
  bottom: 2.5%;
  margin: 0;
  text-align: center;
  font-size: 13px;
  letter-spacing: 3px;
  color: var(--cyan);
  text-shadow: 0 0 12px rgba(0, 215, 215, .5);
  z-index: 25;
  transition: opacity .4s linear;
}
.cursor { color: var(--yellow); animation: blink calc(1s * var(--tmul)) steps(2) infinite; }
@keyframes blink { 50% { opacity: 0; } }
.stage-tv .tape-hint { opacity: 0; pointer-events: none; }

/* --------------------------------------------------------------- chrome */
.chrome {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 20;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: 8px;
  opacity: 0;
  pointer-events: none;
  transition: opacity calc(.6s * var(--tmul)) linear calc(1s * var(--tmul));
}
.stage-tv .chrome { opacity: 1; pointer-events: auto; }

/* ------------------------------------------------------------- overlays */
.overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,.92);
  text-align: center;
  padding: 16px;
}
.overlay[hidden] { display: none; }

.boot-inner { max-width: 560px; }
.boot-logo {
  font-size: clamp(26px, 7vw, 52px);
  font-weight: bold;
  letter-spacing: 3px;
  color: var(--yellow);
  text-shadow: 3px 3px 0 var(--red), 6px 6px 0 rgba(0,0,0,.6);
}
.boot-logo.small { font-size: clamp(20px, 5vw, 34px); }
.boot-msg { color: var(--ink); font-size: 14px; margin: 22px 0 14px; }

/* ------------------------------------------------------- touch controls */
.pad {
  position: fixed;
  inset: auto 0 0 0;
  height: 44vh;
  max-height: 260px;
  z-index: 30;
  pointer-events: none;
}
.pad[hidden] { display: none; }

.stick {
  position: absolute;
  left: 3vw;
  bottom: 3vh;
  width: 33vw;
  max-width: 190px;
  aspect-ratio: 1;
  border: 2px solid rgba(0, 215, 215, .5);
  border-radius: 50%;
  background: rgba(0, 40, 60, .25);
  pointer-events: auto;
  display: grid;
  place-items: center;
  touch-action: none;
}
.stick span {
  font-size: 11px;
  letter-spacing: 2px;
  color: rgba(0, 215, 215, .55);
}
.nub {
  position: absolute;
  width: 34%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: rgba(0, 215, 215, .35);
  border: 2px solid rgba(215, 215, 215, .6);
  transform: translate(0, 0);
  pointer-events: none;
}

.buttons {
  position: absolute;
  right: 3vw;
  bottom: 3vh;
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 10px;
  align-items: end;
  pointer-events: auto;
}
.tbtn {
  font-family: inherit;
  font-weight: bold;
  font-size: 13px;
  letter-spacing: 1px;
  width: 19vw;
  max-width: 96px;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 3px solid rgba(255,255,255,.65);
  color: #000;
  touch-action: none;
  cursor: pointer;
}
.tbtn.gun   { background: rgba(215, 215, 0, .78); }
.tbtn.msl   { background: rgba(215, 0, 0, .78); color: #fff; }
.tbtn.cloak { background: rgba(0, 215, 215, .78); grid-column: 1 / span 2; width: 100%; aspect-ratio: 3 / 1; border-radius: 26px; }
.tbtn:active { filter: brightness(1.5); transform: scale(.96); }

/* ------------------------------------------------------------- toolbar */
.toolbar { display: flex; gap: 8px; }
.tool {
  font-family: inherit;
  font-size: 11px;
  letter-spacing: 1px;
  padding: 6px 10px;
  color: var(--ink);
  background: #17171c;
  border: 1px solid #3a3a44;
  border-radius: 3px;
  cursor: pointer;
}
.tool:hover { border-color: var(--cyan); color: #fff; }
.tool.off { opacity: .45; }

.credits {
  margin: 0;
  font-size: 11px;
  color: #6f6f78;
  text-align: center;
  max-width: 90vw;
}

/* ------------------------------------------------- the tape deck's lamp */
/* The lamp is painted lit in the artwork, so "off" is a disc of deck plastic
   sat on top of it. Pressing PLAY fades that cap away and lights a red bloom
   over the top, which then breathes very slightly as the tape runs. */
.led {
  position: absolute;
  left: 0; top: 0;
  width: 10px; height: 10px;     /* real numbers come from layout() */
  pointer-events: none;
  z-index: 4;
}
.led::before {                    /* the cap that hides the painted lamp */
  content: "";
  position: absolute;
  inset: 28%;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 45%, #2a2018 0%, #171210 62%, rgba(20,16,14,0) 100%);
  opacity: 1;
  transition: opacity calc(.25s * var(--tmul)) linear;
}
.led::after {                     /* the lamp actually alight */
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%,
    rgba(255, 90, 70, .95) 0%,
    rgba(255, 30, 20, .55) 22%,
    rgba(220, 0, 0, .22) 46%,
    rgba(200, 0, 0, 0) 74%);
  opacity: 0;
  transition: opacity calc(.35s * var(--tmul)) ease-out;
}
.stage-tv .led, .stage-tv .spool { opacity: 0; transition: opacity calc(.3s * var(--tmul)) linear; }
.led.on::before { opacity: 0; }
.led.on::after { opacity: 1; animation: ledbreathe calc(3.4s * var(--tmul)) ease-in-out infinite; }
@keyframes ledbreathe { 0%, 100% { opacity: .88; } 50% { opacity: 1; } }

/* ---------------------------------------------- the cassette spools (debug) */
/* Experimental: a circular crop of the artwork over each spool, spun in place.
   layout() lines the background up with the painting. Off unless .spin. */
.spool {
  --sq: .55;                       /* the tape lies at an angle: rx to ry */
  position: absolute;
  left: 0; top: 0;
  width: 10px; height: 10px;       /* real numbers come from layout() */
  pointer-events: none;
  opacity: 0;
  z-index: 3;
  overflow: hidden;
}
.spool.spin { opacity: 1; }
.spool-face {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  animation: spool calc(2.4s * var(--tmul)) linear infinite;
  /* unsquash, turn, squash again - so the ellipse spins in its own plane */
  transform: scaleY(var(--sq)) rotate(0deg) scaleY(calc(1 / var(--sq)));
  -webkit-mask-image: radial-gradient(ellipse at 50% 50%, #000 58%, rgba(0,0,0,0) 74%);
  mask-image: radial-gradient(ellipse at 50% 50%, #000 58%, rgba(0,0,0,0) 74%);
}
@keyframes spool {
  from { transform: scaleY(var(--sq)) rotate(0deg) scaleY(calc(1 / var(--sq))); }
  to   { transform: scaleY(var(--sq)) rotate(360deg) scaleY(calc(1 / var(--sq))); }
}

/* ------------------------------------------------------- the debug menu */
.debug {
  position: fixed;
  left: 10px; bottom: 10px;
  z-index: 60;
  width: 300px;
  padding: 6px 8px 8px;
  background: rgba(10, 12, 16, .93);
  border: 1px solid #2f6f74;
  border-radius: 4px;
  box-shadow: 0 6px 22px rgba(0, 0, 0, .6);
  font-size: 10px;
  letter-spacing: .5px;
  color: #9fe8e8;
}
.debug[hidden] { display: none; }
.debug-bar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 5px;
  color: var(--cyan);
  font-weight: bold;
  letter-spacing: 2px;
}
.debug-bar button {
  background: none; border: 0; color: #7fd; font: inherit;
  font-size: 14px; line-height: 1; cursor: pointer; padding: 0 2px;
}
.debug-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 3px;
  margin-bottom: 3px;
}
.debug-lab { width: 38px; color: #5b8f93; }
.debug button[data-dbg] {
  font-family: inherit;
  font-size: 10px;
  letter-spacing: .5px;
  padding: 3px 5px;
  color: #cfefef;
  background: #14202a;
  border: 1px solid #2d4a52;
  border-radius: 2px;
  cursor: pointer;
}
.debug button[data-dbg]:hover { border-color: var(--cyan); color: #fff; }
.debug button[data-dbg].on { background: #0b4a4a; border-color: var(--cyan); color: #fff; }
.debug-tip { margin: 4px 0 0; color: #4d7377; font-size: 9px; line-height: 1.35; }

/* If the artwork cannot be loaded at all, fall back to a plain dark room with
   a real button, so the game is still playable. */
body.no-art .scene { background: radial-gradient(ellipse at 50% 120%, #1b1d26 0%, #050507 70%); }
body.no-art .playkey {
  width: 340px !important; height: 48px !important;
  left: 50% !important; margin-left: -170px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--cyan);
  border-radius: 4px;
  color: var(--cyan);
  font: inherit;
  font-size: 13px;
  letter-spacing: 2px;
  text-transform: uppercase;
  white-space: nowrap;
}
body.no-art .playkey .visually-hidden {
  position: static; width: auto; height: auto; margin: 0;
  clip-path: none; overflow: visible;
}

/* hide chrome when the screen is short (phones in landscape / fullscreen) */
@media (max-height: 520px), (display-mode: fullscreen) {
  .credits { display: none; }
  .chrome { padding: 4px; }
  .stage-tv .chrome { opacity: .5; }
  .tape-hint { bottom: 1%; font-size: 11px; }
}

@media (prefers-reduced-motion: reduce) {
  .stage, .scene, .chrome, .overscan { transition-duration: .01s !important; }
  .playkey-glow, .crt { animation: none !important; }
}
