/* pech admin.
 *
 * A control surface, not a document: scanned and operated rather than read top to bottom. So
 * state is encoded in shape as well as in number, and every figure carries where it came from
 * — measured, derived, or absent. A number with no source is a number nobody can check, and
 * this panel's whole job is telling an operator what is true about their node.
 *
 * Warm paper rather than the usual dashboard grey: this is a tool people keep open for hours,
 * and a cold ground makes the accent colour do work the accent should not have to do.
 */

:root {
  --ground:#f5f3ee; --surface:#ffffff; --sunken:#edeae3;
  --ink:#17150f; --ink2:#55503f; --ink3:#857e6a;
  --rule:#d9d3c6; --rule2:#ebe6db;
  --ember:#c94000; --ember2:#f7ede7;
  --ok:#2c6e49; --okbg:#e6f0e9;
  --warn:#8a5c00; --warnbg:#f6eddb;
  --crit:#9d211b; --critbg:#f7e7e5;
  --void:#6b6455; --voidbg:#eae7df;
  --mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;
  --sans:ui-sans-serif,-apple-system,"Segoe UI",Helvetica,sans-serif;
}

/* The un-stamped default: only prefers-color-scheme separates light from dark here, and an
   explicit light choice must still win over a dark system setting. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ground:#131210; --surface:#1b1a17; --sunken:#100f0d;
    --ink:#f0ece2; --ink2:#b8b2a2; --ink3:#847e6e;
    --rule:#332f28; --rule2:#26231e;
    --ember:#ff6a2b; --ember2:#2a1710;
    --ok:#6fbf8f; --okbg:#14251b;
    --warn:#d6a55c; --warnbg:#2a2113;
    --crit:#f0817a; --critbg:#2b1614;
    --void:#8b8474; --voidbg:#1d1b17;
  }
}
:root[data-theme="dark"] {
  --ground:#131210; --surface:#1b1a17; --sunken:#100f0d;
  --ink:#f0ece2; --ink2:#b8b2a2; --ink3:#847e6e;
  --rule:#332f28; --rule2:#26231e;
  --ember:#ff6a2b; --ember2:#2a1710;
  --ok:#6fbf8f; --okbg:#14251b;
  --warn:#d6a55c; --warnbg:#2a2113;
  --crit:#f0817a; --critbg:#2b1614;
  --void:#8b8474; --voidbg:#1d1b17;
}

*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:var(--sans);font-size:13.5px;color:var(--ink);background:var(--ground);line-height:1.5;-webkit-font-smoothing:antialiased}
a{color:var(--ember);text-decoration:none}
a:hover{color:var(--ink)}
:focus-visible{outline:2px solid var(--ember);outline-offset:2px}

/* --- Frame ------------------------------------------------------------- */

.app{display:grid;grid-template-columns:196px 1fr;min-height:100vh}
/* Signed out there is no rail, so there is no column for it. Hiding the rail alone removes it
   from the grid and the content slides into the 196px track meant for it. */
.app.no-rail{grid-template-columns:1fr}

.rail{background:var(--sunken);border-right:1px solid var(--rule);padding:14px 0;font-family:var(--mono);font-size:12px;display:flex;flex-direction:column;position:sticky;top:0;height:100vh;overflow-y:auto}
.brand{display:flex;align-items:center;gap:9px;padding:0 14px 13px;border-bottom:1px solid var(--rule);margin-bottom:6px;font-weight:700;letter-spacing:.04em;font-size:13px}
.mark{width:14px;height:14px;background:var(--ember);flex:none}
.rgroup{padding:12px 14px 4px;font-size:9.5px;letter-spacing:.13em;text-transform:uppercase;color:var(--ink3)}
.ritem{display:block;padding:5px 14px;color:var(--ink2);border-left:2px solid transparent}
.ritem:hover{color:var(--ink);background:var(--rule2)}
.ritem.active{color:var(--ink);background:var(--surface);border-left-color:var(--ember);font-weight:600}
#logout-btn{margin:auto 14px 4px;background:none;border:1px solid var(--rule);color:var(--ink3);padding:5px 10px;font-family:var(--mono);font-size:11px;cursor:pointer}
#logout-btn:hover{color:var(--crit);border-color:var(--crit)}

main{padding:20px 24px 40px;max-width:1280px}
.page{display:flex;flex-direction:column;gap:18px}

