/* KH Connect — vehicle console.
 *
 * Self-contained: no CDN, no build step, no webfont. The dev server has to
 * start anywhere python3 does, and a stylesheet that needed npm would defeat
 * that. Dark-first, because this screen lives on a bench next to the car.
 */

:root {
  --bg:#0c0e12; --bg2:#0f1216; --fg:#e8eaf0; --muted:#8b94a3; --faint:#5d6673;
  --line:#1f242c; --line2:#2b3239; --card:#13171d; --card2:#171c23;
  --ok:#3ecf8e; --bad:#ff6b5e; --warn:#e8b23c; --info:#6aa9ff; --accent:#6aa9ff;
  --ok-bg:rgba(62,207,142,.12); --bad-bg:rgba(255,107,94,.12);
  --warn-bg:rgba(232,178,60,.12); --info-bg:rgba(106,169,255,.12);
  --mono:ui-monospace,"SF Mono",Menlo,Consolas,"Liberation Mono",monospace;
  --sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --sidebar:264px;
  --radius:10px;
}
@media (prefers-color-scheme:light) {
  :root {
    --bg:#f5f6f8; --bg2:#eef0f3; --fg:#12151a; --muted:#5f6875; --faint:#868f9d;
    --line:#e0e3e8; --line2:#d2d7de; --card:#fff; --card2:#f7f8fa;
    --ok:#0f7b3f; --bad:#b3261e; --warn:#8a6100; --info:#1f4fd8; --accent:#1f4fd8;
    --ok-bg:rgba(15,123,63,.10); --bad-bg:rgba(179,38,30,.10);
    --warn-bg:rgba(138,97,0,.10); --info-bg:rgba(31,79,216,.10);
  }
}

* { box-sizing:border-box; }
html,body { height:100%; }
body {
  margin:0; background:var(--bg); color:var(--fg);
  font:14px/1.5 var(--sans); -webkit-font-smoothing:antialiased;
}
a { color:var(--accent); }

/* ───────────────────────── header ───────────────────────── */

header {
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:10px 16px; border-bottom:1px solid var(--line);
  background:var(--bg2); position:sticky; top:0; z-index:20;
}
.brand { display:flex; align-items:center; gap:9px; font-weight:650; font-size:15px; letter-spacing:-.01em; }
.brand .mark {
  width:22px; height:22px; border-radius:6px; flex:none;
  background:linear-gradient(135deg,var(--accent),var(--ok));
  display:grid; place-items:center; color:#04070b; font:700 11px/1 var(--sans);
}
.grow { flex:1 1 auto; }

.livedot { width:8px; height:8px; border-radius:50%; background:var(--faint); flex:none; }
.livedot.fresh { background:var(--ok); box-shadow:0 0 0 3px var(--ok-bg); }
.livedot.stale { background:var(--warn); box-shadow:0 0 0 3px var(--warn-bg); }
.livedot.dead  { background:var(--bad); box-shadow:0 0 0 3px var(--bad-bg); }

.hdrstat { display:flex; align-items:center; gap:7px; font:11px/1 var(--mono); color:var(--muted); }

/* ───────────────────────── controls ───────────────────────── */

