/* AlarmCommand console.
 *
 * Light by default. An earlier version of this file was dark, on the reasoning
 * that these screens run on control-room monitors. That was the wrong read of
 * the product: this is where somebody sets up a source, writes a translation
 * rule, and later works out why one signal came out the way it did. Daytime
 * desk work, done occasionally — not a wallboard anyone stares at. Anyone who
 * does want dark gets it from their operating system setting, at the bottom of
 * this file.
 *
 * Two other things were making it feel harder than it is:
 *
 *  - Every label was small uppercase monospace with wide letter-spacing. That
 *    treatment says "technical readout", and it was on table headers, nav
 *    groups and field labels alike. Monospace is now kept for what genuinely is
 *    data — account numbers, event codes, wire captures — where character
 *    alignment helps you compare two of them.
 *  - Hard one-pixel borders on every panel, row and cell drew a grid over the
 *    whole page. Panels now separate by surface and a soft edge instead.
 *
 * Semantic colour is unchanged and stays reserved: red and amber have to mean
 * critical and warning, so the accent is a teal that never competes with them.
 * Every status is still a dot or pill *and* a word — roughly one man in twelve
 * has some colour blindness, and these get read at 3am.
 */

:root {
  --bg:        #F4F6F8;
  --panel:     #FFFFFF;
  --panel-2:   #EFF3F6;
  --rule:      #E4E9ED;
  --rule-2:    #CBD4DB;
  --ink:       #1A242E;
  --ink-2:     #55636F;
  --ink-3:     #646F79;
  --accent:    #0E7C8C;
  --accent-ink:#FFFFFF;
  --accent-dim:rgba(14, 124, 140, 0.16);
  --ok:        #1C7A4F;
  --warn:      #8A5A12;
  --crit:      #B23A32;
  --admin:     #5B4BB0;
  --wire-bg:   #F7F9FA;
  --shadow:    0 1px 2px rgba(20, 35, 50, 0.05),
               0 1px 8px rgba(20, 35, 50, 0.04);

  --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
  --radius: 8px;
  --radius-sm: 5px;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { margin: 0; line-height: 1.25; text-wrap: balance; }
h1 { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.015em; }
h2 { font-size: 1rem; font-weight: 600; }
h3 { font-size: 0.9rem; font-weight: 600; }
p  { margin: 0; }

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.dim  { color: var(--ink-3); }
.muted{ color: var(--ink-2); }
.small{ font-size: 0.85rem; }

/* ── Shell ───────────────────────────────────────────────────────────────── */

.shell { display: grid; grid-template-columns: 224px 1fr; min-height: 100vh; }
@media (max-width: 820px) { .shell { grid-template-columns: 1fr; } }

.side {
  background: var(--panel);
  border-right: 1px solid var(--rule);
  padding: 1.35rem 0;
  display: flex; flex-direction: column; gap: 2px;
}
@media (max-width: 820px) {
  .side { flex-direction: row; overflow-x: auto; border-right: none;
          border-bottom: 1px solid var(--rule); padding: 0.5rem; }
}
.brand {
  font-size: 1rem; font-weight: 650; letter-spacing: -0.01em;
  color: var(--ink); padding: 0 1.15rem 1.25rem; text-decoration: none;
}
.brand:hover { text-decoration: none; }
@media (max-width: 820px) { .brand { display: none; } }

/* Sentence case, not the tiny tracked-out uppercase this used to be. */
.navgroup {
  font-size: 0.74rem; font-weight: 600; color: var(--ink-3);
  padding: 1rem 1.15rem 0.3rem;
}
@media (max-width: 820px) { .navgroup { display: none; } }

.side a.nav {
  display: flex; justify-content: space-between; align-items: center; gap: 0.5rem;
  margin: 0 0.6rem; padding: 0.5rem 0.55rem; color: var(--ink-2);
  font-size: 0.92rem; border-radius: var(--radius-sm);
  text-decoration: none; white-space: nowrap;
}
.side a.nav:hover { color: var(--ink); background: var(--panel-2); text-decoration: none; }
.side a.nav.on { color: var(--accent); background: var(--accent-dim); font-weight: 550; }
.side .count {
  font-size: 0.72rem; font-weight: 600; color: var(--warn);
  background: var(--panel-2); border-radius: 999px; padding: 0.05rem 0.42rem;
}
.side a.nav.on .count { background: var(--panel); }
.side .foot { margin-top: auto; padding: 1rem 1.15rem 0; border-top: 1px solid var(--rule); }
@media (max-width: 820px) { .side .foot { display: none; } }

.main { min-width: 0; display: flex; flex-direction: column; }
.topbar {
  display: flex; align-items: baseline; gap: 0.85rem; flex-wrap: wrap;
  padding: 1.5rem 1.9rem 0.2rem;
}
.topbar .sub { font-size: 0.88rem; color: var(--ink-3); }
.topbar .spacer { flex: 1; }
.content { padding: 1.5rem 1.9rem 4rem; }
.stack { display: flex; flex-direction: column; gap: 1.4rem; }

/* ── Flash ───────────────────────────────────────────────────────────────── */

.flashes { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.2rem; }
.flash {
  border: 1px solid var(--rule); border-left-width: 3px;
  border-radius: var(--radius-sm);
  padding: 0.7rem 1rem; font-size: 0.92rem; background: var(--panel);
  box-shadow: var(--shadow);
}
.flash.ok    { border-left-color: var(--ok); }
.flash.error { border-left-color: var(--crit); }
.flash.info  { border-left-color: var(--accent); }

/* ── Panels and tables ───────────────────────────────────────────────────── */

.panel {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--panel); box-shadow: var(--shadow);
}
.panel-head {
  display: flex; align-items: baseline; gap: 0.8rem; flex-wrap: wrap;
  padding: 1.05rem 1.25rem 0.7rem;
}
.panel-head .spacer { flex: 1; }
.panel-body { padding: 0.5rem 1.25rem 1.25rem; }
.scroll { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 0.9rem;
        font-variant-numeric: tabular-nums; }
