/* The door.
 *
 * Science fiction throughout, per the design notes -- but a sign-in page is
 * not where a game is sold. It says what it is, asks two questions, and gets
 * out of the way. The main page describes the game, later, when there is one.
 */

:root {
  --deep:    #0d1524;
  --panel:   #131d30;
  --sunk:    #0a111d;
  --ink:     #e8ecf2;
  --dim:     #94a2b8;
  --faint:   #6c7c96;
  --line:    #22304a;
  --rule:    #2c3c58;
  --sun:     #ffd67a;
  --land:    #6f8f4a;
  --warn:    #e0996f;

  --display: "Helvetica Neue", Arial, sans-serif;
  --mono:    ui-monospace, "SFMono-Regular", Consolas, monospace;
}

* { box-sizing: border-box; }

/* Anything hidden is hidden. Without this, a class that sets display -- and
 * most layout classes do -- quietly overrides the attribute, and the element
 * stays on screen with no clue as to why. */
[hidden] { display: none !important; }

html, body { height: 100%; }

/* ---- scrollbars -------------------------------------------------------- */

/* THE BROWSER DOES NOT KNOW THIS PAGE IS DARK. Left to itself it paints its
 * own furniture for a light page -- a white scroll track with a grey thumb --
 * and the result is a bright bar down the side of a panel the colour of night.
 * One line tells it, and it also settles the native form controls on the door.
 *
 * Then the bar itself: no track at all, so it takes the colour of whatever it
 * is over, and a thumb the same value as a panel edge. A scrollbar is telling
 * you how much more there is, which is worth about as much attention as a
 * border, and it brightens under the pointer for the moment you want to drag
 * it. Both spellings, because the standard properties are recent and the
 * -webkit- ones are what most of the browsers reading this actually have. */
:root { color-scheme: dark; }

* {
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}

*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }

*::-webkit-scrollbar-thumb {
  background: var(--rule);
  border-radius: 5px;
  /* Inset off the edge, so the thumb reads as sitting in the panel rather
     than being welded to its side. */
  border: 2px solid transparent;
  background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover { background: var(--faint); background-clip: content-box; }
*::-webkit-scrollbar-corner { background: transparent; }

body {
  margin: 0;
  /* One cell. The gate sits in the middle of it; the world fills it. */
  display: grid;
  grid-template: 1fr / 1fr;
  background:
    radial-gradient(ellipse 120% 80% at 50% 118%, #1b2c48 0%, transparent 62%),
    var(--deep);
  color: var(--ink);
  font-family: var(--display);
  font-size: 16px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

/* ---- the panel --------------------------------------------------------- */

.gate {
  place-self: center;
  width: 100%;
  max-width: 21rem;
  padding: 1.5rem;
  display: grid;
  gap: 1.5rem;
  justify-items: center;
  text-align: center;
}

.crest svg {
  display: block;
  width: 3.5rem;
  height: 3.5rem;
  filter: drop-shadow(0 0.75rem 1.5rem #0006);
}

h1 {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  letter-spacing: -0.015em;
}

/* ---- the form ---------------------------------------------------------- */

form {
  width: 100%;
  display: grid;
  gap: 0.9rem;
  padding: 1.5rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 0.65rem;
  box-shadow: 0 1.5rem 3rem #0000004d;
  text-align: left;
}

.field { display: grid; gap: 0.35rem; }

label {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--faint);
}

input {
  width: 100%;
  padding: 0.6rem 0.7rem;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: 0.35rem;
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
}

input:focus-visible {
  outline: 2px solid var(--sun);
  outline-offset: 1px;
  border-color: transparent;
}

button {
  font: inherit;
  cursor: pointer;
}

#submit {
  margin-top: 0.3rem;
  padding: 0.62rem 1rem;
  background: var(--sun);
  border: 0;
  border-radius: 0.35rem;
  color: #241a05;
  font-size: 0.95rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  transition: filter 0.12s ease;
}

#submit:hover:not(:disabled) { filter: brightness(1.08); }
#submit:disabled { opacity: 0.55; cursor: progress; }

#submit:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* ---- the message ------------------------------------------------------- */

/* Errors say what went wrong and what to do about it, and they sit where the
 * eye already is rather than at the top of the page. */
.note {
  margin: 0;
  padding: 0.55rem 0.7rem;
  background: var(--sunk);
  border-left: 2px solid var(--warn);
  border-radius: 0.2rem;
  font-size: 0.86rem;
  color: var(--dim);
}

.note[data-kind="good"] { border-left-color: var(--land); }

/* ---- swapping between signing in and starting out ---------------------- */

.swap {
  margin: 0.2rem 0 0;
  font-size: 0.86rem;
  color: var(--faint);
  text-align: center;
}

.link {
  padding: 0;
  background: none;
  border: 0;
  color: var(--sun);
  font-size: inherit;
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.link:focus-visible {
  outline: 2px solid var(--sun);
  outline-offset: 2px;
  border-radius: 0.15rem;
}

.footnote {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.64rem;
  letter-spacing: 0.06em;
  color: var(--faint);
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}

/* ---- looking down at the zone ------------------------------------------- */

.world {
  place-self: stretch;
  display: grid;
  grid-template-rows: auto 1fr;
  min-height: 0;          /* or the canvas row refuses to shrink */
}

.bar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}

.where { display: flex; align-items: baseline; gap: 0.6rem; min-width: 0; }

.where strong { font-size: 1rem; font-weight: 600; }

.where span,
.who span {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--faint);
}

/* Where the settler is standing, in metres, changing as they walk. In the
   bar rather than in the strip along the bottom, because the command line
   floats over that corner and a number somebody watches while walking must
   never be a number something else is sitting on. */
.where .standing {
  color: var(--sun);
  opacity: 0.75;
  /* The rest of this bar is small uppercase labels. These are a measurement,
     and "35820 M" is not how a metre is written. */
  text-transform: none;
  letter-spacing: 0.04em;
  font-size: 0.74rem;
}

.who { display: flex; align-items: baseline; gap: 0.8rem; }
.who span { color: var(--sun); letter-spacing: 0.06em; text-transform: none;
            font-size: 0.8rem; }

#map {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
}