h2{font-family:var(--sans);font-size:17px;font-weight:620;letter-spacing:-.01em;padding-bottom:10px;border-bottom:1px solid var(--rule);text-wrap:balance}
h3{font-size:13px;font-weight:640;margin-bottom:9px}

/* --- Figures ------------------------------------------------------------
 * Hairline grid rather than floating cards: the figures are one instrument
 * read together, and a gap between cards invites reading them separately. */

.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(172px,1fr));gap:1px;background:var(--rule);border:1px solid var(--rule)}
.stat-card{background:var(--surface);padding:12px 14px;display:flex;flex-direction:column;gap:3px;min-width:0}
.stat-card .label{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink3)}
.stat-card .value{font-family:var(--mono);font-size:20px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums;line-height:1.15;overflow-wrap:anywhere;color:var(--ink)}
.stat-card .value.ok{color:var(--ok)} .stat-card .value.crit{color:var(--crit)} .stat-card .value.warn{color:var(--warn)}

/* A value copied character by character — an address, a key, a port. Breaking one across
   lines is how a digit goes missing between the screen and a config file. */
/* A long opaque string that is copied whole — a key. Wrapping it is harmless; clipping it
   hides half of it behind an edge with nothing to say more exists. */
.value-wrappable{overflow-wrap:anywhere;word-break:break-all;font-size:13px;line-height:1.35}
.value-atomic{overflow-wrap:normal;word-break:keep-all;white-space:nowrap;overflow-x:auto;font-size:15px}

/* --- Provenance ---------------------------------------------------------
 * Where a figure came from, next to the figure. */

.prov{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink3);display:flex;align-items:center;gap:5px;margin-top:2px}
.prov::before{content:"";width:6px;height:6px;flex:none}
.prov-measured::before{background:var(--ok)}
.prov-derived::before{background:var(--ink3)}
.prov-absent{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink3);display:flex;align-items:center;gap:5px;margin-top:2px}
.prov-absent::before{content:"";width:6px;height:6px;flex:none;box-shadow:inset 0 0 0 1px var(--ink3)}

/* A designed absence. Not an empty box: a blank panel reads as broken or still loading, and
   the difference between "zero" and "never measured" is the thing an operator needs. */
.void-state{border:1px dashed var(--rule);background:var(--voidbg);padding:16px;flex:1;min-width:220px;display:flex;flex-direction:column;gap:5px}
.void-title{font-size:13px;font-weight:620;color:var(--ink2)}
.void-detail{font-size:12px;color:var(--ink3);line-height:1.5}

/* --- Tables -------------------------------------------------------------- */

.tw{overflow-x:auto;border:1px solid var(--rule);background:var(--surface)}
table{width:100%;border-collapse:collapse;font-size:12.5px}
th,td{text-align:left;padding:8px 12px;border-bottom:1px solid var(--rule2)}
th{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink3);background:var(--sunken);font-weight:500;white-space:nowrap}
td{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--ink2)}
td:first-child{color:var(--ink);font-weight:600}
tbody tr:last-child td{border-bottom:none}
tbody tr:hover td{background:var(--rule2)}

/* --- Panels, forms, messages --------------------------------------------- */

.panel{background:var(--surface);border:1px solid var(--rule);padding:14px}
.panel ul{padding-left:18px}
.panel li{margin:4px 0;font-size:12.5px;color:var(--ink2)}

button,input,select,textarea{font-family:var(--mono);font-size:12.5px;color:var(--ink)}
button{background:var(--ink);color:var(--ground);border:none;padding:6px 14px;cursor:pointer;font-weight:600;letter-spacing:.01em}
button:hover{background:var(--ember)}
input,select,textarea{background:var(--surface);border:1px solid var(--rule);padding:6px 9px;min-width:0}
input:focus,select:focus,textarea:focus{border-color:var(--ember);outline:none}
input::placeholder{color:var(--ink3)}

.form-row{display:flex;gap:9px;align-items:center;margin:7px 0;flex-wrap:wrap}
.form-col{display:flex;flex-direction:column;gap:9px;max-width:640px;margin:9px 0}
.form-row label{min-width:104px;font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--ink3)}

/* A field's meaning must not live only in its placeholder: the hint disappears exactly when
   the value appears, so a filled form becomes a row of unlabelled boxes. */