button {
  font:inherit; padding:6px 11px; border:1px solid var(--line2); border-radius:8px;
  background:var(--card); color:var(--fg); cursor:pointer; line-height:1.3;
}
button:hover { border-color:var(--accent); }
button:active { transform:translateY(1px); }
button.sm { font:11px/1.3 var(--mono); padding:5px 9px; }
button.primary { background:var(--accent); border-color:var(--accent); color:#04070b; font-weight:600; }
input[type=text], input[type=search], select {
  font:12px var(--mono); padding:6px 10px; border:1px solid var(--line2);
  border-radius:8px; background:var(--bg); color:var(--fg); min-width:0;
}
input:focus, select:focus, button:focus-visible {
  outline:2px solid var(--accent); outline-offset:1px;
}

/* ───────────────────────── layout ───────────────────────── */

.shell { display:grid; grid-template-columns:var(--sidebar) minmax(0,1fr); min-height:calc(100vh - 45px); }
@media (max-width:900px) { .shell { grid-template-columns:1fr; } }

aside {
  border-inline-end:1px solid var(--line); padding:14px 12px;
  background:var(--bg2); display:flex; flex-direction:column; gap:8px;
}
@media (max-width:900px) { aside { border-inline-end:0; border-bottom:1px solid var(--line); } }

.side-h {
  font:10px/1 var(--sans); text-transform:uppercase; letter-spacing:.1em;
  color:var(--faint); font-weight:700; padding:2px 4px 6px;
  display:flex; align-items:center; justify-content:space-between; gap:8px;
}

.vcard {
  text-align:start; width:100%; display:block; padding:10px 11px;
  border-radius:var(--radius); border:1px solid var(--line); background:var(--card);
  cursor:pointer;
}
.vcard:hover { border-color:var(--line2); }
.vcard[aria-current=true] { border-color:var(--accent); background:var(--card2); }
.vcard .vid { font:12px/1.3 var(--mono); font-weight:600; display:flex; align-items:center; gap:7px; }
.vcard .vmeta { margin-top:7px; display:flex; align-items:center; gap:8px; color:var(--muted); font:11px/1 var(--mono); }

main { padding:16px; min-width:0; display:flex; flex-direction:column; gap:14px; }

.titlebar { display:flex; align-items:center; gap:10px; flex-wrap:wrap; }
.titlebar h1 { font-size:19px; margin:0; font-weight:660; letter-spacing:-.02em; font-family:var(--mono); }

/* ───────────────────────── tabs ───────────────────────── */

.tabs {
  display:flex; gap:2px; border-bottom:1px solid var(--line);
  overflow-x:auto; scrollbar-width:none;
}
.tabs::-webkit-scrollbar { display:none; }
.tab {
  border:0; background:none; border-radius:0; color:var(--muted);
  padding:9px 13px; font-size:13px; font-weight:550; white-space:nowrap;
  border-bottom:2px solid transparent; margin-bottom:-1px;
}
.tab:hover { color:var(--fg); border-color:var(--line2); }
.tab[aria-selected=true] { color:var(--fg); border-bottom-color:var(--accent); }
.tab .count {
  font:10px/1 var(--mono); padding:2px 5px; border-radius:999px;
  background:var(--line); color:var(--muted); margin-inline-start:6px;
}
.tab .count.bad { background:var(--bad-bg); color:var(--bad); }
.tab .count.warn { background:var(--warn-bg); color:var(--warn); }

.panel { display:none; flex-direction:column; gap:14px; }
.panel.on { display:flex; }

/* ───────────────────────── cards ───────────────────────── */

.grid { display:grid; gap:12px; grid-template-columns:repeat(auto-fit,minmax(270px,1fr)); }
.card { background:var(--card); border:1px solid var(--line); border-radius:var(--radius); padding:13px 14px; min-width:0; }
.card > h2 {
  font:10px/1 var(--sans); text-transform:uppercase; letter-spacing:.1em;
  color:var(--faint); margin:0 0 11px; font-weight:700;
  display:flex; align-items:center; gap:8px;
}
.card > h2 .grow { font-size:0; }
.span2 { grid-column:span 2; }
@media (max-width:760px) { .span2 { grid-column:auto; } }

/* stat tiles */
.tiles { display:grid; gap:10px; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); }
.tile { border:1px solid var(--line); border-radius:var(--radius); padding:11px 12px; background:var(--card); }
.tile .lbl { font:10px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em; color:var(--faint); font-weight:700; }
.tile .val { margin-top:7px; font:18px/1.15 var(--mono); font-weight:600; word-break:break-word; }
.tile .val.sm { font-size:14px; }
.tile .sub { margin-top:4px; font:11px/1.35 var(--mono); color:var(--muted); word-break:break-word; }
.tile.ok  { border-color:color-mix(in srgb,var(--ok) 42%,var(--line)); }
.tile.bad { border-color:color-mix(in srgb,var(--bad) 42%,var(--line)); }
.tile.warn{ border-color:color-mix(in srgb,var(--warn) 42%,var(--line)); }

/* key/value table */
table.kv { width:100%; border-collapse:collapse; font:12px var(--mono); }
table.kv td { padding:5px 0; vertical-align:top; border-top:1px solid var(--line); }
table.kv tr:first-child td { border-top:0; }
table.kv td:first-child { color:var(--muted); width:44%; padding-inline-end:12px; font-size:11px; }
table.kv td:last-child { word-break:break-word; }

/* chips */
.chip {
  display:inline-flex; align-items:center; gap:5px; vertical-align:middle;
  font:10px/1 var(--mono); font-weight:600; padding:4px 8px; border-radius:999px;
  border:1px solid var(--line2); color:var(--muted); background:var(--card2);
  text-transform:uppercase; letter-spacing:.04em; white-space:nowrap;
}
.chip.ok   { color:var(--ok);   border-color:color-mix(in srgb,var(--ok) 45%,transparent);   background:var(--ok-bg); }
.chip.bad  { color:var(--bad);  border-color:color-mix(in srgb,var(--bad) 45%,transparent);  background:var(--bad-bg); }
.chip.warn { color:var(--warn); border-color:color-mix(in srgb,var(--warn) 45%,transparent); background:var(--warn-bg); }
.chip.info { color:var(--info); border-color:color-mix(in srgb,var(--info) 45%,transparent); background:var(--info-bg); }
.chip.lg { font-size:12px; padding:6px 11px; }