/* ---- choosing where to come down ---------------------------------------- */

.choose {
  place-self: center;
  width: 100%;
  max-width: 62rem;
  padding: 2.5rem 1.5rem;
  display: grid;
  gap: 1.5rem;
  justify-items: center;
}

.choosehead { text-align: center; }
.choosehead h1 { margin: 0 0 0.3rem; font-size: 1.5rem; font-weight: 600; }
.choosehead p { margin: 0; color: var(--dim); font-size: 0.9rem; }

/* ONE PLANET TO A SECTION, its towns as a gallery inside it. A planet may
   have any number of towns before long, so they are laid out as cards that
   wrap rather than as rows that grow -- and the ground each one sits on is
   the biggest thing on the card, because that is what somebody is choosing
   between. */
.planets {
  width: 100%;
  display: grid;
  gap: 1.1rem;
  grid-template-columns: 1fr;
  align-items: start;
}

.planet {
  display: grid;
  gap: 0.6rem;
  padding: 1rem;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 0.65rem;
}

.planet > header { display: grid; gap: 0.45rem; }
.planet h2 { margin: 0; font-size: 1.25rem; font-weight: 600; }

/* The name and the numbers on one line, so a planet is a headline rather
   than a paragraph. */
.planetline {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 1.5rem;
}

.zones {
  display: grid;
  gap: 0.7rem;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
}

.planet dl {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 1.75rem;
}

.planet dl div { grid-auto-flow: column; align-items: baseline; gap: 0.4rem; }

.planet dl div { display: grid; gap: 0.1rem; }

.planet dt {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--faint);
}

.planet dd {
  margin: 0;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
}

/* A landing zone. The whole card is the button, because the whole card is
   what somebody is choosing between. */
