/* ==========================================================
   Anatomy of a Murmur — v2 "Xenon" styles
   Bitmap Brothers flavour on the retro block look: chrome
   bevelled bricks, riveted metal panels, a starfield, and a
   ship that shoots the title. The loader is shared with v1.
   ========================================================== */

:root {
  --space: #04030c;
  --fg: #e8ecf8;
  --dim: #7d86a3;

  --steel-hi: #9aa6c4;
  --steel: #5b6580;
  --steel-lo: #1b2030;

  --gold: #ffd34a;
  --cyan: #7df3ff;
  --silver: #c9d2e6;
  --magenta: #ff7ad9;
  --lime: #9dff6a;

  /* Chrome text fills: sky on top, hard horizon line, warm ground below */
  --text-steel: linear-gradient(180deg, #ffffff 0%, #b9c3da 48%, #5d6782 52%, #d5dcee 100%);
  --text-gold: linear-gradient(180deg, #fff6c0 0%, #ffd34a 46%, #a8480f 52%, #ffb52e 100%);
  --text-cyan: linear-gradient(180deg, #f0feff 0%, #7df3ff 46%, #0b62c9 52%, #6fe3ff 100%);

  --font: "Silkscreen", ui-monospace, "Courier New", monospace;
  --font-display: "Press Start 2P", var(--font);
  --text: clamp(12px, 1.9vw, 18px);
  --small: clamp(9px, 1.3vw, 12px);

  --rivet: radial-gradient(circle at 45% 40%, #f4f7ff 0 1px, #8f9ab5 1.6px 2.6px, #0a0c14 3px 3.6px, transparent 4px);
  --arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 6 7' shape-rendering='crispEdges'%3E%3Cpath fill='%237df3ff' d='M0 0h2v1h2v1h2v3h-2v1h-2v1h-2z'/%3E%3Cpath fill='%23fff' d='M1 1h1v1h2v1h-3z'/%3E%3C/svg%3E");
}

*,
*::before,
*::after { box-sizing: border-box; }

html {
  color-scheme: dark;
  background: var(--space);
  /* iPhone: a quick second tap fires the laser, it is not a request to zoom in */
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
}

/* Taps are taps: no double-tap zoom anywhere (pinch zoom still works), and no grey flash */
body,
body * { touch-action: manipulation; }
iframe { touch-action: auto; } /* leave the video player its own gestures */
* { -webkit-tap-highlight-color: transparent; }

body {
  margin: 0;
  min-height: 100vh;
  min-height: 100svh;
  background:
    radial-gradient(ellipse 70% 50% at 15% 0%, rgb(76 36 140 / .45), transparent 70%),
    radial-gradient(ellipse 60% 50% at 90% 100%, rgb(18 84 150 / .4), transparent 70%),
    var(--space);
  background-attachment: fixed;
  color: var(--fg);
  font-family: var(--font);
  font-size: var(--text);
  line-height: 1.5;
  letter-spacing: .12em;
  text-transform: uppercase;
  -webkit-font-smoothing: none;
  overflow-x: hidden;
}

a { color: inherit; }

button {
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

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

.skip {
  position: absolute;
  top: -4rem;
  left: .5rem;
  z-index: 10;
  padding: .4em .7em;
  background: var(--gold);
  color: #000;
  text-decoration: none;
}
.skip:focus { top: .5rem; }

/* Panels set their own display; hidden must still win. */
[hidden] { display: none !important; }

/* ---------- Layers: stars < content < laser fx < loader < CRT ---------- */
.sky,
.stars,
.fx {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.stars { z-index: 0; image-rendering: pixelated; }
.sky { z-index: 0; } /* the gas clouds: full resolution and smooth, so they never band into blocks */
main { position: relative; z-index: 1; }

/* ---------- About: our ship flies past, afterburners blazing ---------- */
/* Its own layer above the stars; it comes before <main> in the page, so the About text stays on top. */
.flyby {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}
.flyby.is-over { z-index: 3; } /* this pass crosses in front of the text */
.flyby-craft { position: absolute; top: 0; left: 0; }
.flyby-body {
  position: absolute;
  left: -6.5em;
  top: -6.8em;
  width: 13em;  /* the sprite is drawn 13em wide (21 x 22 pixels), so the engine effects keep their size */
  height: 13.62em;
  animation: flyby-wobble 1.8s ease-in-out infinite;
}
.flyby-body svg {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  filter: drop-shadow(0 0 .6em rgb(125 243 255 / .25));
}
@keyframes flyby-wobble { 50% { rotate: 2.5deg; } }

/* The engines sit where the sprite draws its flames; the script sets --x for each one */
.flyby-burner,
.flyby-ember { left: var(--x, 50%); }

/* Afterburners: white-hot cores that flicker, a pulsing orange glow, a fading exhaust trail and embers */
.flyby-burner {
  position: absolute;
  top: 12.6em;
  width: 2.4em;
  height: 6.5em;
  translate: -50% 0;
  transform-origin: 50% 0;
  /* a pixel-stepped teardrop outline with a smooth colour fade inside */
  clip-path: polygon(10% 0, 90% 0, 90% 22%, 76% 22%, 76% 45%, 62% 45%, 62% 68%, 54% 68%, 54% 88%, 46% 88%, 46% 68%, 38% 68%, 38% 45%, 24% 45%, 24% 22%, 10% 22%);
  background: linear-gradient(#fff 0, #fff2b0 18%, #ffb13a 42%, #ff7a22 68%, #e8481a 100%);
  animation: burner-flicker .09s steps(2) infinite alternate;
}
@keyframes burner-flicker {
  from { scale: 1 .8; opacity: .85; }
  to { scale: 1.12 1.18; opacity: 1; }
}
.flyby-glow {
  position: absolute;
  left: 50%;
  top: 13em;
  width: 17em;
  height: 13em;
  translate: -50% -40%;
  background: radial-gradient(closest-side, rgb(255 150 60 / .55), rgb(255 90 30 / .2) 55%, transparent);
  animation: glow-pulse .14s ease-in-out infinite alternate;
}
@keyframes glow-pulse { to { opacity: .7; scale: .9; } }
.flyby-trail {
  position: absolute;
  left: 50%;
  top: 13.5em;
  width: 5em;
  height: 22em;
  translate: -50% 0;
  border-radius: 50%;
  background: linear-gradient(rgb(255 200 110 / .55), rgb(255 120 40 / .22) 35%, transparent 85%);
  filter: blur(.25em);
}
/* Warp speed: the engines open up while the ship holds in frame */
.flyby-burner,
.flyby-trail { transition: height .7s ease, width .7s ease; }
.flyby-craft.is-warp .flyby-burner { width: 2.9em; height: 11em; }
.flyby-craft.is-warp .flyby-trail { width: 6.5em; height: 34em; }
.flyby-craft.is-warp .flyby-body {
  animation: flyby-wobble 1.8s ease-in-out infinite, warp-shake .09s steps(2) infinite alternate;
}
@keyframes warp-shake {
  from { translate: -.06em 0; }
  to { translate: .06em .05em; }
}
.flyby-ember {
  position: absolute;
  top: 13.6em;
  width: .7em;
  height: .7em;
  background: #ffd34a;
  box-shadow: 0 0 .5em #ff8a1a;
  animation: ember .55s linear infinite;
}
.flyby-ember:nth-of-type(even) { background: #ff8a1a; }
@keyframes ember {
  from { translate: -50% 0; opacity: 1; }
  to { translate: calc(-50% + var(--drift, 0em)) 9em; opacity: 0; }
}
.fx { z-index: 2; }

/* Stars, effects and page content: zooms as one when the showreel takes over the screen */
.world {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
}
html.reel-zoomed { overflow: hidden; }
/* As the page zooms in, everything outside the video fades to black: the world falls away.
   The shade is a separate layer that never zooms (so it always covers the window edge to edge);
   the script keeps its see-through middle on the video and sets the fade timing. */
.reel-shade {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 3;
  width: 0;
  height: 0;
  pointer-events: none;
  box-shadow: 0 0 0 200vmax #000;
  opacity: 0;
}
.reel-shade.is-on { opacity: 1; }
/* While zoomed, a see-through layer over the video catches the first mouse movement,
   which the embedded player can't pass on to the page. */
.world.is-zoomed .reel::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
}

.crt {
  position: fixed;
  inset: 0;
  z-index: 5;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, transparent 0 2px, rgb(0 0 0 / .18) 2px 3px);
}
/* The darkened corners are their own layer, so the loader screen can go without them */
.crt::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 60%, rgb(0 0 0 / .55) 100%);
}
html.is-loading .crt::after { opacity: 0; }

/* ---------- Gradient-filled pixel text with a hard shadow ---------- */
.menu a,
.start,
.back,
.warp,
.option-label,
.links a,
.links .plain,
.wip-go {
  font-family: var(--font-display);
  letter-spacing: .08em;
  background: var(--text-steel);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  filter: drop-shadow(2px 2px 0 #000);
}

/* ---------- Screens ---------- */
.screen {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  justify-content: safe center; /* too tall for the window: start at the top rather than crop it */
  gap: clamp(1rem, 3.5vh, 2.2rem);
  padding: clamp(3.5rem, 8vh, 5rem) 1rem clamp(1.5rem, 5vh, 3rem);
}

.js .screen:not(.is-active) { display: none; }


/* ---------- Chrome brick title ---------- */
.brick-title {
  --cell: 17px;
  margin: 0;
  width: min(92vw, calc(var(--cells, 40) * var(--cell)));
  line-height: 0;
  outline: none;
}
.brick-title--small { --cell: 13px; }

.brick-title svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  filter: drop-shadow(3px 4px 0 rgb(0 0 0 / .9)) drop-shadow(0 0 16px rgb(90 150 255 / .35));
}

.brick { transform-box: fill-box; transform-origin: center; }
.brick .hi { fill: rgb(255 255 255 / .8); }
.brick .lo { fill: rgb(6 8 24 / .6); }

/* The whole home screen is a firing range; buttons and links keep the pointer cursor. */
#home,
#home * { touch-action: none; } /* a finger here flies the ship: never a scroll, never a zoom */
#home {
  cursor: crosshair;
  -webkit-user-select: none;
          user-select: none;
}
#home :is(a, button) { cursor: pointer; }

/* Scatter mode: this canvas passes behind the menu panel, and its twin (.brick-over) in front of it, so
   some alien ships cross the box and others go behind. The glow filter is dropped while they move,
   because redrawing it every frame over the whole screen is slow (Safari especially). */
.brick-title--play.is-scattering {
  position: relative;
  z-index: 1;
}
/* The second brick canvas: same picture, stacked over the box. The script keeps it on top of the title. */
.brick-over {
  position: absolute;
  z-index: 3;
  display: block;
  overflow: visible;
  pointer-events: none; /* the menu underneath stays clickable; shots still find these ships */
}
.brick-title--play.is-scattering svg { filter: drop-shadow(0 0 0 transparent); }

/* steps(6) with the default end timing shows six positions but never the last one until the animation
   is over, so each brick jumped the final stretch. `start` lands it on the way instead. */
.intro .brick { animation: drop .5s steps(6, start) var(--d, 0s) both; }
.brick.hit { animation: zap .32s steps(4) forwards; pointer-events: none; }
.brick.respawn { animation: materialise .5s steps(5) both; }

@keyframes drop {
  from { transform: translateY(-60px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes zap {
  0%   { transform: scale(1.35); opacity: 1; }
  100% { transform: scale(0); opacity: 0; }
}
@keyframes materialise {
  from { transform: scale(0); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* ---------- HUD ---------- */
.hud {
  position: absolute;
  top: clamp(.9rem, 3vw, 1.6rem);
  left: 0;
  right: 0;
  z-index: 4; /* above flying bricks, so the high score prompt stays clickable */
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: .6em 1em;
  padding: 0 clamp(1rem, 4vw, 2.2rem);
  font-family: var(--font-display);
  font-size: var(--small);
  letter-spacing: .1em;
  color: var(--gold);
  text-shadow: 2px 2px 0 #000;
}
.hud b { font-weight: normal; color: var(--fg); }

/* "New high score! Add your name?" in the HUD for a few seconds, so the menu layout never jumps */
/* A small banner near the top of the window, the same on every screen. Tapping it still adds your name. */
.hud-prompt {
  position: fixed;
  top: clamp(.5rem, 2vh, 1rem);
  left: 50%;
  translate: -50% 0;
  z-index: 6;
  display: block;
  padding: .4em .8em .5em;
  font-size: var(--small);
  white-space: nowrap;
  color: var(--gold);
  background: rgb(4 3 12 / .85);
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--steel);
  animation: hud-flash .46s steps(1) infinite; /* an arcade flash, banner and all */
}
@keyframes hud-flash {
  50% {
    color: #04030c;
    background: var(--gold);
    box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--gold);
  }
}
/* A bar along the bottom runs down while the offer is up */
.hud-prompt:focus-visible { outline: none; }
.hud-prompt:is(:hover, :focus-visible) { box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--cyan); }

/* ---------- Riveted metal console ---------- */
.console {
  position: relative;
  z-index: 2; /* our ship and half the alien ships pass behind it */
  display: grid;
  justify-items: center;
  gap: clamp(.7rem, 2.2vh, 1.2rem);
  padding: clamp(1rem, 3vh, 1.6rem) clamp(1.5rem, 6vw, 3.5rem);
  background:
    var(--rivet) left 6px top 6px / 10px 10px no-repeat,
    var(--rivet) right 6px top 6px / 10px 10px no-repeat,
    var(--rivet) left 6px bottom 6px / 10px 10px no-repeat,
    var(--rivet) right 6px bottom 6px / 10px 10px no-repeat,
    repeating-linear-gradient(0deg, rgb(255 255 255 / .025) 0 1px, transparent 1px 3px),
    linear-gradient(180deg, #2b3349 0%, #161b2a 50%, #0b0e18 100%);
  box-shadow:
    inset 3px 3px 0 var(--steel-hi),
    inset -3px -3px 0 #05060b,
    0 0 0 2px #000,
    0 0 0 5px var(--steel),
    0 0 0 7px #000,
    0 14px 40px rgb(0 0 0 / .7);
}

/* ---------- Sound option bar ---------- */
.option {
  display: flex;
  align-items: center;
  gap: 1em;
  padding: .3em .2em;
  font-size: .8em;
}
.option .bar {
  width: clamp(28px, 7vw, 64px);
  height: .7em;
  background: linear-gradient(180deg, #d9fdff 0%, #36c8ff 45%, #0a4fc4 55%, #5fdcff 100%);
  box-shadow: 0 0 0 2px #000, 0 0 10px rgb(125 243 255 / .45);
}
.option[aria-pressed="false"] .bar {
  background: linear-gradient(180deg, #8f96a8 0%, #444a5c 50%, #1d2130 51%, #3b4152 100%);
  box-shadow: 0 0 0 2px #000;
}
.option:focus-visible { outline: none; }
.option:is(:hover, :focus-visible, .is-selected) .option-label { background-image: var(--text-gold); }

.options {
  display: grid;
  justify-items: center;
  gap: .15em;
}
/* Fixed width so the bars line up whatever the label says (SFX ON / MUSIC OFF). */
.option-label {
  display: inline-block;
  min-width: 10em;
  text-align: center;
}

/* ---------- Main menu ---------- */
.menu {
  display: grid;
  justify-items: center;
  gap: .6em;
  margin: 0;
  padding: 0;
  list-style: none;
}
.menu a {
  position: relative;
  display: block;
  padding: .15em .3em;
  text-decoration: none;
  outline: none;
}
.menu a.is-selected { background-image: var(--text-gold); }
.menu a.is-selected::before,
.menu a.is-selected::after {
  content: "";
  position: absolute;
  top: 50%;
  width: .85em;
  height: 1em;
  margin-top: -.5em;
  background: var(--arrow) center / contain no-repeat;
  image-rendering: pixelated;
  animation: nudge .5s steps(2) infinite;
}
.menu a.is-selected::before { left: -1.4em; }
.menu a.is-selected::after { right: -1.4em; transform: scaleX(-1); animation-name: nudge-back; }
.menu a.is-flashing { animation: flash .1s steps(1) 4; }
/* Syntheia stays greyed out until the game has been played for a while and the showreel watched */
.menu a.is-locked {
  background-image: linear-gradient(180deg, #8b90a0 0%, #5e6372 48%, #3a3e4a 52%, #6a6f7e 100%);
  opacity: .45;
}
#home .menu a.is-locked { cursor: not-allowed; }
.menu a.is-unlocking { animation: flash .12s steps(1) 10; }

@keyframes nudge { 50% { translate: .25em 0; } }
@keyframes nudge-back { 50% { translate: -.25em 0; } }
@keyframes flash { 50% { background-image: var(--text-cyan); } }

.start {
  padding: .3em .6em;
  background-image: var(--text-cyan);
  animation: blink 1.1s steps(1) infinite;
}
.start:focus-visible { outline: none; }
.start:is(:hover, :focus-visible) { background-image: var(--text-gold); animation: none; }

@keyframes blink { 60% { opacity: 0; } }

/* ---------- Ship ---------- */
.ship-lane {
  position: relative;
  z-index: 1; /* behind the menu box, whenever the box is showing */
  width: 100vw; /* the ship can fly the full width of the window */
  height: clamp(30px, 5.5vh, 48px);
}
.ship {
  position: absolute;
  left: 0;
  bottom: 0;
  width: clamp(35px, 4.6vw, 55px);
  font-size: calc(clamp(35px, 4.6vw, 55px) / 13); /* 1em is one sprite pixel, so the engines line up */
  transform: translate(var(--x, 0px), var(--y, 0px));
  transition: transform .2s cubic-bezier(.2, .8, .2, 1);
}
.ship svg {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(0 0 6px rgb(125 243 255 / .5));
  transition: rotate .12s steps(3);
}
/* Bank into the turn while following the mouse */
.ship[data-bank="left"] svg { rotate: -9deg; }
.ship[data-bank="right"] svg { rotate: 9deg; }
.ship .flame,
.loader-ship .flame,
.flyby-body .flame { animation: flicker .12s steps(2) infinite; }

/* Alien ships: each brick's ship colours are a layer over its chrome face (so they can fade back to chrome).
   Engine bricks flicker; only the layer animates, so hit and respawn animations still work. */
.ship-skin { pointer-events: none; }
.brick > rect.is-engine { animation: engine-flicker .16s steps(2) infinite; }
@keyframes engine-flicker { 50% { opacity: .7; } }
/* Hit by a bomb: gone for a moment, then blinking while it can't be hit */
.ship.is-down { opacity: 0; } /* the ship and its engines go together */
.ship.is-shielded { animation: blink .18s steps(1) infinite; } /* blinks whole, flames and all */

/* Lives: little ships next to the score */
.lives {
  display: inline-flex;
  gap: .35em;
  margin-left: .6em;
  vertical-align: middle;
}
.lives svg {
  display: block;
  width: 1.3em;
  height: auto;
}

/* GAME OVER across the middle of the menu */
.game-over {
  position: fixed;            /* centred in the window itself, on every screen */
  top: 50%;
  left: 0;
  right: 0;
  translate: 0 -50%;
  z-index: 6;
  margin: 0;
  padding: 0 1rem;
  font-family: var(--font-display);
  font-size: clamp(28px, 8.5vw, 96px); /* fits across a phone, big on a desktop */
  line-height: 1.2;
  text-wrap: balance;
  letter-spacing: .1em;
  text-align: center;
  background: var(--text-gold);
  -webkit-background-clip: text;
          background-clip: text;
  color: transparent;
  filter: drop-shadow(3px 3px 0 #000);
  pointer-events: none;
  animation: blink .6s steps(1) infinite;
}

@keyframes flicker { 50% { opacity: .3; } }

.hint,
.copyright {
  margin: 0;
  font-size: var(--small);
  color: var(--dim);
  text-align: center;
}
.copyright { margin-top: clamp(.25rem, 2vh, 1.25rem); }

/* Shorter windows (most laptops): tighter spacing so the whole menu screen, hint and copyright included, fits without scrolling. */
@media (max-height: 820px) {
  .screen { gap: clamp(.6rem, 2vh, 1rem); padding-top: 3rem; padding-bottom: 1rem; }
  .console { gap: .55rem; padding-block: .8rem; }
  .menu { gap: .35em; }
  .copyright { margin-top: 0; }
}

/* ---------- Syntheia: a modern terminal window over the frozen game ---------- */
/* Deliberately not retro: dark glass, a macOS-style title bar, smooth text, and above the CRT scanlines. */
.terminal {
  position: fixed;
  inset: 0;
  z-index: 6; /* above the game, the glitch tears and the CRT glass */
  display: grid;
  place-items: center;
  padding: 1rem;
  cursor: default;
  text-transform: none;
  letter-spacing: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.terminal-window {
  --term-green: #5af78e;
  display: flex;
  flex-direction: column;
  width: min(100%, 44rem);
  max-height: min(80vh, 32rem);
  overflow: hidden;
  border-radius: 12px;
  background: rgb(6 7 9 / .7);
  -webkit-backdrop-filter: blur(8px) saturate(1.4);
          backdrop-filter: blur(8px) saturate(1.4);
  box-shadow:
    inset 0 0 0 1px rgb(255 255 255 / .1),
    0 0 0 1px rgb(0 0 0 / .65),
    0 28px 80px rgb(0 0 0 / .6),
    0 8px 22px rgb(0 0 0 / .35);
  color: #e6e9ee;
  font: 400 clamp(12px, 1.35vw, 14px) / 1.65 "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  outline: none;
}

.terminal-bar {
  position: relative;
  display: flex;
  align-items: center;
  flex: none;
  height: 34px;
  padding: 0 14px;
  background: linear-gradient(rgb(255 255 255 / .07), rgb(255 255 255 / .035));
  border-bottom: 1px solid rgb(0 0 0 / .5);
  box-shadow: 0 1px 0 rgb(255 255 255 / .04);
  font: 500 12px / 1 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
  color: rgb(255 255 255 / .5);
  -webkit-user-select: none;
          user-select: none;
}
.terminal-lights { display: flex; gap: 8px; }
.terminal-light {
  display: grid;
  place-items: center;
  width: 12px;
  height: 12px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  box-shadow: inset 0 0 0 .5px rgb(0 0 0 / .3);
}
.terminal-light--close { background: #ff5f57; cursor: pointer; }
.terminal-light--min { background: #febc2e; }
.terminal-light--max { background: #28c840; }
.terminal-light svg {
  width: 8px;
  height: 8px;
  fill: none;
  stroke: rgb(90 0 0 / .85);
  stroke-width: 1.2;
  stroke-linecap: round;
  opacity: 0;
}
.terminal-lights:hover svg,
.terminal-light--close:focus-visible svg { opacity: 1; }
.terminal-light--close:focus-visible { outline: 2px solid rgb(255 255 255 / .55); outline-offset: 2px; }
.terminal-title {
  position: absolute;
  left: 50%;
  translate: -50% 0;
  white-space: nowrap;
}

.terminal-body {
  min-height: 15em; /* full size from the start, so the window doesn't grow as the session plays */
  padding: 12px 16px 16px;
  overflow: auto;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  scrollbar-width: thin;
  scrollbar-color: rgb(255 255 255 / .2) transparent;
}
.term-line { min-height: 1.65em; }
.term-out { color: var(--term-green); }
.term-line a { color: inherit; text-decoration: underline; cursor: pointer; }
.term-line a:hover, .term-line a:focus-visible { background: var(--term-green); color: #000; }
.term-hot { color: #ffe14d; font-weight: 700; text-shadow: 0 0 8px rgb(255 225 77 / .45); }
.term-hot::before, .term-hot::after { content: "***"; color: #ffe14d; animation: term-flash .8s steps(1) infinite; }
.term-hot::before { margin-right: .8em; }
.term-hot::after { margin-left: .8em; }
.term-hot a:hover, .term-hot a:focus-visible { background: #ffe14d; color: #000; }
@keyframes term-flash { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .term-hot::before, .term-hot::after { animation: none; } }
.term-dim { color: rgb(255 255 255 / .42); }
.term-user { color: var(--term-green); font-weight: 700; }
.term-path { color: #57c7ff; font-weight: 700; }
.term-sign { color: rgb(255 255 255 / .55); }
.term-cmd { color: #f4f6f8; }
.term-dump { white-space: pre; overflow: hidden; } /* hex rows never wrap, even in a narrow window */
.term-hex { color: var(--term-green); }
.terminal-cursor {
  display: inline-block;
  width: .62em;
  height: 1.25em;
  margin-left: 1px;
  vertical-align: -.28em;
  background: var(--term-green);
  opacity: .9;
  animation: term-blink 1.1s steps(1) infinite;
}
@keyframes term-blink { 55% { opacity: 0; } }
.terminal ::selection { background: rgb(90 247 142 / .3); }

/* The game under the terminal: jolts sideways in whole blocks while it glitches... */
.world.is-glitching {
  translate: var(--jx, 0) 0;
  filter: contrast(1.2) saturate(1.4);
}
/* ...every animation on it stops dead while it's frozen (including mid-stutter)... */
.world.is-frozen *,
.world.is-frozen *::before,
.world.is-frozen *::after { animation-play-state: paused !important; }
/* ...and once it has stopped, it dims like a paused game... */
.world.is-paused {
  filter: saturate(.55) brightness(.75);
  transition: filter .5s;
}
/* ...but stays broken while the terminal is up, with a jolt now and then (set by the script). */
.world.is-broken {
  translate: var(--jx, 0) 0;
  filter: saturate(.7) brightness(.72);
  transition: none;
}
/* Digital corruption over the game: a quarter-resolution canvas (so it's all chunky pixels) for pixel sorting
   and broken macroblocks, plus blocks that scramble the colours of whatever is under them */
.glitch {
  position: fixed;
  inset: 0;
  z-index: 3;
  overflow: hidden;
  pointer-events: none;
}
.glitch-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: crisp-edges;
  image-rendering: pixelated;
}

.touch-hint { display: none; }
@media (pointer: coarse) {
  .kbd-hint { display: none; }
  .touch-hint { display: inline; }
}

/* ---------- Section content ---------- */
.panel { width: min(100%, 44rem); }

.back,
.warp {
  padding: .3em .6em;
  text-decoration: none;
  outline: none;
}
:is(.back, .warp):is(:hover, :focus-visible) { background-image: var(--text-gold); }

/* High scores: initials entry beside the top 10 */
.hs-layout {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  width: min(100%, 56rem);
}
.hs-layout .panel {
  flex: 1 1 18rem;
  width: auto;
  max-width: 27rem;
}

.board-note {
  margin: 0;
  font-size: var(--small);
  color: var(--cyan);
  text-align: center;
}
.board-note:empty { display: none; }

.entry {
  gap: .7em;
  text-align: center;
}
.entry p { margin: 0; }
.entry-flash {
  font-family: var(--font-display);
  color: var(--gold);
  text-shadow: 2px 2px 0 #000;
  animation: blink .8s steps(1) infinite;
}
.entry-score {
  font-family: var(--font-display);
  font-size: 1.5em;
  text-shadow: 3px 3px 0 #000;
}
.entry-label { font-size: .85em; color: var(--dim); }
.entry-help { font-size: var(--small); color: var(--dim); }

.initials {
  position: relative;
  display: flex;
  gap: .5em;
}
/* The real input sits invisibly over the slots: typing, phone keyboards and screen readers all work. */
.initials input {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  font-size: 16px; /* stops iOS zooming in on focus */
  caret-color: transparent;
  cursor: pointer;
}
.slot {
  display: grid;
  place-items: center;
  width: 1.6em;
  height: 1.9em;
  font-family: var(--font-display);
  font-size: 2em;
  letter-spacing: 0;
  color: var(--cyan);
  text-shadow: 3px 3px 0 #000;
  background: #05060b;
  box-shadow: inset 0 -.18em 0 var(--steel), 0 0 0 2px #000;
}
.initials:focus-within .slot.is-active {
  box-shadow: inset 0 -.18em 0 var(--gold), 0 0 0 2px #000, 0 0 12px rgb(255 211 74 / .45);
  animation: slot-blink .8s steps(1) infinite;
}
@keyframes slot-blink {
  50% { box-shadow: inset 0 -.18em 0 transparent, 0 0 0 2px #000; }
}

.entry-ask,
.entry-name {
  display: grid;
  justify-items: center;
  gap: .7em;
}
.entry-question { font-size: .9em; }

.entry-actions {
  display: flex;
  gap: 1.25em;
}
/* Chunky bevelled pixel buttons: Yes / No / Del / OK */
.entry-actions button {
  padding: .55em .9em;
  font-family: var(--font-display);
  font-size: .75em;
  color: #000;
  text-shadow: none;
  background: var(--text-steel);
  box-shadow: 0 0 0 2px #000, inset 2px 2px 0 rgb(255 255 255 / .6), inset -2px -2px 0 rgb(0 0 0 / .35);
}
.entry-actions .entry-ok { background: var(--text-gold); }
.entry-actions button:focus-visible { outline: none; }
.entry-actions button:is(:hover, :focus-visible) {
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--cyan), inset 2px 2px 0 rgb(255 255 255 / .6), inset -2px -2px 0 rgb(0 0 0 / .35);
}

.board-panel {
  justify-items: stretch;
  gap: .45em;
  font-family: var(--font-display);
  font-size: .75em;
}
.board-head,
.board li {
  display: grid;
  grid-template-columns: 3.5em 1fr 3.5em;
  gap: 1em;
  align-items: baseline;
  padding: .25em .5em;
}
.board-head {
  margin: 0;
  font-size: .8em;
  color: var(--dim);
}
.board-head span:nth-child(2),
.board .points { text-align: right; }
.board-head span:nth-child(3),
.board .name { text-align: right; }
.board {
  display: grid;
  gap: .3em;
  margin: 0;
  padding: 0;
  list-style: none;
}
.board li {
  color: var(--c);
  text-shadow: 2px 2px 0 #000;
}
.board li:nth-child(5n + 1) { --c: var(--gold); }
.board li:nth-child(5n + 2) { --c: var(--cyan); }
.board li:nth-child(5n + 3) { --c: var(--silver); }
.board li:nth-child(5n + 4) { --c: var(--magenta); }
.board li:nth-child(5n + 5) { --c: var(--lime); }
.board li.is-new {
  color: #000;
  text-shadow: none;
  background: var(--text-gold);
  box-shadow: 0 0 0 2px #000;
  animation: blink .8s steps(1) 6;
}
.board-local {
  margin: .4em 0 0;
  font-family: var(--font);
  font-size: calc(var(--small) * 1.1);
  color: var(--dim);
  text-align: center;
}

/* Showreel: monitor in a steel frame */
.reel {
  position: relative;
  width: min(100%, 48rem);
  aspect-ratio: 16 / 9;
  background: #05060b;
  box-shadow:
    0 0 0 3px #000,
    0 0 0 7px var(--steel),
    0 0 0 9px #000,
    0 0 0 11px var(--steel-hi),
    0 14px 40px rgb(0 0 0 / .7);
}
.reel canvas,
.reel iframe,
.reel video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}
.reel canvas { image-rendering: pixelated; }
/* The Vimeo player loads hidden behind the static, then switches on like a TV. */
.reel iframe {
  z-index: 2;
  opacity: 0;
}
.reel.is-live iframe { opacity: 1; }
.reel.is-live :is(canvas, .no-signal) { visibility: hidden; }
.no-signal {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  margin: 0;
}
.no-signal > span {
  padding: .6em 1em;
  font-family: var(--font-display);
  font-size: .8em;
  color: var(--cyan);
  background: #000;
  box-shadow: 0 0 0 2px var(--steel);
}
/* "Searching for signal..." — the dots count up one at a time, then start again.
   They keep their space while invisible, so the label never changes width. */
.no-signal .dots i {
  font-style: normal;
  animation: 1.6s linear infinite;
}
.no-signal .dots i:nth-child(1) { animation-name: dot-1; }
.no-signal .dots i:nth-child(2) { animation-name: dot-2; }
.no-signal .dots i:nth-child(3) { animation-name: dot-3; }
@keyframes dot-1 { 0%, 24.9% { opacity: 0; } 25%, 100% { opacity: 1; } }
@keyframes dot-2 { 0%, 49.9% { opacity: 0; } 50%, 100% { opacity: 1; } }
@keyframes dot-3 { 0%, 74.9% { opacity: 0; } 75%, 100% { opacity: 1; } }

/* About: the text sits straight on the stars (no box), as big as the menu buttons, left-aligned */
.about p {
  margin: 0;
  white-space: pre-line;  /* the idle messages use line breaks */
  font-family: var(--font-display);
  font-size: var(--text);
  line-height: 2;
  letter-spacing: .08em;
  text-align: left;
  color: var(--fg);
  text-shadow: 2px 2px 0 #000;
}

/* WIP login */
.wip-content {
  width: min(100%, 44rem);
}
.wip-content .links {
  display: grid;
  width: 100%;
}
.wip-login {
  width: min(100%, 26rem);
  justify-items: stretch;
  text-align: left;
}
.wip-login label {
  font-family: var(--font-display);
  font-size: calc(var(--text) * .8);
  letter-spacing: .08em;
  color: var(--fg);
  text-shadow: 2px 2px 0 #000;
}
.wip-login input {
  width: 100%;
  padding: .7em .8em;
  border: 0;
  border-radius: 0;
  font-family: var(--font-display);
  font-size: 16px; /* stops iOS zooming in on focus */
  letter-spacing: .06em;
  color: var(--cyan);
  caret-color: var(--gold);
  background: #05060b;
  box-shadow: inset 0 -.18em 0 var(--steel), 0 0 0 2px #000;
  outline: none;
}
.wip-login input:focus {
  box-shadow: inset 0 -.18em 0 var(--gold), 0 0 0 2px #000, 0 0 12px rgb(255 211 74 / .45);
}
.wip-msg {
  min-height: 1.4em;
  margin: 0;
  font-family: var(--font-display);
  font-size: calc(var(--text) * .65);
  line-height: 1.6;
  letter-spacing: .06em;
  color: var(--gold);
}
.wip-go {
  justify-self: center;
  padding: .3em .8em;
  border: 0;
  cursor: pointer;
  background-image: var(--text-cyan);
}
.wip-go:is(:hover, :focus-visible) { background-image: var(--text-gold); outline: none; }

/* Contact */
.links {
  gap: .9em;
  margin: 0;
  list-style: none;
}
.links a,
.links .plain {
  display: block;
  margin: 0;
  padding: .2em .5em;
  text-align: center;
  text-decoration: none;
  outline: none;
}
.links a:is(:hover, :focus-visible) { background-image: var(--text-gold); }
.links small {
  display: block;
  margin-top: .5em;
  font-family: var(--font);
  font-size: calc(var(--text) * .7);
  letter-spacing: .08em;
  text-transform: none;
}

/* ---------- Audio check panel (only with ?debug in the address) ---------- */
.debug-panel {
  position: fixed;
  top: .75rem;
  left: .75rem;
  z-index: 10;
  width: min(32rem, calc(100vw - 1.5rem));
  max-height: calc(100vh - 1.5rem);
  overflow: auto;
  padding: .9rem 1rem;
  font-family: ui-monospace, Menlo, monospace;
  font-size: 12px;
  line-height: 1.45;
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg);
  background: rgb(4 3 12 / .94);
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--cyan);
}
.debug-panel h2 { margin: 0 0 .5rem; font-size: 13px; color: var(--cyan); }
.debug-panel dl {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: .2rem .8rem;
  margin: 0;
}
.debug-panel dt { color: var(--dim); }
.debug-panel dd { margin: 0; overflow-wrap: anywhere; }
.debug-panel div { display: flex; flex-wrap: wrap; gap: .5rem; margin-top: .75rem; }
.debug-panel button {
  padding: .35rem .6rem;
  font: inherit;
  color: #000;
  background: var(--cyan);
}
.debug-panel p { margin: .6rem 0 0; color: var(--gold); }

/* ---------- Cassette loader (C64 style) ---------- */
.loader { display: none; }

.js .loader {
  /* Sampled from References/Loader1.png */
  --c64-blue: #6b60f7;
  --c64-light-blue: #aca3ff;
  position: fixed;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  background: var(--c64-light-blue);
  font-family: "Press Start 2P", var(--font);
  letter-spacing: 0;
  text-shadow: none;
  cursor: pointer;
}
.js .loader.is-done { display: none; }

.loader-stripes {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}

/* The C64 screen is 40 x 25 characters inside the border. */
.loader-screen {
  position: relative;
  width: min(83vw, calc(73.5vh * 1.6));
  aspect-ratio: 8 / 5;
  container-type: inline-size;
  overflow: hidden;
  background: var(--c64-blue);
  color: var(--c64-light-blue);
}
.loader-text {
  margin: 0;
  font: inherit;
  font-size: 2.5cqi;
  line-height: 1;
  white-space: pre;
}
.loader-play {
  font-family: inherit;
  letter-spacing: 0;
}
.loader-play:focus-visible { outline: none; }
.loader-play:is(:hover, :focus-visible) {
  background: var(--c64-light-blue);
  color: var(--c64-blue);
}
.loader-cursor {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: top;
  background: currentColor;
  animation: blink 1s steps(1) infinite;
}

/* The loading splash: deep space, the title, and the ship out in it, all wiped in line by line */
.loader-picture {
  position: absolute;
  inset: 0;
  display: none;
  clip-path: inset(0 0 calc(100% - var(--reveal, 0%)) 0);
}
.loader-sky {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  image-rendering: pixelated;
}
.loader-art {
  position: absolute;
  top: 8%;
  left: 8%;
  right: 8%;
}
.loader-ship {
  position: absolute;
  bottom: 5%;
  left: 50%;
  width: 25.2%  /* the old 25-pixel ship at 30%: same pixel size, so this one is 21 pixels wide */;
  translate: -50% 0;
  filter: drop-shadow(0 0 .6rem rgba(124, 160, 255, .5));
}
.loader-picture svg,
.loader-ship svg {
  display: block;
  width: 100%;
  height: auto;
}

.loader-skip {
  position: absolute;
  bottom: clamp(.5rem, 2vh, 1.25rem);
  left: 0;
  right: 0;
  margin: 0;
  font-size: clamp(8px, 1.1vw, 11px);
  color: #fff;
  text-align: center;
}
.loader-skip span { padding: .4em .7em; background: #000; }

/* Tape phases, set by JS on data-phase */
.loader:is([data-phase="pilot"], [data-phase="header"]) .loader-screen { background: transparent; }
.loader:is([data-phase="data"], [data-phase="end"]) .loader-screen { background: #000; }
.loader:is([data-phase="pilot"], [data-phase="header"], [data-phase="data"], [data-phase="end"]) .loader-text { visibility: hidden; }
.loader:is([data-phase="data"], [data-phase="end"]) .loader-picture { display: block; }
.loader[data-phase="idle"] .loader-skip { visibility: hidden; }

/* ---------- Accessibility: respect reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}

/* Clicking on About or Contact has the ship shoot */
#about, #contact { cursor: crosshair; }
#about :is(a, button), #contact :is(a, button) { cursor: pointer; }

/* ---------- Showreel: our own player controls, kept as plain as the rest of the site ---------- */
.reel-controls {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 1em;
  padding: .7em 1em;
  background: linear-gradient(transparent, rgb(4 6 14 / .82) 55%);
  border-top: 1px solid rgb(154 166 196 / .18);
  font-family: var(--font-display);
  font-size: var(--small);
  color: var(--fg);
  opacity: 0;
  transition: opacity .25s;
}
.reel-controls.is-showing,
.reel-controls:focus-within { opacity: 1; }

/* Play: a triangle pointing right. Pause: two bars. */
.reel-play { display: flex; padding: .2em; color: var(--cyan); }
.reel-play i {
  width: 0;
  height: 0;
  border-left: 1.1em solid currentColor;
  border-block: .62em solid transparent;
}
.reel-play.is-playing i {
  width: .95em;
  height: 1.25em;
  border: 0;
  background: linear-gradient(90deg, currentColor 0 36%, transparent 36% 64%, currentColor 64%);
}
.reel-play:is(:hover, :focus-visible) { color: var(--gold); outline: none; }

/* Timeline: a hairline with a bar for the playhead */
.reel-seek {
  position: relative;
  flex: 1;
  height: 3px;
  background: rgb(201 210 230 / .35); /* silver */
  cursor: pointer;
}
.reel-head {
  position: absolute;
  top: 50%;
  left: 0;
  width: 3px;
  height: 1.5em;
  translate: -50% -50%;
  background: var(--gold);
  box-shadow: 0 0 0 1px #000;
}
.reel-seek:focus-visible { outline: 2px solid var(--cyan); outline-offset: 4px; }

.reel-time { min-width: 7.4em; color: var(--silver); letter-spacing: .02em; }

/* Volume: upright bars that light up */
.reel-volume {
  display: flex;
  align-items: center;
  gap: 2px;
  cursor: pointer;
}
.reel-volume i {
  width: 3px;
  height: 1.1em;
  background: rgb(91 101 128 / .7); /* steel */
}
.reel-volume i.is-on { background: var(--cyan); }
.reel-volume:focus-visible { outline: 2px solid var(--cyan); outline-offset: 4px; }

/* The way back out of the full-screen video: a small rectangle in the top corner */
.reel-restore {
  position: fixed;
  top: clamp(.6rem, 2vw, 1.2rem);
  right: clamp(.6rem, 2vw, 1.2rem);
  z-index: 7; /* above the black shade and the zoomed page */
  padding: .6em;
  opacity: 0;
  transition: opacity .25s;
}
.reel-restore.is-showing { opacity: .28; }
.reel-restore i {
  display: block;
  width: 1.5em;
  height: 1em;
  border: 2px solid var(--cyan);
  box-shadow: 0 0 0 2px #000;
}
.reel-restore:is(:hover, :focus-visible) { opacity: .85; outline: none; }
.reel-restore, .world.is-zoomed { cursor: default; } /* the pointer stays visible while the video fills the window */
.reel-restore:is(:hover, :focus-visible) i { border-color: var(--gold); }
.reel-restore.is-full i { background: var(--cyan); }
.reel-restore.is-full:is(:hover, :focus-visible) i { background: var(--gold); }

/* Terminal boot: jobs that work away, and the warnings when they do not */
.term-dots { color: var(--silver); }
.term-ok { color: var(--term-green); }
.term-warn { color: var(--gold); }
/* Her name, in big letters, just before she hands over the prompt */
.term-banner {
  color: #00ff85;
  text-shadow: 0 0 10px rgb(0 255 133 / .45);
  line-height: 1;
  white-space: pre;
}

/* Between the tape and the menu: a moment of black silence */
.js .loader.is-blank { background: #000; }
.js .loader.is-blank > * { visibility: hidden; }

/* Taller windows leave slack at the bottom: share it between the console and the group below,
   so both sit lower. These gaps take only the room that is spare, so nothing is ever pushed off. */
@media (min-height: 860px) and (min-width: 700px) {
  #home {
    padding-top: clamp(5.5rem, 10.5vh, 7.6rem); /* the title sits high, but clear of the score bar */
    padding-bottom: clamp(.5rem, 1.2vh, 1rem);  /* ...and everything below sits lower */
  }
  #home #site-title { margin-bottom: clamp(0px, 5vh, 72px); } /* ...with more air before the menu box */
  #home .console { margin-top: auto; }
  #home .ship-lane { margin-top: auto; }
  #home .copyright { margin-top: clamp(.35rem, 1.4vh, 20px); }
}

/* Phones: the title stays clear of the score bar, but the menu, the ship and the small print
   still take up the spare room at the bottom rather than leaving it empty. */
@media (max-width: 699px) and (min-height: 640px) {
  #home {
    padding-top: clamp(5rem, 11vh, 6.5rem);    /* the title sits a few blocks lower */
    padding-bottom: clamp(.5rem, 1.6vh, 1rem); /* ...and the ship and small print sit lower */
  }
  #home #site-title { margin-bottom: clamp(8px, 4vh, 38px); }  /* air between the title and the menu */
  #home .console { margin-top: auto; }
  #home .ship-lane { margin-top: auto; }
  #home .copyright { margin-top: clamp(.35rem, 1.4vh, 16px); }
}

/* The menu ship carries the same engines as the flybys, kept a little tidier for the lane */
.ship svg { position: relative; }
.ship .flyby-trail { height: 22em; opacity: .85; }
/* Fine sparks riding down the flame: each has its own speed, drift and reach, so they come and go */
.flyby-spark {
  position: absolute;
  left: var(--x);
  top: 13.5em;
  width: .3em;
  height: .3em;
  background: #fff;
  box-shadow: 0 0 .45em #ffd34a;
  opacity: 0;
  animation: spark 1s linear infinite;
}
@keyframes spark {
  0% { translate: -50% 0; opacity: 0; }
  10% { opacity: 1; }
  70% { opacity: .85; }
  100% { translate: calc(-50% + var(--drift, 0em)) var(--far, 16em); opacity: 0; }
}
.ship .flyby-glow { width: 14em; height: 11em; }

/* The music credit sits on its own line under the copyright, a touch quieter — part of the same
   paragraph, so it costs one line of text rather than another gap in the menu screen's layout. */
.copyright .credit {
  display: block;
  margin-top: .35em;
  opacity: .8;
}

/* Shorter Mac windows: the menu screen still shows whole, with the title blocks stepping down
   a size at a time rather than the bottom of the page being cropped off. */
@media (max-height: 780px) and (min-width: 700px) {
  #site-title { --cell: 15px; }
  .copyright .credit { margin-top: .2em; font-size: .9em; }
}
@media (max-height: 680px) and (min-width: 700px) {
  #site-title { --cell: 12px; }
  .console { padding-block: .6rem; }
  .menu { gap: .3em; }
  .screen { padding-top: 2.8rem; }
}
@media (max-height: 620px) and (min-width: 700px) {
  :root { --text: clamp(11px, 1.4vw, 14px); }
  #site-title { --cell: 11px; }
  .screen { padding-top: 2.4rem; }
}

/* Warp, second stage: the engines open right up and the whole craft judders */
.flyby-craft.is-warp-2 .flyby-burner { width: 3.7em; height: 17em; }
.flyby-craft.is-warp-2 .flyby-trail { width: 8.5em; height: 52em; opacity: 1; }
.flyby-craft.is-warp-2 .flyby-glow { width: 22em; height: 17em; }
.flyby-craft.is-warp-2 .flyby-body {
  animation: flyby-wobble 1.8s ease-in-out infinite, warp-shake-2 .05s steps(2) infinite alternate;
}
@keyframes warp-shake-2 {
  from { translate: -.14em -.05em; }
  to { translate: .14em .1em; }
}

/* Busy shooting: the menu box fades out of the way, and lets the shots through while it is gone. */
#home .console { transition: opacity .55s ease, transform .55s ease; }
#home .console.is-away {
  opacity: 0;
  transform: scale(.985);
  pointer-events: none;
}

/* Engines grow and ease off with the speed, rather than stepping between warp stages */
.flyby-burner,
.flyby-trail,
.flyby-glow { transition: width .9s ease, height .9s ease; }

/* Menu out of the way: the ship runs forward, holds station on a soft drift, then falls back */
.ship.is-free { transition: none; } /* flown frame by frame while the menu is out of the way */
.ship.is-falling { transition: transform .95s cubic-bezier(.5, .05, .6, 1); }   /* ...and the drop home */
.ship .flyby-burner,
.ship .flyby-trail,
.ship .flyby-glow { transition: width .25s ease, height .25s ease; }
.ship.is-boost .flyby-burner { width: 2.8em; height: 12em; }
.ship.is-boost .flyby-trail { height: 42em; opacity: 1; }
.ship.is-boost .flyby-glow { width: 18em; height: 14em; }

/* About and Contact: Back, or take the scenic route */
.actions {
  display: flex;
  gap: clamp(1.2rem, 5vw, 3rem);
  align-items: center;
  justify-content: center;
  margin: 0;
}
button.warp {
  border: 0;
  font-size: inherit;
  line-height: inherit;
  cursor: pointer;
}
.warp.is-busy { opacity: .45; cursor: default; } /* already out there somewhere */

/* Out at warp: sit still for a moment and the page steps aside, back the instant anything moves */
.screen > * { transition: opacity .3s ease; }
html.is-hushed .screen > * {
  opacity: 0;
  transition: opacity .9s ease;
}

/* Once the Contact terminal is closed, Back stays put so the visitor can always return to the menu */
html.is-hushed.contact-revealed #contact .actions {
  opacity: 1;
  transition: none;
}

/* The menu box sits higher than the layout puts it, closing the gap under the title — but only where
   there is room for it, so it never rides up over the words on a short window. */
@media (min-height: 760px) {
  #home .console { translate: 0 -5px; }
}

/* What the ship is carrying: picked up at warp, spent in the game */
.kit {
  position: absolute;
  top: clamp(2.6rem, 7vw, 3.6rem);
  left: 0;
  right: 0;
  z-index: 4;
  display: flex;
  justify-content: center;
  gap: clamp(.8rem, 3vw, 1.8rem);
  margin: 0;
  padding: 0 clamp(1rem, 4vw, 2.2rem);
  font-family: var(--font-display);
  font-size: var(--small);
  color: var(--dim);
  pointer-events: none;
}
.kit b { color: var(--cyan); }
#kit-shield b { color: #7fd0ff; }
#kit-triple b { color: var(--gold); }

/* The apparition: a face of an old god looming out of the stars when the shooting is going well */
.apparition {
  position: fixed;
  inset: 0;
  z-index: 3;                 /* it looms over the whole sky, the menu box included */
  display: grid;
  place-items: center end;            /* he looms right up against the right-hand edge */
  padding-right: 0;
  opacity: 0;
  pointer-events: none;
}
.apparition svg {
  width: min(55vw, 62vh);
  height: auto;
  opacity: 0;
  filter: drop-shadow(0 0 18px rgb(255 195 90 / .45)) drop-shadow(0 0 60px rgb(150 90 20 / .4));
}
html.has-apparition .apparition { opacity: 1; }
/* Every other visit he is over on the left instead, turned the other way to face into the screen. */
html.god-left .apparition { transform: scaleX(-1); }
html.has-apparition .apparition svg { animation: apparition-loom 4s ease-out both, apparition-flicker .22s steps(2) infinite; }
@keyframes apparition-loom {
  from { opacity: 0; transform: scale(.97); }
  18%  { opacity: .43; }   /* half there: the stars still show through him */
  80%  { opacity: .41; }
  to   { opacity: 0; transform: scale(1.03); }
}
@keyframes apparition-flicker {
  50% { filter: drop-shadow(0 0 22px rgb(255 205 110 / .55)) drop-shadow(0 0 66px rgb(160 95 22 / .42)) brightness(1.04); }
}
@media (prefers-reduced-motion: reduce) {
  html.has-apparition .apparition svg { animation: none; opacity: .42; }
}

/* The shield: a shimmering blue orb around the ship, thinner as its power goes */
.ship.has-shield::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 190%;
  height: 190%;
  translate: -50% -50%;
  border-radius: 50%;
  background: radial-gradient(closest-side, rgb(150 225 255 / .16), rgb(90 180 255 / .28) 68%, rgb(120 220 255 / .5) 88%, transparent 100%);
  box-shadow: 0 0 12px rgb(120 210 255 / .5) inset, 0 0 14px rgb(90 180 255 / .35);
  opacity: calc(.35 + var(--shield, 1) * .65);
  animation: shield-shimmer 2.4s ease-in-out infinite;
  pointer-events: none;
}
@keyframes shield-shimmer {
  50% { transform: scale(1.06); filter: hue-rotate(-12deg) brightness(1.15); }
}

/* The last seconds of a shield: it flickers before it goes */
.ship.has-shield.shield-fading::after {
  animation: shield-shimmer 2.4s ease-in-out infinite, shield-flicker .3s steps(2) infinite;
}
@keyframes shield-flicker { 50% { opacity: .12; } }

/* Out of the tape screen: nothing but stars for a moment, then everything else fades up */
html.is-arriving main { opacity: 0; transition: none; } /* out at once, so the menu never shows through */
html.is-arriving.is-here main { animation: arrive 1.4s ease both; }
@keyframes arrive { from { opacity: 0; } to { opacity: 1; } }

/* For a run of good shooting */
.kipper {
  position: fixed;
  top: clamp(3.6rem, 11vh, 6rem);
  left: 50%;
  translate: -50% 0;
  z-index: 6;
  margin: 0;
  padding: .4em .9em .5em;
  font-family: var(--font-display);
  font-size: var(--small);
  white-space: nowrap;
  color: var(--cyan);
  background: rgb(4 3 12 / .85);
  box-shadow: 0 0 0 2px #000, 0 0 0 4px var(--cyan);
  animation: kipper-flash .42s steps(1) infinite;
}
@keyframes kipper-flash {
  50% {
    color: #04030c;
    background: var(--cyan);
    box-shadow: 0 0 0 2px #000, 0 0 0 4px #fff;
  }
}

/* The Contact finale: everything goes to black, then comes back as nothing but stars */
.blackout {
  position: fixed;
  inset: 0;
  z-index: 8;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.5s ease;
}
html.to-black .blackout { opacity: 1; }
/* The finale hides the page, so Back to menu gets its own spot, bottom centre, over the stars */
.finale-back {
  position: fixed;
  left: 50%;
  bottom: clamp(1rem, 4vh, 2.2rem);
  translate: -50% 0;
  z-index: 9;
  opacity: 0;
  pointer-events: none;
  transition: opacity 1.2s ease;
}
html.stars-only .finale-back { opacity: 1; pointer-events: auto; transition-delay: 1s; }
/* Hidden instantly behind the black, and faded back in when the page returns */
main { transition: opacity 1.2s ease; }
html.stars-only main { opacity: 0; transition: none; }
