:root {
  --panel: rgba(8, 16, 24, 0.62);
  --edge: rgba(150, 190, 220, 0.22);
  --ink: #cfe4f2;
  --dim: #7f97a8;
  --live: #6fe08a;
  --off: #ff7a6b;
  --warn: #ffce54;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  background: #0a1420;
  color: var(--ink);
  font: 13px/1.4 ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
}

#scene {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.hud {
  position: fixed;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  padding: 10px 12px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  min-width: 180px;
  user-select: none;
  pointer-events: none;
}

.hud-tl { top: 14px; left: 14px; }
.hud-tr { top: 14px; right: 14px; }
.hud-bl { bottom: 40px; left: 14px; }

.site { font-weight: 600; margin-bottom: 6px; letter-spacing: 0.02em; }
.hud-title { font-weight: 600; margin-bottom: 8px; text-transform: lowercase; }

.row {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 1px 0;
}
.k { color: var(--dim); }
.v { color: var(--ink); text-align: right; }
.dim { color: var(--dim); font-weight: 400; }

.health { text-transform: uppercase; letter-spacing: 0.04em; font-size: 11px; }
.health.live { color: var(--live); }
.health.offline { color: var(--off); }
.health.mock, .health.fallback, .health.scraped { color: var(--warn); }

.wx-grid { display: flex; gap: 12px; align-items: flex-start; }
.wx-readout { flex: 1; min-width: 140px; }

.vane { text-align: center; }
.vane-ring { fill: none; stroke: var(--edge); stroke-width: 1.5; }
.vane-card { fill: var(--dim); font-size: 11px; text-anchor: middle; dominant-baseline: middle; }
.vane-arrow { transition: transform 0.6s ease; transform-origin: 0 0; }
.vane-arrow polygon { fill: var(--warn); }
.vane-label { margin-top: 2px; color: var(--ink); font-size: 12px; }

/* The centred banner only. Scoped by id because "offline" is also a health
   value, and _health() puts it on a row's span — which used to pick this up and
   turn the vessels reading into a red box in the middle of the screen. */
#offline {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  padding: 18px 30px;
  background: rgba(30, 8, 8, 0.6);
  border: 1px solid rgba(255, 122, 107, 0.4);
  border-radius: 10px;
  z-index: 10;
  pointer-events: none;
}
.offline-title { color: var(--off); font-size: 20px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; }
.offline-sub { color: var(--dim); margin-top: 4px; }

/* The terrain having failed is a different thing from the feed being down, and
   both can be true at once, so this sits below the feed banner rather than on
   top of it. Same reason as #offline for being scoped by id. */
#terrain-fail {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, 90px);
  text-align: center;
  padding: 14px 26px;
  background: rgba(30, 8, 8, 0.6);
  border: 1px solid rgba(255, 122, 107, 0.4);
  border-radius: 10px;
  z-index: 10;
  pointer-events: none;
}

/* --- The buttons -----------------------------------------------------------
   On a desktop these are what they always were: words, in a panel in the bottom
   right corner, behind the hamburger with the readouts. The phone rules are in
   the media query at the foot of this file and they stay there. */
.views {
  position: fixed;
  bottom: 40px;
  right: 14px;
  z-index: 6;
  display: none;
  flex-direction: column;
  gap: 6px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  padding: 10px 12px;
  min-width: 180px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  user-select: none;
}
body.hud-visible .views { display: flex; }
.view-icons { display: flex; flex-direction: column; gap: 6px; }
.icon-btn, .mode-pill {
  font: 12px/1.4 ui-monospace, Menlo, Consolas, monospace;
  width: 100%;
  color: var(--ink);
  background: rgba(150, 190, 220, 0.08);
  border: 1px solid var(--edge);
  border-radius: 5px;
  padding: 4px 8px;
  text-align: center;
  cursor: pointer;
}
.mode-pill { color: var(--warn); }
.icon-btn:hover, .mode-pill:hover { background: rgba(150, 190, 220, 0.2); }
/* A button for something that did not start. Greyed rather than taken away, so
   it is plain that the thing exists and is not here. */