th {
  text-align: left; font-weight: 600; font-size: 0.78rem; color: var(--ink-3);
  padding: 0.5rem 1.25rem; border-bottom: 1px solid var(--rule); white-space: nowrap;
}
td { padding: 0.72rem 1.25rem; border-bottom: 1px solid var(--rule);
     color: var(--ink-2); vertical-align: middle; }
tr:last-child td { border-bottom: none; }
td.key { color: var(--ink); font-weight: 550; }
td.key.mono, td.mono { font-family: var(--mono); font-weight: 400; }
tr.link:hover td { background: var(--panel-2); }

/* Recent signals sit inside a channel card, under everything else on it. The
   page-level row height is right for a table you came to read; here it pushes
   the next channel off the screen, and seeing the next channel is the point. */
table.compact { font-size: 0.8rem; line-height: 1.2; }
table.compact th { padding: 0.2rem 0.6rem; }
table.compact td { padding: 0.1rem 0.6rem; white-space: nowrap; }
table.compact .dot { width: 7px; height: 7px; }
/* An error message is the one thing here worth reading rather than scanning,
   so it keeps its wrapping while everything around it stays on one line. */
table.compact td.wrap { white-space: normal; }
/* An error printed under a cell has to wrap even though the row does
   not: it is the one thing there meant to be read rather than scanned. */
table.compact td .wrap { white-space: normal; line-height: 1.3; }
td.sev { box-shadow: inset 3px 0 0 var(--rule-2); }
td.sev-ok   { box-shadow: inset 3px 0 0 var(--ok); }
td.sev-warn { box-shadow: inset 3px 0 0 var(--warn); }
td.sev-crit { box-shadow: inset 3px 0 0 var(--crit); }

.empty { padding: 2.6rem 1rem; text-align: center; color: var(--ink-3);
         font-size: 0.92rem; }
.empty strong { display: block; color: var(--ink); margin-bottom: 0.35rem;
                font-weight: 600; font-size: 1rem; }

/* ── State ───────────────────────────────────────────────────────────────── */

.pill {
  font-size: 0.76rem; font-weight: 600;
  padding: 0.12rem 0.5rem; border-radius: 999px; border: 1px solid currentColor;
  white-space: nowrap; display: inline-block;
}
.p-ok{color:var(--ok)} .p-warn{color:var(--warn)} .p-crit{color:var(--crit)}
.p-idle{color:var(--ink-3)} .p-live{color:var(--accent)}

