/* jeremiehabel.ca -- JHA-TERM. Green phosphor CRT. No external assets. */

:root {
  --bg:        #04150a;
  --bg2:       #0a2a14;
  --fg:        #3dff7a;
  --fg-dim:    #1f9b47;
  --fg-bright: #ccffdc;
  --mono: "Cascadia Mono", "SF Mono", ui-monospace, "DejaVu Sans Mono",
          Consolas, "Liberation Mono", monospace;
  --glow: 0 0 2px currentColor, 0 0 9px rgba(61, 255, 122, .40);
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; height: 100%; }

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--mono);
  font-size: clamp(12px, 2.6vw, 16px);
  line-height: 1.45;
  overflow: hidden;
  -webkit-text-size-adjust: 100%;
}

::selection { background: var(--fg); color: var(--bg); }

/* ---------- shell ---------- */

.crt {
  position: fixed;
  inset: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  padding: max(12px, 2vmin) max(12px, 2.4vmin) max(10px, 1.6vmin);
  background:
    radial-gradient(ellipse 120% 100% at 50% 45%, var(--bg2) 0%, var(--bg) 78%);
  text-shadow: var(--glow);
  overflow: hidden;
}

.statusbar {
  display: flex;
  align-items: center;
  gap: .7em;
  flex: 0 0 auto;
  color: var(--fg-dim);
  font-size: .78em;
  letter-spacing: .04em;
  white-space: nowrap;
  padding-bottom: .45em;
  margin-bottom: .6em;
  border-bottom: 1px solid rgba(31, 155, 71, .45);
}
.statusbar .rule {
  flex: 1 1 auto;
  min-width: 1em;
  height: 0;
  border-bottom: 1px dashed currentColor;
  opacity: .45;
}
.statusbar .rule.short { flex: 0 1 4em; }
.statusbar .state { color: var(--fg); }

/* The one real control on the page. Dressed as another status field so it
   reads as part of the bar rather than as a web button bolted onto a CRT. */
.statusbar .snd {
  flex: 0 0 auto;
  appearance: none;
  -webkit-appearance: none;
  margin: 0;
  padding: 0 .5ch;
  border: 1px solid rgba(31, 155, 71, .55);
  border-radius: 0;
  background: transparent;
  color: var(--fg);
  font: inherit;
  letter-spacing: inherit;
  text-shadow: inherit;
  line-height: 1.3;
  cursor: pointer;
}
/* Status-bar type is small on purpose, which makes a fine label and a hopeless
   thumb target -- and on a phone this is the only way to mute, competing with
   a tap-anywhere handler that summons the keyboard. So the hit area grows well
   past the box, upwards into the bar's own padding where it cannot steal a tap
   meant for the screen below. */
.statusbar .snd { position: relative; }
.statusbar .snd::after {
  content: '';
  position: absolute;
  inset: -10px -12px -8px;
}
@media (hover: none) {
  .statusbar .snd::after { inset: -18px -16px -12px; }
}

.statusbar .snd:hover  { background: rgba(61, 255, 122, .12); }
.statusbar .snd:active { background: var(--fg); color: var(--bg); text-shadow: none; }
.statusbar .snd.muted  { color: var(--fg-dim); border-color: rgba(31, 155, 71, .28); }
.statusbar .snd:focus-visible { outline: 1px solid var(--fg-bright); outline-offset: 1px; }
.statusbar .snd[hidden] { display: none; }

.screen {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--fg-dim) transparent;
}
.screen::-webkit-scrollbar { width: 6px; }
.screen::-webkit-scrollbar-thumb { background: var(--fg-dim); }

/* ---------- text ---------- */

.line {
  white-space: pre-wrap;
  overflow-wrap: break-word;
  min-height: 1.45em;
}
.line.dim     { color: var(--fg-dim); }
.line.bright  { color: var(--fg-bright); }
.line.echo    { color: var(--fg-bright); }
.line u       { text-decoration: underline; text-underline-offset: .18em; }

.line.inverse {
  display: block;
  width: fit-content;
  background: var(--fg);
  color: var(--bg);
  text-shadow: none;
  padding: 0 .6ch;
}

.banner {
  margin: 0;
  font-family: inherit;
  font-size: clamp(6px, 3.2vw, 17px);
  line-height: 1.02;
  white-space: pre;
  color: var(--fg);
  text-shadow: 0 0 3px currentColor, 0 0 14px rgba(61, 255, 122, .5);
}