.icon-btn.off { opacity: 0.35; cursor: default; }
.icon-btn.off:hover { background: rgba(150, 190, 220, 0.08); }
.icon-btn.on { color: var(--live); }
/* The words carry it here, so the drawings stand down. */
.icon-btn svg { display: none; }
.btn-short { display: none; }
/* Nothing on a desktop to tilt. */
#gyro-btn { display: none; }

/* Only filled when tilting was asked for and refused, and it says why. Stands
   over the button that was pressed rather than in the middle, so it is plain
   which one it is answering for. */
.gyro-note {
  position: fixed;
  right: 12px;
  bottom: 116px;
  z-index: 7;
  max-width: calc(100vw - 24px);
  color: var(--warn);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 12px;
  pointer-events: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.gyro-note.hidden { display: none; }

/* The card for a clicked ship or aircraft. Down the left, where nothing else
   lives, and it is not a .hud: the corner panels are off on a phone until the
   hamburger is pressed, and a card you asked for by picking the thing out of the
   water has to come up on its own. */
.track {
  position: fixed;
  left: 14px;
  top: 60px;
  z-index: 7;
  width: 300px;
  max-width: calc(100vw - 28px);
  max-height: 70vh;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  padding: 10px 12px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-size: 12px;
}
.track-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 6px;
}
.track-title { font-weight: 600; letter-spacing: 0.02em; }
.track-close {
  font: inherit; font-size: 16px; line-height: 1;
  color: var(--dim); background: none; border: none;
  padding: 0 2px; cursor: pointer;
}
.track-close:hover { color: var(--ink); }

/* The list of what is out there. Down the right, opposite the card, so picking
   one and reading it do not happen in the same place. */
.tracks {
  position: fixed;
  right: 14px;
  top: 60px;
  z-index: 7;
  width: 240px;
  max-width: calc(100vw - 28px);
  max-height: 60vh;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  padding: 10px 12px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-size: 12px;
}
.tracks.hidden { display: none; }
#tracks-rows { overflow-y: auto; }
.tracks-group {
  color: var(--dim);
  text-transform: lowercase;
  margin: 6px 0 2px;
}
.tracks-row {
  font: inherit;
  display: flex;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  min-height: 30px;
  align-items: center;
  padding: 2px 4px;
  background: none;
  border: 1px solid transparent;
  border-radius: 4px;
  cursor: pointer;
  text-align: left;
}
.tracks-row:hover { background: rgba(255, 255, 255, 0.06); }
.tracks-row.on { border-color: var(--edge); background: rgba(255, 255, 255, 0.08); }

/* The helm readout. Above the tide panel on the left; bottom right is taken by
   the buttons and bottom centre by the hint. */
.hud-helm { bottom: 156px; left: 14px; min-width: 168px; }
.hud-helm.hidden { display: none; }
.helm-note { color: var(--dim); font-size: 11px; margin-top: 4px; }

/* The aim slider, in live mode. Not a .hud: it is the one thing live mode cannot
   do without, so it does not go behind the hamburger. Sits above the hint bar. */