.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block;
       margin-right: 0.45rem; vertical-align: 0; }
.d-ok{background:var(--ok)} .d-warn{background:var(--warn)}
.d-crit{background:var(--crit)} .d-idle{background:var(--ink-3)}

/* ── Metrics ─────────────────────────────────────────────────────────────── */

.metrics { display: grid; gap: 0.9rem;
           grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.metric { background: var(--panel); padding: 1rem 1.15rem;
          border: 1px solid var(--rule); border-radius: var(--radius);
          box-shadow: var(--shadow); }
.metric .k { font-size: 0.8rem; color: var(--ink-3); display: block;
             margin-bottom: 0.15rem; }
.metric .v { font-family: var(--mono); font-size: 1.65rem; font-weight: 600;
             color: var(--ink); display: flex; align-items: baseline; gap: 0.3rem;
             letter-spacing: -0.02em; }
.metric .v small { font-size: 0.7rem; font-weight: 400; color: var(--ink-3);
                   font-family: var(--sans); }
.metric a { color: inherit; }

/* ── Buttons and forms ───────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-family: var(--sans); font-size: 0.88rem; font-weight: 550;
  padding: 0.44rem 0.9rem; border-radius: var(--radius-sm); cursor: pointer;
  border: 1px solid var(--rule-2); background: var(--panel); color: var(--ink);
  text-decoration: none;
}
.btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent);
               color: var(--accent-ink); font-weight: 600; }
.btn.primary:hover { filter: brightness(1.1); color: var(--accent-ink); }
.btn.danger { color: var(--crit); }
.btn.danger:hover { border-color: var(--crit); color: var(--crit); }
.btn.small { font-size: 0.8rem; padding: 0.26rem 0.6rem; }
.btn:disabled, .btn.disabled { opacity: 0.45; cursor: not-allowed; }

.field { display: flex; flex-direction: column; gap: 0.32rem; margin-bottom: 1.15rem; }
.field > label { font-size: 0.9rem; font-weight: 600; color: var(--ink); }
.field .help { font-size: 0.84rem; color: var(--ink-3); line-height: 1.5; }
.field.req > label::after { content: " *"; color: var(--crit); }

input[type=text], input[type=password], input[type=number], input[type=search],
input[type=file], select, textarea {
  background: var(--panel); color: var(--ink); font-family: var(--sans);
  font-size: 0.92rem; border: 1px solid var(--rule-2); border-radius: var(--radius-sm);
  padding: 0.5rem 0.7rem; width: 100%;
}
/* Data entry stays monospace: account numbers and codes get compared by eye. */
.steps input, input.mono { font-family: var(--mono); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim);
}
textarea { min-height: 5rem; resize: vertical; font-family: var(--mono); }
select { cursor: pointer; }

.check { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 1.15rem; }
.check input { margin-top: 0.3rem; width: auto; }
.check label { font-size: 0.92rem; color: var(--ink); font-weight: 550; }
.check .help { font-size: 0.84rem; color: var(--ink-3); display: block;
               font-weight: 400; }

.grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));
         gap: 0 1.6rem; }
.formbar { display: flex; gap: 0.6rem; align-items: center; padding-top: 1rem;
           border-top: 1px solid var(--rule); margin-top: 0.4rem; }
.formbar .spacer { flex: 1; }

.filters { display: flex; gap: 0.7rem; flex-wrap: wrap; align-items: flex-end; }
.filters .field { margin-bottom: 0; min-width: 9rem; }
.filters .field > label { font-size: 0.82rem; font-weight: 600; color: var(--ink-3); }

/* ── Kind picker ─────────────────────────────────────────────────────────── */

.picker { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
          gap: 0.75rem; }
.pick { border: 1px solid var(--rule); border-radius: var(--radius);
        background: var(--panel);
        padding: 1rem 1.1rem; display: flex; flex-direction: column; gap: 0.35rem;
        text-decoration: none; color: inherit; }
.pick:hover { border-color: var(--accent); background: var(--panel-2);
              text-decoration: none; }