.field{display:flex;flex-direction:column;gap:3px}
.field > label{font-family:var(--mono);font-size:9.5px;letter-spacing:.11em;text-transform:uppercase;color:var(--ink3)}
.field-note{font-size:11.5px;color:var(--ink3)}

/* An empty message is not a message: without this the border draws a stray mark under every
   form that has not said anything yet. */
.msg:empty{display:none}
.msg{padding:9px 12px;margin:9px 0;font-size:12.5px;border-left:2px solid}
.msg-ok{background:var(--okbg);border-color:var(--ok);color:var(--ok)}
.msg-err{background:var(--critbg);border-color:var(--crit);color:var(--crit)}

pre{background:var(--sunken);border:1px solid var(--rule);padding:11px;overflow-x:auto;font-family:var(--mono);font-size:11.5px;margin:9px 0;color:var(--ink2)}
.qr-wrap{background:#fff;padding:10px;border:1px solid var(--rule);display:inline-block}
.qr-wrap svg{display:block;height:240px;width:240px}

/* --- Login --------------------------------------------------------------- */

/* --- The door ------------------------------------------------------------
 *
 * The one screen someone who is not signed in can see, so it says as little
 * about the node as it can while still being the product's front door rather
 * than a form floating in a void.
 *
 * The ember rail is the same device the active rail item uses. A visitor sees
 * a card; an operator recognises the room behind it.
 */
/* Fills the viewport rather than the content column: `main` is capped at 1280px and flush
   left, so centring inside it puts the card at 640px on a wider screen — centred within
   something the reader cannot see the edges of, which reads as misaligned.
 *
 * The ground is a measured field rather than an empty one. Two hairline gratings at 32px give
 * the eye something to register scale against, so the card reads as placed on a surface
 * instead of floating in a void — and they say nothing about the node, which is the whole
 * constraint on this page. */
.gate-page{
  min-height:100vh;max-width:none;padding:24px;
  display:grid;place-items:center;position:relative;overflow:hidden;
  background-image:
    repeating-linear-gradient(to right, var(--rule2) 0 1px, transparent 1px 32px),
    repeating-linear-gradient(to bottom, var(--rule2) 0 1px, transparent 1px 32px);
  background-position:center;
}

/* The grating fades toward the edges, so the page does not end in a hard grid. Drawn first,
   because the horizon has to sit on top of it — the other order hides the horizon entirely,
   which is what happened when this was written the obvious way round. */
.gate-page::before{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse at center, transparent 25%, var(--ground) 72%);
}

/* The horizon.
 *
 * One hairline across the full width, which the card interrupts because the card is opaque.
 * That interruption is what turns a floating box into a placed one: the eye reads the card as
 * sitting *on* a surface rather than in nothing, and it costs one pseudo-element. */
.gate-page::after{
  content:"";position:absolute;left:0;right:0;top:50%;height:1px;background:var(--rule);
  pointer-events:none;
}
.gate{width:min(380px,100%);display:flex;flex-direction:column;gap:10px;position:relative;z-index:1}

.gate-card{background:var(--surface);border:1px solid var(--rule);border-left:2px solid var(--ember);
  padding:20px 22px 22px;display:flex;flex-direction:column;gap:11px;
  /* Opaque, so it interrupts the horizon behind it rather than letting the line run through. */
  box-shadow:0 1px 0 var(--rule2)}

.gate-brand{display:flex;align-items:center;gap:9px;font-family:var(--mono);font-size:13px;
  font-weight:700;letter-spacing:.04em;padding-bottom:13px;border-bottom:1px solid var(--rule2)}
.gate-brand .mark{width:14px;height:14px;background:var(--ember);flex:none}

.gate-title{font-size:18px;font-weight:620;letter-spacing:-.01em;line-height:1.2}
.gate-lede{font-size:12.5px;color:var(--ink3);line-height:1.5;margin-top:-6px}

.gate .field > label{font-size:9.5px}
.gate input{padding:7px 10px;font-size:13px}
.gate button{width:100%;padding:9px 14px;font-size:12.5px}

/* Which node this is. An operator with three tabs open cannot tell them apart, and the page
   has known the answer all along. Outside the card because it is about the address, not the
   account. */
.gate-where{font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;
  color:var(--ink3);display:flex;align-items:center;justify-content:center;gap:6px;padding:0 2px}