.trim {
  position: fixed;
  bottom: 46px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 6px 12px;
  z-index: 6;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.trim.hidden { display: none; }
/* Clear of the aim trim, which live mode puts at the same place.
   The clock does go behind the hamburger, unlike the aim trim above it. It sits
   across the middle of the window and the window is what the page is for, and
   the hour is a thing you set once and then want out of the way. Same rule as
   the readouts: not the .hidden ones, so hiding it from script still holds. */
.clock { bottom: 82px; display: none; }
body.hud-visible .clock:not(.hidden) { display: flex; }
.trim-label { color: var(--dim); font-size: 12px; white-space: nowrap; }
.trim-label span { color: var(--ink); }
.trim-range { width: 180px; accent-color: var(--live); }
/* The day is twelve hours either side of now, and across 180 px that is eight
   minutes to the pixel, which is not a control. Width is the only thing that
   buys resolution back, so the clock bar takes what the window has and the
   slider takes what is left of the bar: about three minutes to the pixel on a
   desktop. Under that the arrow keys move it a minute a press. The aim slider
   keeps its 180 px — thirty degrees either way fits there.
   The label is pinned because it grows from "now" to "14:32 (+3h12)", and a
   label that grows takes width off the slider and slides the thumb out from
   under the finger holding it. */
.clock { width: min(720px, calc(100vw - 24px)); }
.clock .trim-label { min-width: 132px; }
.clock .trim-range { width: auto; flex: 1; }

/* Back to the real hour. Sits in the clock bar, not with the view buttons. */
.clock-now {
  font: 12px/1.4 ui-monospace, Menlo, Consolas, monospace;
  color: var(--dim);
  background: rgba(150, 190, 220, 0.08);
  border: 1px solid var(--edge);
  border-radius: 5px;
  padding: 2px 8px;
  cursor: pointer;
}
.clock-now:hover { background: rgba(150, 190, 220, 0.2); color: var(--ink); }

.fly-hint {
  position: fixed;
  bottom: 14px;
  left: 50%;
  transform: translateX(-50%);
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 12px;
  pointer-events: none;
}

/* The stick. Placed at the thumb, so left and top are set from script; the
   translate is what centres the ring on that point. Nothing here takes a
   pointer event — the canvas underneath owns them all, which is what lets one
   thumb work the stick while another drags the view. */
.stick {
  position: fixed;
  width: 116px;
  height: 116px;
  margin: -58px 0 0 -58px;
  border: 1px solid var(--edge);
  border-radius: 50%;
  background: rgba(8, 16, 24, 0.35);
  pointer-events: none;
  z-index: 6;
}
.stick-knob {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 46px;
  height: 46px;
  border: 1px solid var(--edge);
  border-radius: 50%;
  background: rgba(150, 190, 220, 0.22);
}
.stick.hidden { display: none; }

/* A drag has to reach the canvas rather than scroll or zoom the page. */
#scene { touch-action: none; }

/* The camera readout, in editing mode. Top centre, clear of both HUD corners. */
.cam-aim {
  position: fixed;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 7;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 5px 12px;
  font-size: 12px;
  pointer-events: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.cam-aim.hidden { display: none; }
/* What S puts up: the block to paste into the source, kept selectable in case
   the clipboard is not available, which on plain http it is not. */
.cam-aim.saved {
  white-space: pre;
  text-align: left;
  pointer-events: auto;
  user-select: text;
  -webkit-user-select: text;
}

.vessel-tip {
  position: fixed;
  z-index: 6;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  padding: 6px 9px;
  font-size: 12px;
  pointer-events: none;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  max-width: 260px;
}
.tip-row { display: flex; justify-content: space-between; gap: 16px; padding: 1px 0; }
.tip-k { color: var(--dim); }
.tip-v { color: var(--ink); text-align: right; }
.tip-stale { color: var(--off); margin-top: 3px; text-transform: uppercase; font-size: 11px; }

.hidden { display: none; }

/* The mode chooser. Shown first, and again on demand. */
.chooser {
  position: fixed; inset: 0; z-index: 20;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 12, 18, 0.72);
}
/* .hidden is declared earlier and is only a class, so this display would beat
   it on source order alone and the chooser would never go away. */
.chooser.hidden { display: none; }
.chooser-card {
  background: var(--panel); border: 1px solid var(--edge); border-radius: 12px;
  padding: 22px 26px; text-align: center; pointer-events: auto;
}
.chooser-title { color: var(--ink); font-size: 20px; letter-spacing: 0.06em; text-transform: uppercase; }
.chooser-sub { color: var(--dim); margin: 4px 0 14px; }
.chooser-btns { display: flex; flex-direction: column; gap: 6px; min-width: 210px; }
.chooser-btn {
  font: 13px/1.6 ui-monospace, Menlo, Consolas, monospace;
  color: var(--ink); background: rgba(255, 255, 255, 0.05);
  border: 1px solid var(--edge); border-radius: 6px; padding: 7px 12px;
  cursor: pointer; text-align: left;
}
.chooser-btn:hover { background: rgba(255, 255, 255, 0.12); }
.chooser-btn .cb-note { color: var(--dim); float: right; }
/* Only filled when a mode is refused, and it says why. */
.chooser-note { color: var(--warn); font-size: 12px; margin-top: 10px; max-width: 240px; }

/* Overview map. Sits above the tide readout, bottom left. */
.overview {
  position: fixed;
  left: 14px;
  bottom: 148px;
  width: 260px;
  height: 260px;
  background: rgba(8, 16, 24, 0.78);
  border: 1px solid var(--edge);
  border-radius: 8px;
  pointer-events: none;
  z-index: 5;
}
.overview.hidden { display: none; }

/* --- Hamburger, at every width ---------------------------------------------
   Four readout panels and a button stack sit in the four corners, and on any
   screen they cover most of the water. This page is a window, so the window wins:
   the panels are off until the button is pressed. Not a phone rule — a desktop is
   where they blocked the most. */
.hamburger {
  display: block;
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 1000;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--edge);
  font-size: 24px;
  width: 44px;
  height: 44px;
  border-radius: 6px;
  cursor: pointer;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.hud { display: none; }
.visitors-btn {
  position: fixed; top: 8px; left: 60px; z-index: 1000;
  min-height: 44px; padding: 8px 12px; font: inherit;
  color: var(--ink); background: var(--panel); border: 1px solid var(--edge);
  border-radius: 6px; cursor: pointer; backdrop-filter: blur(6px);
}
.visitors {
  position: fixed; top: 60px; left: 8px; z-index: 1001;
  width: 330px; max-width: calc(100vw - 16px); max-height: calc(100dvh - 76px);
  display: flex; flex-direction: column; padding: 12px;
  background: rgba(8, 16, 24, 0.96); border: 1px solid var(--edge); border-radius: 8px;
}
.visitors .track-close { min-width: 44px; min-height: 44px; }
#visitors-status { margin: 0 0 10px; color: var(--dim); }
#visitors-rows { list-style: none; margin: 0; padding: 0; overflow-y: auto; }
#visitors-rows li { display: flex; gap: 10px; align-items: center; border-top: 1px solid var(--edge); }
#visitors-rows li > :first-child { flex: 1; }
#visitors-rows button {
  min-height: 44px; padding: 6px 10px; margin: 4px 0; font: inherit;
  color: var(--ink); background: rgba(150, 190, 220, 0.12);
  border: 1px solid var(--edge); border-radius: 5px; cursor: pointer;
}
.visitors button:hover, .visitors-btn:hover { background: rgba(80, 140, 180, 0.5); }
/* Not the .hidden ones. The helm readout is a .hud and is hidden unless you are
   in the boat, and an unqualified reveal put a panel of dashes on the screen. */