.pick.off { opacity: 0.5; pointer-events: none; }
.pick h3 { font-size: 0.95rem; color: var(--ink); }
.pick p { font-size: 0.85rem; color: var(--ink-3); line-height: 1.45; }
.pick .state { font-size: 0.72rem; color: var(--ink-3); align-self: flex-start;
               border: 1px solid currentColor; border-radius: 999px;
               padding: 0.05rem 0.45rem; }

/* Channel state, readable across a page of them. Three carriers, loudest to
   quietest: the dot, then the name band behind it, then the card's own border.
   The band is a wash rather than a fill so the dot stays the brightest thing
   in the header and the channel name stays readable on top of it.

   Critical is the exception and is meant to be: a started channel taking
   alarms in with a destination refusing them is the one state somebody has to
   act on now, so it gets a solid band instead of a wash. Grey never shouts --
   a switched-off channel is not a fault, and if it looks like one the real
   faults get lost among them. */
:root {
  --tint-running:  #E6F4EC;
  --tint-warning:  #FBF0DC;
  --tint-error:    #F9E7E5;
  --tint-critical: #B23A32;
  --tint-stopped:  #EFF3F6;
  --on-critical:   #FFFFFF;
}

.channel { border: 2px solid var(--rule-2); overflow: hidden; }
.c-running  { border-color: var(--ok); }
.c-warning  { border-color: var(--warn); }
.c-error    { border-color: var(--crit); }
.c-critical { border-color: var(--crit); }
.c-stopped  { border-color: var(--rule-2); }

/* The name sits on the state, so which channel and how it is doing are one
   glance rather than two. */
.band { align-items: center; border-bottom: 1px solid var(--rule); }
.band h2 { color: var(--ink); }
.d-running  { background: var(--tint-running); }
.d-warning  { background: var(--tint-warning); }
.d-error    { background: var(--tint-error); }
.d-stopped  { background: var(--tint-stopped); }
.d-critical { background: var(--tint-critical); }
.d-critical h2, .d-critical a, .d-critical .muted { color: var(--on-critical); }
.d-critical .btn { border-color: rgba(255, 255, 255, 0.55);
                   background: transparent; color: var(--on-critical); }

.band-detail { padding: 0.4rem 1.25rem 0.55rem; font-size: 0.85rem;
               color: var(--ink-2); border-bottom: 1px solid var(--rule); }
.band-detail.d-critical { color: var(--on-critical); font-weight: 550; }
.band-detail.d-stopped, .band-detail.d-running { color: var(--ink-3); }

.beacon { width: 14px; height: 14px; border-radius: 50%; display: inline-block;
          flex: 0 0 auto; box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.7); }
