/* One stylesheet, no framework, no build step.
 *
 * Light and dark both come from tokens on :root, with dark redefining only
 * the tokens. Nothing gets its only definition inside a media query, so a
 * browser that reports no preference still gets a complete palette. */

:root {
  --bg: #f6f7f9;
  --panel: #ffffff;
  --panel-2: #f0f2f5;
  --ink: #16191d;
  --ink-dim: #5b6472;
  --line: #dfe3e8;
  --accent: #1f6feb;
  --accent-ink: #ffffff;
  --ok: #1a7f37;
  --ok-bg: #e6f4ea;
  --warn: #9a6700;
  --warn-bg: #fff8e1;
  --bad: #b42318;
  --bad-bg: #fdeceb;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --radius: 10px;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14171b;
    --panel: #1c2025;
    --panel-2: #23282f;
    --ink: #e6e9ee;
    --ink-dim: #9aa4b2;
    --line: #2d333b;
    --accent: #4c8dff;
    --accent-ink: #0b1220;
    --ok: #4ac26b;
    --ok-bg: #14301d;
    --warn: #d4a72c;
    --warn-bg: #33290d;
    --bad: #ff6b5e;
    --bad-bg: #3a1614;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

code, pre, .mono { font-family: var(--mono); font-size: 0.9em; }

pre {
  background: var(--panel-2);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 14px;
  overflow-x: auto;
}

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

.top {
  display: flex; align-items: center; gap: 20px;
  padding: 12px 22px;
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  flex-wrap: wrap;
}
.brand { font-weight: 650; color: var(--ink); }
.brand span { color: var(--ink-dim); font-weight: 400; }
.topnav { display: flex; gap: 16px; margin-right: auto; }
.topnav a { color: var(--ink-dim); padding: 4px 0; }
.topnav a.on { color: var(--ink); border-bottom: 2px solid var(--accent); }
.whoami { display: flex; align-items: center; gap: 12px; color: var(--ink-dim); }

.wrap { max-width: 1020px; margin: 26px auto 60px; padding: 0 22px; }
.wrap.wide { max-width: 1400px; }

.foot {
  display: flex; gap: 18px; flex-wrap: wrap;
  max-width: 1020px; margin: 0 auto 40px; padding: 0 22px;
  color: var(--ink-dim); font-size: 13px;
}

/* --- blocks ------------------------------------------------------------ */

h1 { font-size: 25px; margin: 0 0 6px; }
h2 { font-size: 19px; margin: 30px 0 10px; }
h3 { font-size: 15px; margin: 20px 0 6px; }
.lede { color: var(--ink-dim); margin: 0 0 22px; max-width: 68ch; }

.panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 18px 20px;
  margin-bottom: 16px;
}
.panel > :first-child { margin-top: 0; }
.grid { display: grid; gap: 16px; }
.grid.two { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid.three { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.note {
  border: 1px solid var(--line);
  border-left-width: 4px;
  border-radius: var(--radius);
  padding: 12px 16px;
  margin-bottom: 18px;
}
.note.info { background: var(--panel); border-left-color: var(--accent); }
.note.good { background: var(--ok-bg); border-left-color: var(--ok); }
.note.warn { background: var(--warn-bg); border-left-color: var(--warn); }
.note.bad  { background: var(--bad-bg); border-left-color: var(--bad); }

.badge {
  display: inline-block; padding: 1px 8px; border-radius: 999px;
  font-size: 12px; font-weight: 600; border: 1px solid transparent;
}
.badge.ok { background: var(--ok-bg); color: var(--ok); }
.badge.warn { background: var(--warn-bg); color: var(--warn); }
.badge.crit, .badge.bad { background: var(--bad-bg); color: var(--bad); }
.badge.unknown, .badge.pending {
  background: var(--panel-2); color: var(--ink-dim); border-color: var(--line);
}

.dim { color: var(--ink-dim); }
.small { font-size: 13px; }
.right { text-align: right; }
.nowrap { white-space: nowrap; }

/* --- the setup checklist ---------------------------------------------- */

.steps { list-style: none; padding: 0; margin: 0; }
.step {
  display: grid; grid-template-columns: 34px 1fr auto; gap: 14px;
  align-items: start;
  padding: 16px 0; border-top: 1px solid var(--line);
}
.step:first-child { border-top: 0; }
.step .dot {
  width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 700;
  background: var(--panel-2); color: var(--ink-dim);
  border: 1px solid var(--line);
}
.step.done .dot { background: var(--ok-bg); color: var(--ok); border-color: var(--ok); }
.step.current .dot { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.step.current { background: linear-gradient(90deg, var(--panel-2), transparent); }
.step.blocked { opacity: 0.62; }
.step h3 { margin: 0 0 2px; }
.step p { margin: 0; color: var(--ink-dim); font-size: 14px; }
.step .why { font-size: 13px; margin-top: 4px; }

.bar { height: 6px; background: var(--panel-2); border-radius: 999px; overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--accent); }

/* --- forms ------------------------------------------------------------ */

label { display: block; font-weight: 600; margin: 14px 0 4px; }
label .hint { display: block; font-weight: 400; color: var(--ink-dim); font-size: 13px; }

input[type=text], input[type=email], input[type=password], input[type=number],
select, textarea {
  width: 100%; padding: 9px 11px;
  background: var(--bg); color: var(--ink);
  border: 1px solid var(--line); border-radius: 8px;
  font: inherit;
}
textarea { min-height: 92px; font-family: var(--mono); font-size: 13px; }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}

button, .btn {
  display: inline-block; padding: 9px 16px;
  background: var(--accent); color: var(--accent-ink);
  border: 1px solid transparent; border-radius: 8px;
  font: inherit; font-weight: 600; cursor: pointer;
}
button:hover, .btn:hover { filter: brightness(1.08); text-decoration: none; }
button[disabled] { opacity: 0.55; cursor: progress; }
.btn.sub, button.sub {
  background: var(--panel-2); color: var(--ink); border-color: var(--line);
}
.btn.danger, button.danger { background: var(--bad); color: #fff; }
button.linkish {
  background: none; border: 0; color: var(--accent);
  padding: 0; font-weight: 400; cursor: pointer;
}
button.linkish:hover { text-decoration: underline; }
form.inline { display: inline; }
.actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 18px; }

/* A field whose head is fixed: the granted subdomain prefix, printed rather
   than typed. One box visually, so it reads as the whole hostname it will
   become, with only the editable half actually editable. */
.affixed { display: flex; align-items: stretch; }
.affixed .affix {
  display: flex; align-items: center; padding: 9px 4px 9px 11px;
  background: var(--panel-2); color: var(--ink-dim);
  border: 1px solid var(--line); border-right: 0;
  border-radius: 8px 0 0 8px; white-space: nowrap;
}
.affixed input[type=text] { border-radius: 0 8px 8px 0; min-width: 0; }

fieldset { border: 1px solid var(--line); border-radius: var(--radius); padding: 6px 16px 16px; }
legend { font-weight: 600; padding: 0 6px; }

/* Radio cards: a kind or a network is a decision that needs a sentence of
   explanation next to it, which a bare <select> has nowhere to put. */
.choices { display: grid; gap: 10px; margin-top: 8px; }
.choice {
  display: grid; grid-template-columns: 22px 1fr; gap: 12px;
  padding: 12px 14px; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--panel-2); cursor: pointer;
}
.choice:has(input:checked) { border-color: var(--accent); background: var(--panel); }
.choice input { margin-top: 3px; }
.choice strong { display: block; }
.choice span { color: var(--ink-dim); font-size: 13px; }