body.hud-visible .hud:not(.hidden) { display: block; }
body.hud-visible .hamburger { background: rgba(80, 140, 180, 0.5); }
/* The button sits in the top left corner and so does the first panel. Move the
   panel down out from under it rather than the button off the corner. */
body.hud-visible .hud-tl { top: 60px; }
body.hud-visible .track { top: 230px; }
body.hud-visible .tracks { top: 270px; }

/* --- Everything steps back --------------------------------------------------
   The view is what the page is for, so what is drawn over it fades once nothing
   has been touched for a few seconds. It stays where it is and stays pressable:
   the tap that brings it back is the same tap that works the button it landed
   on, which is what a map app does and what a hand expects. */
.views, .hamburger, .trim, .clock, .fly-hint { transition: opacity 0.5s ease; }
body.chrome-dim .views,
body.chrome-dim .hamburger,
body.chrome-dim .trim,
body.chrome-dim .clock,
body.chrome-dim .fly-hint { opacity: 0.22; }

/* --- A phone ----------------------------------------------------------------
   Everything below here is the phone and nothing above it is. Six lines of text
   down the corner cover the water on a small screen, and how you are getting
   about is the one control a stranger has to find — a hamburger in the far
   corner from a right thumb is not where it goes. So the words become drawings,
   the group leaves the hamburger, and the mode button is the widest thing on the
   glass. Targets are 44 px, the smallest a thumb hits without looking.

   Gated on a coarse pointer as well as on width, because a laptop dragged narrow
   is still a laptop and wants its words. */