.promptline {
  display: flex;
  align-items: flex-start;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  margin-top: .3em;
}
.promptline[hidden] { display: none; }
.promptline .sigil  { color: var(--fg-dim); flex: 0 0 auto; }
.promptline .typed  { color: var(--fg-bright); }
.cursor { animation: blink .9s steps(1, end) infinite; }
@keyframes blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }

/* ---------- hidden input (drives the mobile keyboard) ---------- */

#captureform {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

#capture {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
  font-size: 16px; /* < 16px makes iOS zoom the page on focus */
  background: transparent;
  color: transparent;
  caret-color: transparent;
}

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

/* ---------- pre-launch gate ---------- */

/* .output is the centring column, rather than .screen becoming a flex row.
   Both lay out correctly, but a flex item defaults to min-width:auto, so a row
   holding 1488px-wide artwork is one stylesheet edit away from inflating past
   its container and making max-width:100% resolve against the wrong number.
   Keeping the wide child out of a flex row removes that trap entirely. */
.screen.gating .output {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100%;
}

.gate {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: 0;
  max-width: 100%;
  gap: clamp(16px, 3.4vh, 34px);
  padding: clamp(6px, 2vh, 22px) 0;
  transition: opacity .45s linear, filter .45s linear;
}
.gate.opening { opacity: 0; filter: brightness(2.8); }

.gate-art {
  display: block;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 62vh;
  max-height: 62dvh;
  /* The glow is deliberately NOT baked into the file: the screen casts it live,
     which keeps the PNG at 46 KB and free of gradient banding. */
  filter:
    drop-shadow(0 0 3px rgba(61, 255, 122, .95))
    drop-shadow(0 0 14px rgba(61, 255, 122, .55))
    drop-shadow(0 0 40px rgba(61, 255, 122, .26));
}

.gate-msg {
  color: var(--fg-bright);
  letter-spacing: .22em;
  text-indent: .22em;
  text-align: center;
}
.gate-timer {
  letter-spacing: .12em;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ---------- CRT effects ---------- */

.fx { position: fixed; left: 0; right: 0; pointer-events: none; }

.scanlines {
  top: 0; bottom: 0;
  z-index: 6;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, .30) 0 1px,
    rgba(0, 0, 0, 0)   1px 3px);
}

/* the interlacing bar sweeps bottom -> top */
.interlace {
  top: 0;
  height: 14vh;
  z-index: 7;
  background: linear-gradient(to bottom,
    rgba(190, 255, 210, 0)    0%,
    rgba(190, 255, 210, .045) 42%,
    rgba(190, 255, 210, .085) 50%,
    rgba(190, 255, 210, .045) 58%,
    rgba(190, 255, 210, 0)    100%);
  animation: sweep 7.5s linear infinite;
}
.interlace.slow {
  height: 26vh;
  opacity: .5;
  animation-duration: 13s;
  animation-delay: -4s;
}
@keyframes sweep {
  from { transform: translate3d(0, 105vh, 0); }
  to   { transform: translate3d(0, -30vh, 0); }
}

.flicker {
  top: 0; bottom: 0;
  z-index: 8;
  background: var(--fg);
  opacity: .012;
  animation: flick .09s steps(2, end) infinite;
}
@keyframes flick { 0% { opacity: .008; } 50% { opacity: .022; } 100% { opacity: .012; } }

.vignette {
  top: 0; bottom: 0;
  z-index: 9;
  background: radial-gradient(ellipse at center,
    rgba(0, 0, 0, 0) 50%, rgba(0, 0, 0, .58) 100%);
  box-shadow: inset 0 0 13vmin rgba(0, 0, 0, .75);
}

.invert {
  top: 0; bottom: 0;
  z-index: 10;
  background: var(--fg);
  opacity: 0;
  transition: opacity .07s linear;
}
.invert.on { opacity: .82; }

.crt.shake { animation: shake .34s linear; }
@keyframes shake {
  0%   { transform: translate(0, 0); }
  15%  { transform: translate(-3px, 2px); }
  30%  { transform: translate(3px, -2px); }
  45%  { transform: translate(-2px, -1px); }
  60%  { transform: translate(2px, 2px); }
  75%  { transform: translate(-1px, 1px); }
  100% { transform: translate(0, 0); }
}

/* ---------- narrow screens ---------- */

@media (max-width: 480px) {
  .statusbar .ver, .statusbar .rule.short { display: none; }
}

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

@media (prefers-reduced-motion: reduce) {
  .interlace, .flicker, .cursor, .crt.shake { animation: none; }
  .gate { transition: none; }
  .interlace { opacity: .05; transform: translate3d(0, 40vh, 0); }
  .interlace.slow { display: none; }
  .invert { transition: none; }
}