.zone {
  display: grid;
  gap: 0.55rem;
  align-content: start;
  padding: 0.5rem;
  background: var(--sunk);
  border: 1px solid var(--rule);
  border-radius: 0.45rem;
  color: inherit;
  text-align: left;
  transition: border-color 0.12s ease, background 0.12s ease;
}

.zone:hover:not(:disabled) { border-color: var(--sun); background: #111d33; }
.zone:focus-visible { outline: 2px solid var(--sun); outline-offset: 2px; }
.zone:disabled { opacity: 0.45; cursor: default; }
.zone.picked { border-color: var(--sun); opacity: 1; }

/* Room kept for a picture of the place. There is no terrain yet, so there is
   nothing honest to draw -- but the space it will need is already here, so
   adding it later does not move everything else. */
/* The country round a landing zone, painted from the same tiles the world
   view uses. Solid rather than dashed now: it is a picture of somewhere, not
   a space held for one. */
.survey {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1;
  border: 1px solid var(--rule);
  border-radius: 0.3rem;
  background: #0b1220;
}

.country {
  margin: 0.35rem 0 0;
  font-family: var(--mono);
  font-size: 0.74rem;
  letter-spacing: 0.04em;
  color: var(--faint);
}

.survey em {
  font-family: var(--mono);
  font-style: normal;
  font-size: 0.5rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--faint);
  text-align: center;
  line-height: 1.3;
}

.zonebody { display: grid; gap: 0.1rem; min-width: 0; }
.zonebody strong { font-size: 1rem; font-weight: 600; }

.stat {
  font-family: var(--mono);
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--dim);
  font-variant-numeric: tabular-nums;
}

/* ---- windows ----------------------------------------------------------- */
/*
   One look for every screen in the game. Nothing here knows what a window
   contains, which is the point: the survey tool, a crafting screen and a
   structure's management page are all this.
*/

.stage {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 0;
  /* So the zoom label can sit over the map rather than take a row of its own */
  position: relative;
}

.zoomlabel {
  position: absolute;
  top: 1.1rem;
  left: 50%;
  transform: translateX(-50%);
  margin: 0;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: #0a1120c4;
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--dim);
  pointer-events: none;
}

/* The halo: an invisible margin round every window, holding nothing but the
   grab zone for resizing. Same idea as .chatbox, and for the same reason -- an
   edge shared with a scrollbar is an edge the browser answers for, so the grip
   has to live somewhere no scrollbar can reach. Kept in step with HALO in
   hudwindow.js, which pulls each window back out by this much so nothing
   visibly moved. */
.winhalo {
  position: fixed;
  display: flex;
  min-width: 232px;
  min-height: 192px;
  border: 6px solid transparent;
}

.win {
  flex: 1;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  box-shadow: 0 18px 48px #00000073;
  overflow: hidden;
}

.winbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.45rem 0.5rem 0.45rem 0.75rem;
  border-bottom: 1px solid var(--line);
  background: #131c2e;
  cursor: move;
  user-select: none;
  flex: none;
}

.winbar h2 {
  margin: 0;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
}

.winclose {
  font: inherit;
  font-size: 1.1rem;
  line-height: 1;
  width: 1.5rem;
  height: 1.5rem;
  border: 0;
  border-radius: 5px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
}

