/* Old-Minecraft chrome, built from the game's own art.
   Textures come out of the client jar already on this host (see assets.py), so the
   dirt behind the menu and the 8x8 font are the real ones rather than an imitation.

   Everything is sized in --px, one Minecraft pixel. Shrinking the interface for a
   phone is therefore one number, not a second stylesheet. */

:root {
  --px: 3;
  --font: url(/assets/font.png);
  --panel: #c6c6c6;
  --panel-lo: #555555;
  --btn: #8b8b8b;
  --btn-hi: #b9b9b9;
  --btn-lo: #4f4f4f;
  --btn-hover: #99a0c8;
  --void: #0c0c10;
  --bar: #3ade3a;
  --gap: calc(4px * var(--px));
}

* { box-sizing: border-box; }

html, body {
  margin: 0; height: 100%; overflow: hidden;
  background: var(--void);
  font-family: monospace;
  color: #fff;
  -webkit-user-select: none; user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;                 /* the page handles its own pinch and pan */
  overscroll-behavior: none;
}

.hidden { display: none !important; }

/* ---------------------------------------------------------------- the map itself */

#viewport {
  position: fixed; inset: 0;
  cursor: grab;
  /* Vanilla's menu backdrop: dirt, scaled up, darkened. It shows through wherever
     the world has never been generated. */
  background-image: linear-gradient(rgba(0,0,0,.72), rgba(0,0,0,.72)), url(/assets/dirt.png);
  background-size: auto, calc(16px * var(--px));
  image-rendering: pixelated;
}
#viewport.dragging { cursor: grabbing; }

#world { position: absolute; inset: 0; }

/* One band per zoom level in play, plus a bottom one for placeholders. Each carries
   the whole pan and scale in a single transform, with tiles at integer offsets
   inside it -- so there are no subpixel seams and a pan is one style write per band
   rather than one per tile.

   The stacking is what stops a zoom flickering. The level being left stays visible
   underneath the level arriving, so the map only ever gains detail; and placeholders
   are *below* both, so a tile still loading shows the previous zoom's pixels instead
   of a hole punched through them. */
.layer {
  position: absolute; inset: 0;
  transform-origin: 0 0;
  will-change: transform;
}
.layer.loading { z-index: 1; }

.tile, .ph {
  position: absolute;
  transform-origin: 0 0;
  image-rendering: pixelated;
}
.tile img { display: block; width: 100%; height: 100%; image-rendering: pixelated; }

/* Imagery is never dimmed, tinted or overlaid, whatever its provenance -- a stale
   tile, a coarse ancestor standing in for one that has not rendered yet, all of it
   is shown as it is. A render of the world beats any decoration this page could put
   on top of it, and the bands already put every one of them above the loading
   layer. */

/* The loading area. Only ever drawn over genuine void -- if any band is showing this
   ground, even a much coarser one, there is something better to look at and this is
   not drawn at all (see coveredElsewhere). Sits in the bottom band, so imagery is
   above it by construction as well as by policy. */
.ph {
  background:
    repeating-linear-gradient(45deg,
      rgba(255,255,255,.05) 0 calc(4px * var(--px)),
      rgba(0,0,0,0) calc(4px * var(--px)) calc(8px * var(--px)));
  outline: 1px solid rgba(255,255,255,.06);
  outline-offset: -1px;
}

/* Progress. Lives on the placeholder when there is nothing under it yet, and on the
   tile itself when there is -- see showBar() -- so it is never hidden by the pixels
   it is reporting on. It exists only while a render does. */
.bar {
  position: absolute; left: 8%; right: 8%; top: 50%;
  height: calc(3px * var(--px));
  transform: translateY(-50%);
  border: calc(1px * var(--px)) solid #000;
  background: #1c1c1c;
  overflow: hidden;
  z-index: 2;
}
.bar > span {
  display: block; height: 100%; width: 0%;
  background: var(--bar);
  transition: width .2s linear;
}

/* Small tiles are rendered in one call and can only report 0% then 100%, so a bar
   sitting at zero would be reporting a stall that is not happening. Until a real
   percentage arrives the fill sweeps instead, which says "working" without claiming
   to know how far along it is. Coarse tiles -- the ones slow enough for progress to
   be worth watching -- report per work-tile and get the real bar. */
.bar.indet > span, .track.indet > span {
  width: 35% !important;
  transition: none;
  animation: sweep 1.1s ease-in-out infinite;
}
@keyframes sweep {
  0%   { margin-left: 0; }
  50%  { margin-left: 65%; }
  100% { margin-left: 0; }
}

/* --------------------------------------------------------------------- the font */