@media (max-width: 700px), (pointer: coarse) {
  .views {
    display: flex;
    align-items: flex-end;
    bottom: 12px;
    right: 12px;
    gap: 8px;
    min-width: 0;
    max-width: calc(100vw - 24px);
    padding: 0;
    background: none;
    border: 0;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .view-icons { flex-direction: row; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
  .icon-btn {
    width: 44px;
    height: 44px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--panel);
    border-radius: 10px;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
  }
  .icon-btn:hover { background: var(--panel); }
  .icon-btn.off:hover { background: var(--panel); }
  .icon-btn.on { border-color: rgba(111, 224, 138, 0.5); }
  .icon-btn svg {
    display: block;
    width: 22px;
    height: 22px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.6;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  #gyro-btn { display: flex; }
  .btn-label { display: none; }
  .btn-short { display: inline; }
  .snd-slash { display: none; }
  .icon-btn.muted .snd-wave { display: none; }
  .icon-btn.muted .snd-slash { display: inline; }
  .mode-pill {
    width: auto;
    min-height: 44px;
    padding: 0 24px;
    font-size: 13px;
    letter-spacing: 0.1em;
    border-radius: 22px;
    background: var(--panel);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
  }
  .mode-pill:hover { background: var(--panel); }

  /* The button group stands 96 px off the bottom right and the sliders and the
     hint are centred on the bottom. On a wide screen they never meet. Here there
     is no width to share, so the centred ones stack above the buttons. */
  /* A hand on the glass covers what it touches and everything below it. So the
     card goes to the top, clear of the buttons, the list, and the thumb; the
     list stays at the bottom where the thumb already is. Both are told what
     height they may have so they cannot grow into each other. */
  .track {
    left: 8px; right: 8px; top: 60px;
    width: auto; max-width: none;
    max-height: 34vh;
  }
  .tracks {
    left: 8px; right: 8px; top: auto; bottom: 170px;
    width: auto; max-width: none;
    max-height: 30vh;
  }
  /* The list stays at the bottom whatever the readouts are doing. */
  body.hud-visible .tracks { top: auto; }
  .tracks-row { min-height: 40px; }

  .fly-hint { bottom: 116px; max-width: calc(100vw - 24px); }
  .trim { bottom: 158px; }
  .clock { bottom: 200px; }
  .trim-range { width: 130px; }
}

/* --- The keys ---------------------------------------------------------------
   A mark in the top right corner and the list under it on hover. The list is
   the only place on the page that says what any key does. */
.keys-btn {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 1000;
  width: 44px;
  height: 44px;
  font: 24px/1 ui-monospace, Menlo, Consolas, monospace;
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 6px;
  cursor: help;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.keys {
  display: none;
  position: fixed;
  top: 60px;
  right: 8px;
  z-index: 1001;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--edge);
  border-radius: 8px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  user-select: none;
}
/* The list stays up while the pointer is on the mark or on the list itself, so
   it can be read on the way down. */
.keys-btn:hover + .keys,
.keys:hover { display: block; }
.keys-row { display: flex; gap: 10px; align-items: baseline; }
.keys-k {
  flex: none;
  width: 32px;
  color: var(--ink);
  font-family: ui-monospace, Menlo, Consolas, monospace;
}
.keys-v { color: var(--dim); }
/* Same reason the top-left panel steps down under the hamburger. */
body.hud-visible .hud-tr { top: 60px; }