.winclose:hover { background: #2a3448; color: var(--ink); }

/* The body must be able to shrink, or a canvas inside refuses to give the
   window back its space when it is dragged smaller. */
.winbody { flex: 1; min-height: 0; display: flex; }
.winbody > * { flex: 1; min-height: 0; min-width: 0; }

/* Where a window says what it is doing. One strip, one place, whatever the
   window -- so a player never hunts for the answer to "is it working". */
.winfoot {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  flex: none;
  padding: 0.45rem 0.6rem;
  border-top: 1px solid var(--line);
  background: #0f1728;
}

.winactions { display: flex; gap: 0.4rem; flex: none; }

.winactions button {
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.3rem 1rem;
  border: 1px solid var(--sun);
  border-radius: 6px;
  background: #ffd67a1c;
  color: var(--sun);
  cursor: pointer;
}

.winactions button:disabled { opacity: 0.45; cursor: default; }

.winstatus {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 0.74rem;
  line-height: 1.35;
  color: var(--dim);
}

/* ---- the survey tool ---------------------------------------------------- */

.surveytool { display: grid; grid-template-columns: 11rem 1fr;
             min-height: 0; }

.matlist {
  display: flex;
  flex-direction: column;
  gap: 1px;
  overflow-y: auto;
  min-height: 0;
  padding: 0.4rem;
  border-right: 1px solid var(--line);
}

.matlist button {
  font: inherit;
  font-size: 0.82rem;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.4rem 0.55rem;
  border: 0;
  border-radius: 6px;
  background: transparent;
  color: var(--faint);
  cursor: pointer;
  flex: none;
}

.matlist button small {
  font-family: var(--mono);
  font-size: 0.6rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  opacity: 0.65;
}

.matlist button:hover { background: #1a2436; color: var(--ink); }
.matlist button.is-on { background: #ffd67a14; color: var(--sun); }

.scanmap {
  display: block;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  background: #0d1524;
  cursor: crosshair;
}

/* ---- the sample window -------------------------------------------------- */
/*
   A sample is work, so it reads as a worksheet: what is being tested, the
   steps as they finish, then a bar per property. The bars are the point --
   a value alone is a number, and a value against the range the material could
   ever have reached is a judgement.
*/

/* THE SCROLLER OWNS THE PADDING, which is what puts a scrollbar where it
   belongs. Padding this box instead left the bar 0.8rem inside the window,
   floating in the panel with the readings pressed up against it -- because a
   scrollbar sits at the edge of the element that scrolls, and that was not
   this one. The material list next door had it right by accident: it is its
   own scroller and carries its own padding, so its bar rides the panel edge
   and the content clears it evenly. Same arrangement here now. */
.sampler {
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* The material and where it was taken, and nothing else. What the window is
   doing goes in the status strip along the bottom, the same as every other
   window. */

.sampler .props {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
  padding: 0.8rem;
}

.prop header { display: flex; align-items: baseline; justify-content: space-between; }
.prop header b { font-size: 0.8rem; font-weight: 600; }
.prop .val { font-family: var(--mono); color: var(--sun);
             font-variant-numeric: tabular-nums; }

/* 0 to 100 always, so two properties can be compared by eye. The capped part
   is the range this material can never read in -- drawn, not omitted, because
   what is impossible is half the information. */
.track {
  position: relative;
  height: 0.6rem;
  margin: 0.25rem 0 0.2rem;
  border-radius: 3px;
  background: #101828;
  overflow: hidden;
}

.track span { position: absolute; top: 0; bottom: 0; }

/* The bar is a WINDOW ONTO A FULL-WIDTH GRADIENT rather than a coloured strip
   of its own, which is what lets the colour mean something. The gradient runs
   from where this material's readings can begin to the top of the scale, so
   the colour at the end of the bar says how far up the range it got -- on a
   strip that started at nothing, a material that can never read below 55 would
   have looked half-warm at its very worst. Below the floor it is flat: that
   stretch is not a low reading, it is not a reading at all.

   Clipped rather than sized, so the gradient stays anchored to the track while
   the reading counts down across it. --floor is set per bar. */
.track .fill {
  left: 0;
  right: 0;
  background: linear-gradient(90deg,
    #3d5a8a 0 var(--floor, 0%), var(--sun) 100%);
}

/* The stretch below where this material's readings can ever begin. Hatched
   like warning tape, over the flat head of the gradient rather than instead of
   it: the ground there is not missing, it is ruled out, and a bar with a bite
   taken from it said the wrong one. */
.track .below {
  left: 0;
  background-image: repeating-linear-gradient(45deg,
    #05080e 0 2px, transparent 2px 6px);
}

/* Where this material's readings begin. A tick, so the shape of the bar is
   still one piece. */
.track .floor { width: 1px; background: #0d1524b0; }

.prop .range {
  margin: 0;
  font-size: 0.66rem;
  color: var(--faint);
}

/* ---- the command line --------------------------------------------------- */
/*
   Not a window, on purpose. The view is click-through at all times so it never
   swallows a click meant for the ground beneath it, and it only grows a frame
   while the input has focus. A window with a title bar you drag has different
   rules; making one object do both would spoil both.
*/

/* ONE BOX, two halves. The log fills it and the command line sits at its
   foot, so the pair move and resize together instead of ending up at opposite
   corners -- and so the edges of one rectangle are all the resize arithmetic
   there is. Invisible itself: what a player sees is the log's frame and the
   field's, with a gap between them, exactly as before. */
.chatbox {
  position: fixed;
  z-index: 30;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  /* AN INVISIBLE MARGIN, and the resize grip lives in it. An edge shared with
     a scrollbar is an edge the browser answers for: 9px of bar over a 7px grab
     zone left nothing to aim at once the log filled up, so the right side went
     from resizable to barely. This puts the whole zone in transparent space no
     scrollbar can reach. Border rather than padding because box-sizing is
     border-box everywhere, so the saved width and height go on meaning the
     same thing. Keep in step with HALO in chat.js, which pulls the frame back
     out by this much so nothing visibly moved. */
  border: 6px solid transparent;
  /* Click-through while idle, which is what the log inside it is too. It
     becomes solid with the frame, because a box you cannot point at is a box
     you cannot grab by the edge. */
  pointer-events: none;
}

body.chatting .chatbox { pointer-events: auto; }

.chatview {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 0.45rem 0.6rem;
  border: 1px solid transparent;
  border-radius: 7px;
  transition: background 0.12s, border-color 0.12s;
  /* THE LOG IS NOT A THING WHEN NOBODY IS TYPING. No frame, and with it no
     scrollbar and no hit test: the last few lines sit over the world like
     subtitles, and every click goes past them to the ground. Clipped rather
     than scrollable, because a bar down the side of something with no edges
     is furniture belonging to a panel that is not there. */
  overflow: hidden;
  pointer-events: none;
}

/* And it IS a thing while somebody is. The frame appears, and the log becomes
   a panel that can be read back through -- which needs the scrollbar and needs
   the pointer, and the earlier note here saying it must be click-through at
   all times was written before there was anything to scroll. Clicking past it
   is still one keystroke away: Escape, or Enter on an empty line. */
body.chatting .chatview {
  background: #0a1120e0;
  border-color: var(--line);
  overflow-y: auto;
  pointer-events: auto;
}

/* THE LOG HANGS FROM THE BOTTOM. The box is whatever size the player dragged
   it to, and three lines of chat belong just above the command line rather
   than stranded at the top of it. An auto margin on a spacer rather than
   justify-content, which would push the overflowing top of a long log out of
   reach of its own scrollbar. */
.chatview::before { content: ""; margin-top: auto; }

.chatline {
  margin: 0.1rem 0;
  flex: none;
  font-size: 0.78rem;
  line-height: 1.45;
  color: #b9c6da;
  text-shadow: 0 1px 3px #000a;
}

.chatline.said { color: var(--sun); }
.chatline.bad { color: #ef8b80; }

/* The field is always live, frame or no frame: clicking it is one of the three
   ways into it. So it keeps the pointer while the box around it has given it
   up. */
.chatinput {
  flex: none;
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid transparent;
  border-radius: 7px;
  background: #0a112094;
}

body.chatting .chatinput { border-color: var(--sun); background: #0a1120f0; }

/* And the chevron is the handle. Grabbing the prompt is grabbing the whole
   thing by its base -- it is the one part of the chat that is chrome rather
   than something to read or type in. The cursor only offers it while the frame
   is up, since that is the only time it does anything. */
.chatinput .prompt { color: var(--faint); font-family: var(--mono); }
body.chatting .chatinput .prompt { color: var(--sun); cursor: move; }

.chatinput input {
  flex: 1;
  min-width: 0;
  font: inherit;
  font-size: 0.8rem;
  padding: 0.15rem 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  outline: none;
}

.chatinput input::placeholder { color: #5d6b80; }