.gate-where::before{content:"";width:5px;height:5px;flex:none;background:var(--ink3)}

/* The first account owns the node. That is consequential and one-time, so it is weighted
   rather than phrased more strongly in the same grey as everything else. */
.gate-claim{background:var(--ember2);border-left:2px solid var(--ember);padding:10px 12px;
  font-size:12.5px;color:var(--ink2);line-height:1.5}

/* --- Status, said in a shape as well as a colour ------------------------------------------
   A pill carries its meaning in weight and ground as well as hue, so the difference survives a
   monochrome display and the common colour deficiencies. Semantic colour is separate from the
   accent: `--ember` means "this is pech", not "this is fine". */
.pill{display:inline-block;font-size:11px;line-height:1.6;padding:1px 8px;border:1px solid;
  white-space:nowrap;font-family:var(--mono);letter-spacing:.02em}
.pill-ok{color:var(--ok);border-color:var(--ok);background:var(--okbg)}
.pill-warn{color:var(--warn);border-color:var(--warn);background:var(--warnbg)}
.pill-crit{color:var(--crit);border-color:var(--crit);background:var(--critbg);font-weight:600}
.pill-void{color:var(--void);border-color:var(--void);background:var(--voidbg)}

/* A configuration is transcribed, not read: monospace, wrapping off, selectable whole. */
.config-text{width:100%;font-family:var(--mono);font-size:11.5px;line-height:1.55;
  background:var(--sunken);color:var(--ink);border:1px solid var(--rule);padding:10px;
  white-space:pre;overflow-x:auto;margin:10px 0}

/* An action that removes something reads as one before it is clicked, not after. */
button.danger-text{background:none;border:none;color:var(--crit);cursor:pointer;font-size:11.5px;
  padding:2px 4px;text-decoration:underline;text-underline-offset:2px}
button.danger-text:hover{background:var(--critbg)}

/* --- Outcome of an action ---------------------------------------------------------------
   A refusal that vanishes on a timer is a refusal nobody read, so only the successes fade.
   Colour is not the only carrier: the rule down the left edge differs in weight as well as
   hue, which survives both a monochrome display and the common colour deficiencies. */
.toasts{position:fixed;right:16px;bottom:16px;z-index:50;display:flex;flex-direction:column;
  gap:8px;max-width:min(460px,calc(100vw - 32px))}
.toast{display:flex;align-items:flex-start;gap:10px;background:var(--surface);
  border:1px solid var(--rule);padding:10px 12px;font-size:12.5px;line-height:1.5;
  color:var(--ink);box-shadow:0 2px 10px rgb(0 0 0 / .12)}
.toast-text{flex:1;overflow-wrap:anywhere}
.toast-ok{border-left:3px solid var(--ok);background:var(--okbg)}
.toast-crit{border-left:5px solid var(--crit);background:var(--critbg)}
.toast-close{flex:none;background:none;border:none;color:var(--ink3);cursor:pointer;
  font-size:15px;line-height:1;padding:0 2px}
.toast-close:hover{color:var(--ink)}

/* --- Empty, unmeasured, failed ----------------------------------------------------------
   Three different claims, and rendering them alike is how "nothing has measured this" comes
   to read as "there is none of it". `--void` is the palette's deliberately inert tone; the
   failure state borrows the critical one because a failed request is an event, not a state. */
.state{padding:12px 14px;font-size:12.5px;line-height:1.6;border-left:2px solid}
.state-empty{border-color:var(--rule);background:var(--sunken);color:var(--ink2)}
.state-void{border-color:var(--void);background:var(--voidbg);color:var(--ink2)}
.state-failed{border-color:var(--crit);background:var(--critbg);color:var(--ink)}
.state-why{display:block;margin-top:4px;color:var(--ink3);font-size:11.5px}

@media (max-width:760px){
  .app{grid-template-columns:1fr}
  .rail{position:static;height:auto;flex-direction:row;flex-wrap:wrap;align-items:center;gap:2px;padding:8px}
  .rail nav{display:flex;flex-wrap:wrap;gap:2px}
  .rgroup{display:none}
  .brand{border:none;margin:0;padding:0 10px 0 4px}
  .ritem{border-left:none;border-bottom:2px solid transparent;padding:4px 8px}
  .ritem.active{border-left:none;border-bottom-color:var(--ember)}
  main{padding:14px}
}