.ok{color:var(--ok)} .bad{color:var(--bad)} .warn{color:var(--warn)} .info{color:var(--info)}
.muted{color:var(--muted)} .faint{color:var(--faint)}
.empty { color:var(--faint); font-style:italic; font-size:12px; }
.note {
  font:11px/1.5 var(--sans); color:var(--muted); margin:11px 0 0;
  padding:9px 11px; border-radius:8px; background:var(--bg2);
  border:1px solid var(--line); border-inline-start:2px solid var(--line2);
}
.note.warn { border-inline-start-color:var(--warn); }
.note.info { border-inline-start-color:var(--info); }
.note b { color:var(--fg); font-weight:600; }
code { font:11px var(--mono); background:var(--bg2); border:1px solid var(--line);
       border-radius:5px; padding:1px 5px; word-break:break-all; }

/* ───────────────────────── signal bars ───────────────────────── */

.signal { display:flex; align-items:flex-end; gap:3px; height:22px; position:relative; }
.signal .b {
  width:5px; border-radius:1.5px; background:var(--line2);
  border:1px solid transparent; transition:background .2s;
}
.signal .b:nth-child(1){height:25%} .signal .b:nth-child(2){height:50%}
.signal .b:nth-child(3){height:75%} .signal .b:nth-child(4){height:100%}
.signal.good .b.on { background:var(--ok); }
.signal.fair .b.on { background:var(--warn); }
.signal.poor .b.on { background:var(--bad); }
/* Unregistered / unknown: nothing is lit, and a slash says so outright. */
.signal.none::after, .signal.unknown::after {
  content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(to top right,
    transparent calc(50% - 1px), var(--bad) calc(50% - 1px),
    var(--bad) calc(50% + 1px), transparent calc(50% + 1px));
  opacity:.85;
}
.signal.unknown::after { background:linear-gradient(to top right,
    transparent calc(50% - 1px), var(--faint) calc(50% - 1px),
    var(--faint) calc(50% + 1px), transparent calc(50% + 1px)); }
.signal.sm { height:14px; } .signal.sm .b { width:3px; gap:2px; }
.signal.lg { height:34px; gap:4px; } .signal.lg .b { width:8px; border-radius:2px; }

.radiorow { display:flex; align-items:center; gap:14px; flex-wrap:wrap; }
.radiobig { font:26px/1 var(--mono); font-weight:600; }
.radiobig small { font-size:12px; color:var(--muted); font-weight:400; }

/* generation badge (LTE / 3G / …) */
.gen {
  font:11px/1 var(--mono); font-weight:700; padding:5px 9px; border-radius:7px;
  border:1px solid var(--line2); color:var(--muted); background:var(--card2); letter-spacing:.04em;
}
.gen.g4 { color:var(--ok);   border-color:color-mix(in srgb,var(--ok) 45%,transparent);   background:var(--ok-bg); }
.gen.g5 { color:var(--info); border-color:color-mix(in srgb,var(--info) 45%,transparent); background:var(--info-bg); }
.gen.g3 { color:var(--warn); border-color:color-mix(in srgb,var(--warn) 45%,transparent); background:var(--warn-bg); }
.gen.g2 { color:var(--bad);  border-color:color-mix(in srgb,var(--bad) 45%,transparent);  background:var(--bad-bg); }
.gen.gx { border-style:dashed; }

/* ───────────────────────── attention list ───────────────────────── */

ul.issues { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:7px; }
ul.issues li {
  display:flex; gap:9px; align-items:flex-start; padding:9px 10px;
  border:1px solid var(--line); border-radius:8px; background:var(--card2); font-size:12.5px;
}
ul.issues li .ic { flex:none; width:16px; text-align:center; font-weight:700; font-family:var(--mono); }
ul.issues li .tx { min-width:0; }
ul.issues li .tx b { display:block; font-weight:600; font-size:12.5px; }
ul.issues li .tx span { color:var(--muted); font-size:11.5px; line-height:1.45; display:block; margin-top:2px; }
ul.issues li.bad  { border-inline-start:2px solid var(--bad); }
ul.issues li.warn { border-inline-start:2px solid var(--warn); }
ul.issues li.info { border-inline-start:2px solid var(--info); }
ul.issues li.ok   { border-inline-start:2px solid var(--ok); }