/* --- tables ----------------------------------------------------------- */

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--line); }
th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-dim); }
tbody tr:hover { background: var(--panel-2); }
.table-scroll { overflow-x: auto; }

dl.kv { display: grid; grid-template-columns: minmax(120px, auto) 1fr; gap: 6px 18px; margin: 0; }
dl.kv dt { color: var(--ink-dim); }
dl.kv dd { margin: 0; }

/* --- console ---------------------------------------------------------- */

/* A *definite* height, not just a minimum. noVNC builds its own
   `<div style="height:100%">` inside this element and measures it to decide
   the scale; a percentage height inside a parent whose own height is `auto`
   resolves to zero, so it computed scale 0, set the canvas to 0x0, and drew
   every frame into something the browser rendered at no size at all. The
   console said "connected", the pixels arrived, and the screen was blank.
   xterm.js measures this element directly, which is why container consoles
   never showed the fault. */
#screen {
  background: #000; border: 1px solid var(--line); border-radius: var(--radius);
  height: 70vh; min-height: 460px; overflow: hidden;
}
.console-bar { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }

/* --- job log ---------------------------------------------------------- */

#joblog {
  background: #0b0e12; color: #d7dee8;
  border-radius: var(--radius); padding: 14px 16px;
  min-height: 140px; max-height: 420px; overflow: auto;
  font-family: var(--mono); font-size: 13px; white-space: pre-wrap;
}
.spinner {
  display: inline-block; width: 14px; height: 14px;
  border: 2px solid var(--line); border-top-color: var(--accent);
  border-radius: 50%; animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation: none; } }

@media (max-width: 640px) {
  .step { grid-template-columns: 26px 1fr; }
  .step > .right { grid-column: 2; }
  .whoami { width: 100%; justify-content: flex-end; }
}

/* --- the network map -------------------------------------------------- */
/* One colour per provenance, used by both the badges and the occupancy
   strip, because the strip is only readable if a cell means the same thing
   as the badge on the row below it. */

.badge.k-static        { background: var(--ok-bg);   color: var(--ok); }
.badge.k-dhcp-reserved { background: var(--panel-2); color: var(--accent); border-color: var(--accent); }
.badge.k-dhcp-dynamic  { background: var(--warn-bg); color: var(--warn); }
.badge.k-dhcp-external { background: var(--panel-2); color: var(--ink-dim); border-color: var(--line); }
.badge.k-vpn-assigned  { background: var(--panel-2); color: var(--ink); border-color: var(--line); }
.badge.k-observed      { background: var(--bad-bg);  color: var(--bad); }

.strip { display: flex; flex-wrap: wrap; gap: 1px; margin: 10px 0 0; }
.cell {
  width: 10px; height: 10px; border-radius: 2px;
  background: var(--panel-2); border: 1px solid var(--line);
}
.cell.k-static        { background: var(--ok);     border-color: transparent; }
.cell.k-dhcp-reserved { background: var(--accent); border-color: transparent; }
.cell.k-dhcp-dynamic  { background: var(--warn);   border-color: transparent; }
.cell.k-dhcp-external { background: var(--ink-dim);border-color: transparent; }
.cell.k-vpn-assigned  { background: var(--ink-dim);border-color: transparent; }
.cell.k-observed      { background: var(--bad);    border-color: transparent; }
/* free, but not equally free: a cell in the pool will be handed out by the
   DHCP server, and one in the portal's range belongs to the next guest
   somebody creates. */
.cell.free.pool   { border-color: var(--warn); }
.cell.free.portal { border-style: dashed; border-color: var(--accent); }