.b-running  { background: var(--ok); }
.b-warning  { background: var(--warn); }
.b-error    { background: var(--crit); }
.b-critical { background: #FFFFFF; }
.b-stopped  { background: var(--rule-2); }

/* The backlog, as a number. "queue" as a word told you nothing you did not
   already know; the count is the thing that decides whether to look. */
.queue-chip { font-size: 0.78rem; color: var(--ink-3); text-decoration: none;
              border: 1px solid var(--rule-2); border-radius: 999px;
              padding: 0.1rem 0.55rem; white-space: nowrap; }
.queue-chip b { font-variant-numeric: tabular-nums; color: var(--ink-2); }
.queue-chip.has-work { border-color: var(--warn); color: var(--warn); }
.queue-chip.has-work b { color: var(--warn); }
.d-critical .queue-chip, .d-critical .queue-chip b {
  color: var(--on-critical); border-color: rgba(255, 255, 255, 0.55); }

/* Start and stop is the control on this screen, and it was the same size as
   the three buttons that only ever check something. */
.btn.run { font-size: 0.95rem; font-weight: 600; padding: 0.42rem 1.5rem;
           letter-spacing: 0.01em; }
.btn.run.start { background: var(--ok); border-color: var(--ok); color: #FFFFFF; }
.btn.run.start:hover { filter: brightness(1.08); }
.btn.run.stop { background: var(--panel); border-color: var(--ink-3);
                color: var(--ink); }
.d-critical .btn.run.stop { background: rgba(255, 255, 255, 0.14);
                            border-color: var(--on-critical);
                            color: var(--on-critical); }

.status { font-size: 0.8rem; font-weight: 600; padding: 0.1rem 0.55rem;
          border-radius: 999px; border: 1px solid currentColor; }
.s-running  { color: var(--ok); }
.s-warning  { color: var(--warn); }
.s-error    { color: var(--crit); }
.s-critical { color: var(--on-critical); border-color: var(--on-critical); }
.s-stopped  { color: var(--ink-3); }

.adder { margin-top: 0.5rem; }

/* Test and Edit are the same size of decision and both about the destination.
   Stacked, they read as two steps to work through. */
.node-actions { display: flex; gap: 0.4rem; flex-wrap: wrap;
                margin-top: 0.5rem; }

/* Recent signals folds away. The summary keeps the shape of a panel head so
   the card does not visibly change layout when it opens and closes. */
details.recent > summary { display: flex; align-items: baseline; gap: 0.8rem;
  flex-wrap: wrap; padding: 0.7rem 1.25rem 0.6rem; cursor: pointer;
  list-style: none; border-top: 1px solid var(--rule); }
details.recent > summary::-webkit-details-marker { display: none; }
details.recent > summary h2 { font-size: 0.95rem; font-weight: 600; margin: 0; }
details.recent > summary::before { content: "\25B8"; color: var(--ink-3);
  font-size: 0.7rem; transition: transform 0.12s ease; }
details.recent[open] > summary::before { transform: rotate(90deg); }
details.recent > summary:hover { background: var(--panel-2); }
details.recent > summary .spacer { flex: 1; }
.adder > summary {
  list-style: none; cursor: pointer; width: 2rem; height: 2rem;
  display: grid; place-items: center; font-size: 1.15rem; line-height: 1;
  border: 1px dashed var(--rule-2); border-radius: var(--radius-sm);
  color: var(--ink-3); background: var(--panel);
}
.adder > summary::-webkit-details-marker { display: none; }
.adder > summary:hover { border-color: var(--accent); color: var(--accent);
                         border-style: solid; }
.adder[open] > summary { border-style: solid; border-color: var(--accent);
                         color: var(--accent); }
.adder .filters { margin-top: 0.55rem; }

/* A fold with words in it. `.adder` is a fixed 2rem square built to hold a
   single "+", so any text summary spilled straight out of its border. */
.fold > summary {
  list-style: none; cursor: pointer; display: inline-flex; align-items: center;
  gap: 0.4rem; padding: 0.35rem 0.75rem; font-size: 0.85rem;
  border: 1px solid var(--rule-2); border-radius: var(--radius-sm);
  color: var(--ink-2); background: var(--panel);
}
.fold > summary::-webkit-details-marker { display: none; }
.fold > summary::before { content: "B8"; font-size: 0.65rem;
                          color: var(--ink-3); transition: transform 0.12s ease; }
.fold[open] > summary::before { transform: rotate(90deg); }
.fold > summary:hover { border-color: var(--accent); color: var(--accent); }
.fold[open] > summary { border-color: var(--accent); color: var(--accent); }
.fold-body { margin-top: 0.7rem; }

/* Pictures shown as pictures. A filename and a byte count leaves you trusting
   that the bytes are what you think they are; a thumbnail settles it. */
.shots { display: grid; gap: 1rem;
         grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); }
.shots figure { margin: 0; border: 1px solid var(--rule);
                border-radius: var(--radius-sm); overflow: hidden;
                background: var(--panel-2); }
.shots img { display: block; width: 100%; height: 11rem; object-fit: contain;
             background: var(--wire-bg); }
/* Remove a signal from a template's list. Quiet until the row is hovered:
   it sits in every row, and twelve red crosses read as twelve problems. */
.rowx { border: 0; background: none; color: var(--ink-3); cursor: pointer;
        font-size: 1rem; line-height: 1; padding: 0.1rem 0.35rem;
        border-radius: var(--radius-sm); }
.rowx:hover { background: var(--crit); color: #FFFFFF; }
.steps tr:hover .rowx { color: var(--ink-2); }

.shots figcaption { display: flex; flex-direction: column; gap: 0.15rem;
                    padding: 0.5rem 0.6rem; font-size: 0.78rem; }
/* Where the picture was, once the retention policy has discarded the bytes.
   Same footprint as the image so the row does not reflow. */
.shots .gone { display: grid; place-items: center; align-content: center;
               gap: 0.2rem; height: 11rem; background: var(--wire-bg);
               color: var(--ink-3); font-size: 0.78rem; text-align: center; }

/* The numbered step, so the order of the work is on the page rather than in
   somebody's memory of being told it. */
.step { display: grid; place-items: center; width: 1.5rem; height: 1.5rem;
        border-radius: 50%; background: var(--accent); color: var(--accent-ink);
        font-size: 0.8rem; font-weight: 700; flex: 0 0 auto; }
.step.optional { background: var(--rule-2); color: var(--ink-2);
                 font-size: 0.62rem; width: auto; height: auto;
                 border-radius: 999px; padding: 0.15rem 0.5rem;
                 text-transform: uppercase; letter-spacing: 0.04em; }

.sub-head { font-size: 0.78rem; font-weight: 600; color: var(--ink-3);
            text-transform: uppercase; letter-spacing: 0.04em;
            margin: 0 0 0.4rem; }

/* The message and what it produced, side by side, so a capture can be checked
   without scrolling away from the message it came from. Stacked below 1100px,
   where two columns would leave neither readable. */
.capture-bar { align-items: flex-end; padding: 0.6rem 0.75rem;
               background: var(--panel-2); border: 1px solid var(--rule);
               border-radius: var(--radius-sm); margin: 0.8rem 0; }
.build { display: grid; gap: 1rem; align-items: start;
         grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
.build-message pre { margin: 0; white-space: pre-wrap; cursor: text;
                     max-height: 30rem; overflow: auto; }
.build-result { min-width: 0; }
.build-result table { font-size: 0.78rem; }
.build-result .formbar { margin-top: 0.6rem; }
@media (max-width: 1100px) { .build { grid-template-columns: 1fr; } }
/* Shown but inert while the channel runs, so the card is the same size and
   shape whether it is started or stopped. */
.adder > summary.off { opacity: 0.4; cursor: not-allowed; pointer-events: none; }

/* ── The flow: sources on the left, destination on the right ─────────────── */
/* The line between them carries the state. Colour is never the only signal —
   each node also says "sending", "online, not getting through" or "not
   sending" in words, and shows a dot. */

/* Two columns that stack only when this card genuinely lacks the room —
   `auto-fit` measures the card, a media query measures the window. The window
   is the wrong thing to ask: the card sits inside a 224px sidebar and page
   padding, so a viewport breakpoint either stacks far too early or not at all,
   and gets it wrong on every screen size except the one it was tuned on.
   `min()` keeps the track from exceeding the card on a narrow phone. */
.flow { display: grid; gap: 1.6rem;
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
        padding: 0.4rem 1.25rem 1.25rem; align-items: start; }
.flow-side h3 { color: var(--ink-3); margin-bottom: 0.55rem; }

/* The left edge is where the state lives, so it is worth seeing from a normal
   reading distance -- three pixels of it against a white card was not. A
   slightly tinted body separates the boxes from the card behind them without
   adding another line to look at. */
.node { display: flex; align-items: flex-start; gap: 0.6rem;
        border: 1px solid var(--rule-2); border-left-width: 6px;
        border-radius: var(--radius-sm); background: var(--panel-2);
        padding: 0.7rem 0.85rem; }
.node-body { flex: 1; min-width: 0; }
.node .btn { padding: 0.1rem 0.45rem; line-height: 1.2; }
.n-dest { border-left-color: var(--accent); }
/* State after identity: these must win over .n-dest, which is only saying
   "this is the destination node". Equal specificity, so order decides. */
.n-ok   { border-left-color: var(--ok); }
.n-warn { border-left-color: var(--warn); }
.n-idle { border-left-color: var(--rule-2); }

/* Drawn between a source and the destination it feeds. Vertical on a narrow
   screen is wrong — it would point at the next source rather than across — so
   it is hidden there and the node's own left edge carries the state. */
.wire-line { display: flex; align-items: center; gap: 0; height: 1.3rem;
             margin-left: 1.1rem; color: var(--rule-2); font-size: 0.8rem; }
.wire-line span { flex: 1; height: 3px; background: currentColor; max-width: 2.2rem;
                  border-radius: 2px; }
.w-ok   { color: var(--ok); }
.w-warn { color: var(--warn); }
.w-idle { color: var(--rule-2); }


.grid3 { display: grid; gap: 1.6rem;
          grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid3 h3 { color: var(--ink-3); margin-bottom: 0.5rem; }
.kvlist > div { display: flex; justify-content: space-between; gap: 1rem;
                padding: 0.2rem 0; font-size: 0.88rem; }
.kvlist b { font-family: var(--mono); font-weight: 500; color: var(--ink); }
.feed { display: flex; justify-content: space-between; align-items: center;
        gap: 0.8rem; padding: 0.45rem 0; border-bottom: 1px solid var(--rule); }
.feed:last-of-type { border-bottom: none; }

/* ── Signal detail chain ─────────────────────────────────────────────────── */

.chain { display: flex; flex-direction: column; }
.chain-step { display: grid; grid-template-columns: 160px 1fr; gap: 1.2rem;
              padding: 1.1rem 0; border-bottom: 1px solid var(--rule); }
.chain-step:last-child { border-bottom: none; }
@media (max-width: 700px) { .chain-step { grid-template-columns: 1fr; gap: 0.4rem; } }
.chain-label { font-size: 0.85rem; font-weight: 600; color: var(--ink-2); }
.chain-label .n { color: var(--accent); }
.chain-body { min-width: 0; }
.wire { background: var(--wire-bg); border: 1px solid var(--rule);
        border-radius: var(--radius-sm);
        padding: 0.65rem 0.8rem; overflow-x: auto; margin: 0 0 0.5rem; }
.wire code { font-family: var(--mono); font-size: 0.82rem; color: var(--ink-2);
             white-space: pre; }
.kv { display: flex; flex-wrap: wrap; gap: 0.3rem 1.2rem; font-family: var(--mono);
      font-size: 0.84rem; color: var(--ink-3); }
.kv b { color: var(--ink); font-weight: 550; }
.chip { display: inline-flex; align-items: center; gap: 0.4rem; font-family: var(--mono);
        font-size: 0.8rem; color: var(--ink-2); background: var(--panel-2);
        border: 1px solid var(--rule); border-radius: var(--radius-sm);
        padding: 0.22rem 0.55rem; margin: 0.15rem 0.3rem 0.15rem 0; }
.chip .arrow { color: var(--accent); }
.note { font-size: 0.86rem; color: var(--ink-3); margin-top: 0.5rem; line-height: 1.55; }

/* ── Login ───────────────────────────────────────────────────────────────── */

.login { min-height: 100vh; display: grid; place-items: center; padding: 2rem; }
.login .card { width: 100%; max-width: 360px; }
.login .brandline { font-size: 1.35rem; font-weight: 650; letter-spacing: -0.02em;
                    color: var(--ink); margin-bottom: 1.5rem; text-align: center; }

/* ── Steps editor ────────────────────────────────────────────────────────── */

.steps td { padding: 0.28rem 0.3rem; border-bottom: none; }
.steps input { padding: 0.32rem 0.45rem; font-size: 0.85rem; }
.steps th { padding: 0.4rem 0.3rem; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; } }

/* ── Dark, for anyone whose system asks for it ───────────────────────────── */
/* Only the tokens change. Every rule above is written against them, so there is
 * one palette to keep consistent rather than two sets of component styles. */

@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #10171E;
    --panel:     #161F28;
    --panel-2:   #1D2833;
    --rule:      #26333F;
    --rule-2:    #35485A;
    --ink:       #E2E9EF;
    --ink-2:     #9DAEBB;
    --ink-3:     #71828F;
    --accent:    #4FC3D4;
    --accent-ink:#082228;
    --accent-dim:rgba(79, 195, 212, 0.16);
    --ok:        #46C98C;
    --warn:      #E0AC48;
    --crit:      #EF7A72;
    --admin:     #A492EE;
    --wire-bg:   #0C1219;
    --shadow:    none;

    --tint-running:  #17322A;
    --tint-warning:  #33291442;
    --tint-error:    #341F1E;
    --tint-critical: #8E2F29;
    --tint-stopped:  #1B242D;
    --on-critical:   #FFFFFF;
  }
}