.mc {
  display: inline-flex; align-items: flex-start; flex-wrap: wrap;
  color: #fff;
  filter: drop-shadow(calc(1px * var(--px)) calc(1px * var(--px)) 0 #3f3f3f);
  line-height: 0;
}
.mc > i {
  display: block;
  height: calc(8px * var(--px));
  background-color: currentColor;
  -webkit-mask-image: var(--font); mask-image: var(--font);
  -webkit-mask-size: calc(128px * var(--px)); mask-size: calc(128px * var(--px));
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  flex: 0 0 auto;
}
.mc.big { --px: 4; }
.mc.tiny { --px: 2; }
.mc.dim { color: #a0a0a0; }
.mc.bad { color: #ff5555; }

/* -------------------------------------------------------------------- the chrome */

#chrome { position: fixed; inset: 0; pointer-events: none; }
#chrome > * { pointer-events: auto; }

.panel {
  position: absolute;
  background: var(--panel);
  border: calc(1px * var(--px)) solid #000;
  box-shadow:
    inset calc(2px * var(--px)) calc(2px * var(--px)) 0 #ffffff,
    inset calc(-2px * var(--px)) calc(-2px * var(--px)) 0 var(--panel-lo);
  padding: var(--gap);
  color: #3f3f3f;
  max-width: calc(100vw - 2 * var(--gap));
}
.panel .mc { filter: none; color: #3f3f3f; }
.panel .mc.dim { color: #6b6b6b; }

#titlebar {
  top: max(var(--gap), env(safe-area-inset-top)); left: var(--gap);
  display: flex; flex-direction: column; gap: calc(2px * var(--px));
}
#controls {
  top: max(var(--gap), env(safe-area-inset-top)); right: var(--gap);
  display: flex; flex-direction: column; gap: calc(2px * var(--px));
}
#queuepanel {
  bottom: max(var(--gap), env(safe-area-inset-bottom)); left: var(--gap);
  width: calc(132px * var(--px));
  display: flex; flex-direction: column; gap: calc(2px * var(--px));
}
.panel-head {
  border-bottom: calc(1px * var(--px)) solid #8b8b8b;
  padding-bottom: calc(2px * var(--px));
}

.row { display: flex; gap: calc(2px * var(--px)); align-items: center; }

.btn {
  font: inherit; cursor: pointer;
  background: var(--btn);
  border: calc(1px * var(--px)) solid #000;
  box-shadow:
    inset calc(1px * var(--px)) calc(1px * var(--px)) 0 var(--btn-hi),
    inset calc(-1px * var(--px)) calc(-1px * var(--px)) 0 var(--btn-lo);
  padding: calc(3px * var(--px)) calc(4px * var(--px));
  display: flex; align-items: center; justify-content: center;
}
.btn .mc { color: #fff; filter: drop-shadow(calc(1px * var(--px)) calc(1px * var(--px)) 0 #3f3f3f); }
.btn:hover { background: var(--btn-hover); }
.btn:active { box-shadow: inset calc(1px * var(--px)) calc(1px * var(--px)) 0 var(--btn-lo),
                          inset calc(-1px * var(--px)) calc(-1px * var(--px)) 0 var(--btn-hi); }
.btn.wide { min-width: calc(18px * var(--px)); }
.btn.on { background: #6a8f4a; }

.zoomlabel { min-width: calc(20px * var(--px)); justify-content: center; }

/* ------------------------------------------------------------------- queue rows */

#queuelist { display: flex; flex-direction: column; gap: calc(2px * var(--px)); }

.qrow {
  display: flex; align-items: center; gap: calc(3px * var(--px));
  background: #a8a8a8;
  padding: calc(1px * var(--px)) calc(2px * var(--px));
}
.qrow.run { background: #b6c69a; }
.qrow .slot { width: calc(14px * var(--px)); }
.qrow .what { flex: 1 1 auto; min-width: 0; }
.qrow .track {
  width: calc(34px * var(--px)); height: calc(4px * var(--px));
  border: calc(1px * var(--px)) solid #000; background: #1c1c1c;
  flex: 0 0 auto; overflow: hidden;
}
.qrow .track > span { display: block; height: 100%; width: 0; background: var(--bar);
                      transition: width .2s linear; }

/* ------------------------------------------------------------ status and errors */

/* The client's own queue: a count, nothing more, and only when it is not zero. */
#status {
  position: absolute; right: var(--gap);
  bottom: max(var(--gap), env(safe-area-inset-bottom));
  display: flex; flex-direction: column; align-items: flex-end;
  gap: calc(2px * var(--px));
}
#status .mc { color: #e0e0e0; }
#waiting { color: #ffd35c !important; }

/* Always there, never in the way: enough contrast to read if you look for it, not
   enough to compete with the map. No panel behind it -- the drop shadow every .mc
   already carries is what keeps it legible over both a snowfield and a deep ocean. */
#version { opacity: .38; }

#conn {
  position: absolute; left: 50%;
  bottom: max(var(--gap), env(safe-area-inset-bottom));
  transform: translateX(-50%);
  background: rgba(0,0,0,.6);
  padding: calc(2px * var(--px)) calc(4px * var(--px));
  white-space: nowrap;
}

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

/* One number does the shrinking, because everything is expressed in it. Tablets get
   2px, phones 2px with the decorative half of the chrome dropped: on a 390px-wide
   screen the title is the one thing that earns none of the space it takes. */
@media (max-width: 900px) {
  :root { --px: 2; --gap: calc(3px * var(--px)); }
  .mc.big { --px: 3; }
}

@media (max-width: 560px) {
  #title { display: none; }
  #titlebar { padding: calc(2px * var(--px)); }
  #queuepanel { width: min(46vw, calc(96px * var(--px))); }
  .qrow .track { width: calc(20px * var(--px)); }
  .qrow .slot { display: none; }
  #controls { padding: calc(2px * var(--px)); }
  .btn { padding: calc(4px * var(--px)) calc(4px * var(--px)); }   /* still tappable */
}

/* Phone in landscape: almost no vertical room, so the chrome gets out of the way. */
@media (max-height: 430px) {
  #titlebar { display: none; }
  #queuepanel { width: min(38vw, calc(88px * var(--px))); }
}