/* ───────────────────────── service list ───────────────────────── */

ul.svcs { list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:4px; }
ul.svcs li {
  display:flex; align-items:center; gap:9px; padding:6px 9px; border-radius:7px;
  font:11.5px var(--mono); background:var(--card2); border:1px solid var(--line);
}
ul.svcs li .nm { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
ul.svcs li .dot { width:7px; height:7px; border-radius:50%; flex:none; background:var(--faint); }
ul.svcs li.up .dot { background:var(--ok); }
ul.svcs li.down .dot { background:var(--bad); }

/* ───────────────────────── log viewer ───────────────────────── */

.logbar { display:flex; gap:7px; align-items:center; flex-wrap:wrap; margin-bottom:10px; }
.filters { display:flex; gap:4px; flex-wrap:wrap; }
.f {
  font:10px/1 var(--mono); font-weight:600; padding:5px 8px; border-radius:6px;
  border:1px solid var(--line2); background:var(--card2); color:var(--faint);
  cursor:pointer; text-transform:uppercase; letter-spacing:.04em;
}
.f[aria-pressed=true] { color:var(--fg); border-color:var(--accent); background:var(--info-bg); }
.f.lv-error[aria-pressed=true] { color:var(--bad);  border-color:var(--bad);  background:var(--bad-bg); }
.f.lv-warn[aria-pressed=true]  { color:var(--warn); border-color:var(--warn); background:var(--warn-bg); }
.f.lv-info[aria-pressed=true]  { color:var(--info); border-color:var(--info); background:var(--info-bg); }

.logwrap {
  border:1px solid var(--line); border-radius:8px; background:var(--bg);
  height:min(62vh,620px); overflow:auto; overflow-anchor:none;
}
.logline {
  display:grid; grid-template-columns:82px 52px 62px minmax(0,1fr);
  gap:9px; padding:3px 10px; font:11.5px/1.55 var(--mono);
  border-bottom:1px solid color-mix(in srgb,var(--line) 45%,transparent);
}
.logline:hover { background:var(--card2); }
.logline .t { color:var(--faint); }
.logline .lv { font-weight:700; font-size:10px; text-transform:uppercase; letter-spacing:.03em; padding-top:1px; }
.logline .ct { color:var(--muted); font-size:10px; padding-top:1px; }
.logline .m { color:var(--fg); white-space:pre-wrap; word-break:break-word; }
.logline.lv-error .m { color:var(--bad); }
.logline.lv-warn  .m { color:var(--warn); }
.logline.lv-debug .m { color:var(--muted); }
.logline .m .tr {
  display:block; margin-top:3px; padding:6px 8px; border-radius:6px;
  background:var(--card2); border:1px solid var(--line); color:var(--muted); font-size:11px;
}
@media (max-width:700px) { .logline { grid-template-columns:70px 46px minmax(0,1fr); } .logline .ct { display:none; } }

pre.out {
  margin:0; white-space:pre-wrap; word-break:break-word; font:11px/1.5 var(--mono);
  max-height:44vh; overflow:auto; background:var(--bg); border:1px solid var(--line);
  border-radius:8px; padding:10px;
}
.res { border-top:1px solid var(--line); padding-top:10px; margin-top:10px; }
.res:first-child { border-top:0; padding-top:0; margin-top:0; }
.res .meta { font:10px var(--mono); color:var(--muted); margin-bottom:6px; }

/* RTL: the mono values stay LTR, the chrome flips. */
[dir=rtl] table.kv td:first-child { text-align:right; }
[dir=rtl] .logline .m, [dir=rtl] .logline .t, [dir=rtl] code { direction:ltr; text-align:left; }
[dir=rtl] .vcard .vid, [dir=rtl] .titlebar h1 { direction:ltr; }

/* ───────────────────────── sign-in gate ─────────────────────────
 * A full-screen overlay rather than a separate page: the console is one
 * document served from disk, and a second page would be a second thing to
 * keep in sync with the stylesheet and the language toggle. It is `hidden`
 * until the first /admin/whoami answers, so a signed-in reload does not
 * flash a login form. */

/* Neither the console nor the gate is painted until /admin/whoami has
 * answered. Without this the page flashes whichever one is wrong for about a
 * frame on every load, which reads as a bug. */
body.booting header, body.booting .shell, body.booting footer { visibility:hidden; }

.gate {
  position:fixed; inset:0; z-index:50; display:flex;
  align-items:center; justify-content:center; padding:20px;
  background:var(--bg); overflow:auto;
}
.gate[hidden] { display:none; }
.gatebox {
  width:min(100%,360px); display:flex; flex-direction:column; gap:6px;
  background:var(--card); border:1px solid var(--line);
  border-radius:var(--radius); padding:22px;
}
.gatebox .brand { margin-bottom:2px; font-size:17px; }
.gatesub { margin:0 0 10px; color:var(--muted); font-size:13px; }
.gatebox label, .cmdform label {
  font:10px/1 var(--sans); text-transform:uppercase; letter-spacing:.08em;
  color:var(--faint); font-weight:700; margin-top:8px;
}
.gatebox input, .cmdform input, .cmdform select { width:100%; }
.gatebox button { margin-top:14px; width:100%; padding:9px; }
.gateerr {
  margin-top:10px; padding:9px 11px; border-radius:8px; font:12px var(--mono);
  background:var(--bad-bg); color:var(--bad);
  border:1px solid color-mix(in srgb,var(--bad) 35%,transparent);
}
.gateerr[hidden] { display:none; }
.gatenote { margin:14px 0 0; color:var(--faint); font-size:11.5px; line-height:1.5; }

/* The attribution. On the login screen, in the page footer, and therefore on
 * every screen a person can reach. */
.byline {
  margin:0; padding:14px 16px; text-align:center;
  font:11px/1.4 var(--sans); color:var(--faint); letter-spacing:.02em;
}
.gatebox .byline { padding:12px 0 0; border-top:1px solid var(--line); margin-top:14px; }

/* ───────────────────────── command form ───────────────────────── */

.cmdform { display:flex; flex-direction:column; gap:4px; margin-top:10px; }
.cmdform input[type=text], .cmdform input[type=password] {
  font:12px var(--mono);
}
.cmdrow {
  display:flex; gap:10px; align-items:flex-end; flex-wrap:wrap; margin-top:10px;
}
.cmdrow > div { flex:1 1 130px; min-width:0; display:flex; flex-direction:column; gap:4px; }
.cmdrow > div > label { margin-top:0; }
.cmdrow button { flex:0 0 auto; padding:8px 18px; }
input[type=password], input[type=email], input[type=number] {
  background:var(--bg2); color:var(--fg); border:1px solid var(--line2);
  border-radius:8px; padding:7px 9px; font:12px var(--sans);
}
button[disabled] { opacity:.5; cursor:not-allowed; }

.msg {
  margin-top:10px; padding:9px 11px; border-radius:8px;
  font:12px/1.5 var(--mono); word-break:break-word;
}
.msg.ok   { background:var(--ok-bg);   color:var(--ok);
            border:1px solid color-mix(in srgb,var(--ok) 35%,transparent); }
.msg.bad  { background:var(--bad-bg);  color:var(--bad);
            border:1px solid color-mix(in srgb,var(--bad) 35%,transparent); }
.msg.warn { background:var(--warn-bg); color:var(--warn);
            border:1px solid color-mix(in srgb,var(--warn) 35%,transparent); }

/* A generated secret. Selectable, wrapping, and never truncated with an
 * ellipsis — a half-copied key is a debugging session. */
.secret {
  margin-top:10px; padding:11px; border-radius:8px; background:var(--bg2);
  border:1px solid var(--line2); font:12px/1.6 var(--mono);
  word-break:break-all; user-select:all;
}

/* ─────────────── bring-up relaxation banner ───────────────
 * Amber rather than red: this is a deliberate setting the owner asked for, not
 * a fault. Loud enough to be remembered, calm enough not to be dismissed as
 * noise. No close control, on purpose — the risk being guarded against is
 * forgetting it months later, and a banner you can close is closed once. */
.relaxbar {
  padding:9px 16px; font:12px/1.5 var(--mono); font-weight:600;
  color:var(--warn); background:var(--warn-bg);
  border-bottom:1px solid color-mix(in srgb,var(--warn) 40%,transparent);
  text-align:center;
}
.relaxbar[hidden] { display:none; }
.relaxbar b { color:var(--fg); }

/* ───────────────────────── the trail ───────────────────────── */

.trail { display:flex; flex-direction:column; gap:1px; font:11.5px var(--mono); }
.trail .row { display:flex; gap:10px; padding:6px 0; border-top:1px solid var(--line); }
.trail .row:first-child { border-top:0; }
.trail .who { color:var(--muted); flex:0 0 auto; }
.trail .what { flex:1 1 auto; word-break:break-all; }
.trail .row.denied .what { color:var(--bad); }

@media (max-width:640px) {
  .cmdrow > div { flex:1 1 100%; }
  .cmdrow button { width:100%; }
}
