/* Nexus Test Console styles.
 *
 * Extracted from a 229 line inline block in index.html. Spacing spread across
 * inline style attributes could not be read without adding up padding, margin,
 * min-height and a container gap on separate elements, which is why three
 * attempts at one gap were guesses.
 *
 * sidebar.css holds the setup panel, the SIM rows and the confirm modal. This
 * file holds everything else the console renders.
 */
*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:#000;overflow-x:auto;overflow-y:hidden}
/* --- Token system -------------------------------------------------------- */
.nx{
  --ink:#f0eeea;--ground:#000000;--surface:#0d0e0f;
  --rule:rgba(240,238,234,.28);--rule-soft:rgba(240,238,234,.14);--rule-faint:rgba(240,238,234,.07);
  --mute:#8a8781;--dim:#a8a49e;--inv:#000000;
  --accent:#a366ff;--accent-deep:#bf94ff;--accent-ink:#0a0016;
  --tint:#1d0d38;--tint-ink:#cfaeff;
  --ok:#3ddc84;--ok-tint:#07301c;--ok-ink:#8bf5bb;
  --warn:#f2b53d;--warn-tint:#2e2004;--warn-ink:#ffd98a;
  --bad:#ff6a5b;--bad-tint:#320f0b;--bad-ink:#ffb3aa;
  --idle:#6f6c67;--idle-tint:rgba(240,238,234,.07);

  /* Control scale. Three tiers, no fourth. Every button, input and toggle
   * derives its height from one of these, so a row of mixed controls lines up
   * without per-element padding. */
  --ctl-lg:40px;   /* the one committing action in a panel */
  --ctl-md:32px;   /* inputs, secondary actions, segmented toggles */
  --ctl-sm:24px;   /* icon buttons and inline chips */
  --ctl-pad-lg:0 14px;
  --ctl-pad-md:0 12px;
  --ctl-pad-sm:0 8px;
  --gap-tight:6px;
  --gap:10px;
  --gap-loose:14px;

  /* Sidebar width, shared with the header so the brand block and the panel
   * toggle above the sidebar end on the sidebar's own edge. */
  --sb-w:282px;
  --ctl-rail:52px
}
.nx[data-theme="light"]{
  --ink:#181716;--ground:#f4f3f1;--surface:#e9e7e4;
  --rule:rgba(24,23,22,.42);--rule-soft:rgba(24,23,22,.16);--rule-faint:rgba(24,23,22,.07);
  --mute:#7c7873;--dim:#5c5854;--inv:#f4f3f1;
  --accent:#4700b3;--accent-deep:#35008a;--accent-ink:#f7f4ff;
  --tint:#e3d8f9;--tint-ink:#2e0075;
  --ok:#16794a;--ok-tint:#cfe9dc;--ok-ink:#0b3f27;
  --warn:#a8730a;--warn-tint:#f2e4c4;--warn-ink:#5a3d02;
  --bad:#b3352a;--bad-tint:#f3d8d4;--bad-ink:#5f1811;
  --idle:#8d8983;--idle-tint:rgba(24,23,22,.08)
}
/* --- Base ---------------------------------------------------------------- */
.nx{font-family:Archivo,system-ui,sans-serif;font-size:15px;background:var(--ground);color:var(--ink);height:100vh;display:flex;flex-direction:column;overflow:hidden}
.nx button{transition:background .16s ease,color .16s ease,border-color .16s ease,box-shadow .16s ease}
.nx tbody tr{transition:background .14s ease,box-shadow .14s ease}
.nx tbody tr:hover{box-shadow:inset 3px 0 0 var(--accent)}
.nx .input{transition:border-color .16s ease,box-shadow .16s ease;background:var(--surface);color:var(--ink);border:1px solid var(--rule);border-radius:0;width:100%;padding:var(--ctl-pad-lg);font:inherit;font-size:13px;height:var(--ctl-lg)}
.nx .input.mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
.nx .input:focus{border-color:var(--accent);box-shadow:inset 0 -2px 0 var(--accent);outline:none}
.nx .input::placeholder{color:var(--mute)}
/* Stage-panel select. Same treatment as the sidebar's, on .input instead of
 * .sb-input: appearance:none removes the OS control, the chevron is drawn from
 * an inline SVG so it follows the theme, and options get explicit colours
 * because the OS draws that list. */
.nx select.input{appearance:none;-webkit-appearance:none;padding-right:32px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238a8781' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 12px center;cursor:pointer}
.nx[data-theme="light"] select.input{background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%237c7873' stroke-width='2.5' stroke-linecap='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E")}
.nx select{cursor:pointer}
.nx select:hover{border-color:var(--accent)}
.nx select.input option{background:var(--surface);color:var(--ink)}
.nx :focus-visible{outline:2px solid var(--accent);outline-offset:2px}
.nx .field{display:flex;flex-direction:column;gap:4px}
.nx .field>label{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}
.nx .btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--gap);border:1px solid var(--rule);border-radius:0;background:transparent;color:var(--ink);font:inherit;font-size:13px;font-weight:800;cursor:pointer;min-height:var(--ctl-lg);padding:var(--ctl-pad-md);white-space:nowrap;user-select:none;width:100%}
.nx .btn:hover{background:var(--rule-faint)}
.nx .btn-accent{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.nx .btn-accent:hover{background:var(--accent-deep);box-shadow:0 3px 0 0 var(--ink)}
.nx .sim-pill{font:inherit;font-size:11px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;height:var(--ctl-md);padding:var(--ctl-pad-md);border:1px solid var(--rule);border-radius:0;cursor:pointer;background:transparent;color:var(--mute);transition:background .14s,color .14s,border-color .14s}
.nx .sim-pill:hover{border-color:var(--accent);color:var(--ink)}
.nx .sim-pill.active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.nx .table td{border-bottom:1px solid var(--rule-soft)}
.nx .table tbody tr:hover{background:var(--rule-faint)}
@media(max-width:1240px){.nx-meta-3{display:none!important}}
@media(max-width:1080px){.nx-meta-2{display:none!important}}
/* --- Animations ---------------------------------------------------------- */
@keyframes nx-pulse{0%,100%{opacity:1}50%{opacity:.28}}
@keyframes nx-ring{0%{box-shadow:0 0 0 0 var(--accent)}70%{box-shadow:0 0 0 6px transparent}100%{box-shadow:0 0 0 0 transparent}}
@keyframes nx-flow{0%{background-position:0 0}100%{background-position:24px 0}}
@keyframes nx-rise{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
@keyframes nx-bar{from{width:0}}
@keyframes nx-fade-out{from{opacity:1;transform:none}to{opacity:0;transform:translateY(-6px)}}
/* --- Scrollbar ----------------------------------------------------------- */
a{color:var(--accent)}
::-webkit-scrollbar{width:4px;height:4px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:var(--rule)}
/* --- Toast --------------------------------------------------------------- */
#toasts{position:fixed;bottom:20px;right:20px;display:flex;flex-direction:column;gap:var(--gap);z-index:999;pointer-events:none}
.nx-toast{padding:10px 16px;border:1px solid var(--rule);background:var(--surface);color:var(--ink);font-size:13px;max-width:320px;animation:nx-rise .2s ease}
.nx-toast-ok{border-color:var(--ok);color:var(--ok)}
.nx-toast-err{border-color:var(--bad);color:var(--bad)}
.nx-toast-acc{border-color:var(--accent);color:var(--accent)}
/* --- USSD grid ----------------------------------------------------------- */
.ussd-grid{display:grid;grid-template-columns:1fr 1fr;gap:0}
.ussd-grid button{font:inherit;font-weight:800;font-size:11px;letter-spacing:.06em;text-transform:uppercase;text-align:center;padding:var(--ctl-pad-md);height:var(--ctl-lg);border:1px solid var(--rule);border-radius:0;margin:0;min-width:0;cursor:pointer;background:transparent;color:var(--ink)}
.ussd-grid button:nth-child(2){margin-left:-1px}
.ussd-grid button:nth-child(3){margin-top:-1px}
.ussd-grid button:nth-child(4){margin-top:-1px;margin-left:-1px}
.ussd-grid button:hover{background:var(--ink);color:var(--inv);border-color:var(--ink);box-shadow:inset 0 -3px 0 var(--accent)}
/* --- Net pair ------------------------------------------------------------ */
.net-pair{display:flex;gap:0}
.net-pair button{flex:1;font:inherit;font-weight:800;font-size:11px;letter-spacing:.1em;text-transform:uppercase;padding:var(--ctl-pad-md);height:var(--ctl-lg);border:1px solid var(--rule);border-radius:0;cursor:pointer;background:transparent;color:var(--ink)}
.net-pair button:nth-child(2){margin-left:-1px}
.net-pair button.active{background:var(--ink);color:var(--inv);border-color:var(--ink)}
.net-pair button:not(.active):hover{background:var(--rule-faint)}
/* --- Scenario buttons ---------------------------------------------------- */
.nx-sc-btn{display:flex;align-items:center;justify-content:space-between;gap:var(--gap);width:100%;font:inherit;font-weight:800;font-size:13px;text-align:left;padding:8px 10px;border:1px solid var(--rule);border-radius:0;cursor:pointer;color:var(--ink);background:transparent}
.nx-sc-btn:hover{border-color:var(--accent);color:var(--accent)}
.nx-sc-btn.running{background:var(--tint);color:var(--tint-ink);border-color:var(--accent)}
.nx-sc-steps{border:1px solid var(--rule);border-top:none;padding:8px 10px;display:flex;flex-direction:column;gap:var(--gap-tight);background:var(--surface)}
/* --- Sidebar collapse ---------------------------------------------------- */
aside{transition:width .22s cubic-bezier(.4,0,.2,1);overflow-y:auto;overflow-x:hidden}
/* --- Small screen message ------------------------------------------------ */
#nx-small-screen{display:none;position:fixed;inset:0;z-index:9999;background:#000;flex-direction:column;align-items:center;justify-content:center;gap:var(--gap-loose);text-align:center;padding:32px;font-family:Archivo,system-ui,sans-serif;color:#f0eeea}
#nx-small-screen p{margin:0;font-size:14px;color:#8a8781;max-width:260px;line-height:1.6}
@media(max-width:949px){#nx-small-screen{display:flex}.nx{display:none!important}}
/* --- Responsive min-width ------------------------------------------------ */
.nx{min-width:950px}
/* --- Ops filter buttons -------------------------------------------------- */
/* Segmented filter. 12px of side padding on 11px uppercase text left the four
 * labels touching their borders. A minimum width also keeps the strip from
 * resizing as the active label changes weight. */
.filter-btn{display:inline-flex;align-items:center;justify-content:center;font:inherit;font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;height:var(--ctl-lg);padding:0 18px;min-width:82px;border:1px solid var(--rule);border-radius:0;margin-left:-1px;cursor:pointer;color:var(--ink);background:transparent;white-space:nowrap}
.filter-btn:hover{background:var(--rule-faint)}
.filter-btn.active{background:var(--ink);color:var(--inv);border-color:var(--ink)}
/* sidebar.css loaded via <link> -- includes sidebar, SIM rows, and confirm modal */

/* === OPERATIONS TABLE ===================================================== */
.ops-table{width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;min-height:0}

/* Fixed column widths -- layout holds even when cells are empty */
.ops-table .col-id     {width:196px}
.ops-table .col-amount {width:116px}
.ops-table .col-type   {width:60px}
.ops-table .col-dest   {width:136px}
.ops-table .col-sim    {width:174px}
.ops-table .col-status {width:126px}
.ops-table .col-outcome{width:154px}
.ops-table .col-age    {width:68px}
.ops-table .col-action {width:52px}

/* Shared th / td rhythm */
.ops-table th,.ops-table td{padding:0 12px;vertical-align:middle;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ops-table th:first-child,.ops-table td:first-child{padding-left:24px}
.ops-table th:last-child,.ops-table td:last-child{padding-right:8px;text-align:center;overflow:visible}

/* Header */
.ops-table thead th{height:var(--ctl-lg);background:var(--ground);position:sticky;top:0;z-index:1;color:var(--mute);border-bottom:2px solid var(--rule);text-align:left;font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase}
/* Data rows */
.ops-table tbody td{height:calc(var(--ctl-lg) + var(--gap));border-bottom:1px solid var(--rule-soft)}
.ops-table tbody tr{cursor:pointer}
.ops-table tbody tr:hover{box-shadow:inset 3px 0 0 var(--accent);background:var(--rule-faint)}
.ops-table tbody tr.nx-expanded{background:var(--rule-faint)}

/* Age column -- right-aligned */
.ops-table .col-age{text-align:right;padding-right:20px}
.ops-table thead .col-age{padding-right:20px}

/* Header utility classes */
.nx-ops-th{height:var(--ctl-lg);padding:0 12px;background:var(--ground);position:sticky;top:0;z-index:1;color:var(--mute);border-bottom:2px solid var(--rule);text-align:left;font-size:10px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:middle}
.nx-ops-th-first{padding-left:24px}
.nx-ops-th-age{text-align:right;padding-right:20px}
.nx-ops-th-action{width:52px;text-align:center;padding-right:8px;overflow:visible}

/* Cell type modifiers -- typography only; padding/height come from td rule above */
.nx-cell-id{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--dim);letter-spacing:.02em}
.nx-cell-amount{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:800;font-size:14px;font-variant-numeric:tabular-nums}
.nx-cell-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;color:var(--dim)}
.nx-cell-age{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px;color:var(--mute);font-variant-numeric:tabular-nums}

/* Status tag */
.nx-tag{display:inline-flex;align-items:center;gap:var(--gap-tight);font-size:11px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;padding:3px 9px;white-space:nowrap}
.nx-tag-dot{width:6px;height:6px;flex:none}

/* Type badge */
.nx-type-badge{display:inline-block;font-size:10px;font-weight:700;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;padding:1px 5px;border:1px solid;border-radius:2px;white-space:nowrap;letter-spacing:.04em}

/* Operator mini badge inside SIM cell */
.nx-op-badge{display:inline-block;margin-left:6px;font-size:9px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:1px 4px;color:#000;vertical-align:middle}

/* Action buttons */
/* Row actions stay hidden until the row is hovered, so a long table is not a
 * wall of icons. Sized on the scale with every other icon button. */
.ops-row-cancel,.ops-row-del{display:inline-flex;align-items:center;justify-content:center;width:var(--ctl-sm);height:var(--ctl-sm);border:1px solid transparent;background:transparent;cursor:pointer;color:transparent;border-radius:0;padding:0;flex:none;vertical-align:middle;transition:color .14s,border-color .14s,background .14s}
.ops-table tr:hover .ops-row-cancel{color:var(--warn)}
.ops-table tr:hover .ops-row-del{color:var(--mute)}
.ops-row-cancel:hover{color:var(--warn);border-color:var(--warn);background:var(--warn-tint)}
.ops-row-del:hover{color:var(--bad);border-color:var(--bad);background:var(--bad-tint)}

/* Detail expand row */
.nx-detail-row td{height:auto;background:var(--surface);cursor:default;overflow:visible;white-space:normal}
.nx-detail-inner{display:flex;gap:40px;padding:16px 24px 18px;border-bottom:2px solid var(--rule)}
.nx-detail-fields{flex:none;width:280px;display:flex;flex-direction:column;gap:var(--gap-tight)}
.nx-detail-field{display:flex;gap:var(--gap-loose);font-size:12px;white-space:normal}
.nx-detail-key{width:112px;flex:none;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--mute);padding-top:2px}
.nx-detail-val{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;word-break:break-all;color:var(--dim)}

/* Trace panel */
.nx-trace{flex:1;min-width:0}
.nx-trace-label{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);margin-bottom:10px}
.nx-trace-list{display:flex;flex-direction:column}
.nx-trace-step{display:flex;align-items:center;gap:var(--gap-loose);padding:6px 0;border-bottom:1px solid var(--rule-soft)}
.nx-trace-dot{width:9px;height:9px;flex:none}
.nx-trace-name{width:158px;flex:none;font-size:12px;font-weight:600}
.nx-trace-at{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--dim);width:92px;flex:none}
.nx-trace-bar-wrap{flex:1;height:6px;background:var(--rule-faint);position:relative;overflow:hidden}
.nx-trace-bar{position:absolute;top:0;bottom:0;animation:nx-bar .5s cubic-bezier(.2,.7,.3,1)}
.nx-trace-dur{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;width:64px;text-align:right;flex:none}

/* === DETAIL PANEL (ops-detail.js) ========================================= */
.nx-dp-inner{display:flex;gap:0;border-bottom:2px solid var(--rule);background:var(--surface)}
.nx-dp-left{flex:none;width:320px;padding:18px 20px 18px 24px;display:flex;flex-direction:column;gap:var(--gap-loose)}
.nx-dp-divider{width:1px;background:var(--rule-soft);flex:none;align-self:stretch}
.nx-dp-fields{display:flex;flex-direction:column;gap:0}
.nx-dp-field{display:flex;align-items:baseline;gap:0;padding:5px 0;border-bottom:1px solid var(--rule-soft)}
.nx-dp-field:last-child{border-bottom:none}
.nx-dp-key{width:80px;flex:none;font-size:9px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--mute);padding-top:1px}
.nx-dp-val{font-size:12px;color:var(--dim);word-break:break-all;line-height:1.4}
.nx-dp-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px}
/* USSD response block -- balance result, shown prominently */
.nx-dp-ussd{display:flex;flex-direction:column;gap:var(--gap-tight);padding:10px 12px;background:var(--rule-faint);border-left:2px solid #9b59b6}
.nx-dp-ussd-label{font-size:9px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:#9b59b6}
.nx-dp-ussd-text{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--dim);line-height:1.5;word-break:break-word}
/* Trace panel */
.nx-trace{flex:1;min-width:0;padding:18px 20px}
.nx-trace-label{font-size:9px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute);margin-bottom:12px}
.nx-trace-list{display:flex;flex-direction:column}
.nx-trace-step{display:flex;align-items:center;gap:var(--gap);padding:7px 0;border-bottom:1px solid var(--rule-soft)}
.nx-trace-step.nx-trace-pending .nx-trace-name{color:var(--mute)}
.nx-trace-step.nx-trace-pending .nx-trace-at,.nx-trace-step.nx-trace-pending .nx-trace-dur{color:var(--rule)}
.nx-trace-dot{width:8px;height:8px;flex:none;border-radius:0}
.nx-trace-name{width:148px;flex:none;font-size:11px;font-weight:600;letter-spacing:.02em}
.nx-trace-at{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--dim);width:90px;flex:none}
.nx-trace-bar-wrap{flex:1;height:4px;background:var(--rule-faint);position:relative;overflow:hidden}
.nx-trace-bar{position:absolute;top:0;bottom:0;left:0;animation:nx-bar .5s cubic-bezier(.2,.7,.3,1)}
.nx-trace-dur{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;color:var(--mute);width:56px;text-align:right;flex:none}

/* === SCENARIO / TEST CONSOLE ============================================== */
.nx-sc-step{display:flex;align-items:center;gap:var(--gap);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;padding:1px 0}
.nx-sc-mark{width:11px;height:11px;flex:none;border:1px solid var(--rule);transition:background .14s,border-color .14s}
.nx-sc-label{flex:1;color:var(--dim)}
.nx-sc-dur{font-size:10px;color:var(--mute);margin-left:auto}
/* n8n stage panel.
   The spacing here was inline on each element, so the gap between the last
   field and the create button differed between deposit and balance mode and
   could not be reasoned about without adding up four style attributes. It is
   stated once, here.

   The preview reserved a minimum height whether or not it held text, so in
   deposit mode an empty 22px box sat between the field grid and the button.
   It now collapses when empty and takes its height from its content. */
.nx .n8n-fields{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap)}
/* The USSD preview appears only once a prefix resolves to an operator, so
 * collapsing it when empty moved the Create Operation button every time the
 * MSISDN field changed. The row keeps its height and goes blank instead. */
.nx .n8n-ussd{font-size:11px;color:var(--mute);font-family:ui-monospace,monospace;height:var(--ctl-sm);display:flex;align-items:center;overflow-x:auto;overflow-y:hidden;white-space:nowrap;overscroll-behavior:contain;flex:none}
.nx .n8n-sim-select-label{font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--mute);margin-bottom:4px}
.nx .n8n-sim-pills{display:flex;gap:var(--gap-tight);flex-wrap:wrap}
.nx .n8n-no-sim{font-size:11px;color:var(--mute)}
.nx .n8n-submit{min-height:var(--ctl-lg)}

/* Role badge and the Management section.
 *
 * The role was known but never shown, so an admin and a basic agent saw an
 * identical console and neither was told which they were. */
.nx .nx-role{font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:2px 7px;border:1px solid var(--rule);color:var(--mute)}
.nx .nx-role[data-role="admin"]{color:var(--accent);border-color:var(--accent)}
.nx .nx-role[data-role="agent"]{color:var(--ink)}
.nx .nx-role[data-role="none"]{color:var(--mute);opacity:.7}

/* Management styles live in sidebar.css with the rest of the panel chrome.
 * They were here, which is how the section ended up using .input and .btn from
 * the stage panels instead of the sidebar's own field and button classes. */

/* Both slots in use.
 *
 * This read "Device full. Remove a SIM to add another." in var(--bad), the
 * same red as a failure, for what is the normal end state of provisioning a
 * two slot device. It states the state rather than instructing the operator to
 * undo something. */
.nx .sb-sim-full{display:flex;align-items:center;gap:var(--gap-tight);font-size:11px;line-height:1.45;color:var(--mute);padding:6px 8px;background:var(--ground);border:1px solid var(--rule-soft)}
.nx .sb-sim-full svg{flex:none;color:var(--ok)}

/* Stage panel control scale.
 *
 * The four stages sized the same kinds of control four different ways: the
 * create button through a class at 42px, claim and abandon through inline
 * padding at about 34px, send SMS through an inline 42px duplicating what a
 * class should do, and the grouped controls through height on one and padding
 * on the other. Nothing was wrong individually and the band read as untidy
 * because no two stages agreed.
 *
 * One scale, stated once:
 *   primary action   42px, the action that advances the pipeline
 *   secondary action 34px, everything else a stage offers
 *   grouped control  32px, segmented rows such as the USSD grid
 *
 * Every stage panel uses the same 8px column gap, so a row of the same rank
 * lands at the same height across all four. */
.nx .stage-primary{min-height:var(--ctl-lg);padding:var(--ctl-pad-lg);justify-content:center}
.nx .stage-secondary{min-height:var(--ctl-lg);padding:var(--ctl-pad-md);justify-content:center}

/* Segmented groups. Both were 30 to 32px by different means, so they now share
 * the grouped control height and read as the same kind of thing. */

/* A stage's field labels and its readouts share one row shape, so the Claimed
 * line in Android and the Active SIM line in n8n sit at the same height. */
.nx .stage-readout{display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap);font-size:11px;min-height:18px}
.nx .stage-readout-key{letter-spacing:.1em;text-transform:uppercase;color:var(--mute)}
.nx .stage-readout-val{font-family:ui-monospace,monospace}

/* Checkbox rows are the same height wherever they appear, rather than carrying
 * an ad hoc margin-top in one stage and none in another. */
/* Checkbox rows. A 14px box is below the comfortable hit target and read as
 * an afterthought next to 40px buttons. The row is a control in its own
 * right, bordered so it groups with the buttons around it. */
.nx .stage-check{display:flex;align-items:center;gap:var(--gap);font-size:12px;cursor:pointer;min-height:var(--ctl-lg);padding:0 10px;border:1px solid var(--rule-soft);background:var(--surface);user-select:none}
.nx .stage-check:hover{border-color:var(--rule)}
.nx .stage-check input{accent-color:var(--accent);width:16px;height:16px;flex:none;margin:0;cursor:pointer}

/* The mono readout panels the operator and API stages show. */
.nx .stage-note{font-family:ui-monospace,monospace;font-size:12px;line-height:1.6;color:var(--dim);white-space:pre-line}

/* Every stage panel is the same column, so the shape is stated once rather
 * than repeated inline on two of the four. */
.nx .stage-panel{display:flex;flex-direction:column;gap:var(--gap)}

/* SMS preview.
 *
 * The MTN and Orange templates are different lengths, so switching network
 * resized the panel and everything below it moved. A fixed height with its own
 * scroll keeps the stage still, and the extra padding lets the text breathe
 * rather than sitting against the border. */
.nx .stage-preview{
  font-family:ui-monospace,monospace;
  font-size:11px;
  line-height:1.6;
  padding:10px 12px;
  background:var(--surface);
  border:1px solid var(--rule-soft);
  color:var(--dim);
  height:88px;
  overflow-y:auto;
  overscroll-behavior:contain;
}

/* Mode toggles. The same declaration block was repeated inline twelve times,
 * once per stage per mode, so a change to the shape meant twelve edits. The
 * active colour stays inline, since the JS sets it per stage. */
.nx .mode-btn-static{cursor:default}
.nx .mode-btn{position:relative;background:transparent;border:none;font:inherit;font-weight:800;font-size:10px;letter-spacing:.12em;text-transform:uppercase;cursor:pointer;padding:0}

/* --- Segmented controls -------------------------------------------------- */
/* filter-btn, sim-pill and net-pair are all "pick one of N" strips. They were
 * three different heights and three different border treatments, so no two
 * strips on the page lined up. They share the scale now. */
.nx .filter-btn,
.nx .sim-pill,
.nx .net-pair button,
.nx .ussd-grid button{display:inline-flex;align-items:center;justify-content:center;line-height:1}

/* --- Stage panel chrome -------------------------------------------------- */
/* Four stages share one structure: head, mode toggle, strip, body. Each was
 * styled by inline attributes repeated four times, so the bodies had drifted
 * to different gaps and the toggle track sat off the control scale. */
/* Stage title. The index sits in its own gutter so the four names align on a
 * common left edge regardless of index width, and the subtitle reads as a
 * caption rather than as fine print. */
.nx .stage-head{display:flex;align-items:center;gap:var(--gap-loose);padding:10px 14px;height:68px;overflow:hidden}
.nx .stage-head-idx{font-family:ui-monospace,monospace;white-space:nowrap;font-size:13px;font-weight:700;color:var(--accent);flex:none;width:18px;text-align:left}
.nx .stage-head-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}
.nx .stage-head-name{font-weight:800;font-size:17px;line-height:1.15;letter-spacing:-.01em}
.nx .stage-head-sub{font-size:12px;color:var(--mute);line-height:1.25}
.nx .stage-toggle{padding:0 14px 8px}
.nx .stage-knob{position:absolute;top:0;bottom:0;width:33.3333%;transition:left .16s cubic-bezier(.4,0,.2,1),background .16s}
.nx .stage-toggle-track{position:relative;display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--rule);height:var(--ctl-md);user-select:none}
.nx .stage-strip{display:flex;align-items:center;gap:var(--gap);padding:6px 14px;height:var(--ctl-lg);overflow:hidden;border-top:1px solid var(--rule-soft);border-bottom:1px solid var(--rule-soft)}
.nx .stage-body{flex:1;min-height:0;overflow-y:auto;padding:10px 14px 14px;display:flex;flex-direction:column;gap:var(--gap)}
.nx .stage-row{display:flex;align-items:baseline;gap:var(--gap);padding:5px 0;border-bottom:1px solid var(--rule-faint)}
.nx .stage-row:last-child{border-bottom:none}

/* Header controls. btn-ping is an icon button and btn-toggle-band a labelled
 * one, both styled inline and both off the scale. */
.nx .hdr-icon-btn{display:flex;align-items:center;justify-content:center;width:var(--ctl-sm);height:var(--ctl-sm);background:transparent;border:1px solid var(--rule);border-radius:0;cursor:pointer;color:var(--mute);padding:0;flex:none}
.nx .hdr-btn{display:flex;align-items:center;gap:var(--gap-tight);height:var(--ctl-sm);background:transparent;border:1px solid var(--rule);color:var(--mute);cursor:pointer;padding:var(--ctl-pad-sm);font:inherit;font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;white-space:nowrap}
.nx .hdr-icon-btn:hover,.nx .hdr-btn:hover{background:var(--rule-faint);color:var(--ink)}

/* Top bar rails. Three identical inline blocks differing only in which side
 * carries the divider. */
.nx .hdr-rail{width:var(--ctl-rail);flex:none;display:flex;align-items:center;justify-content:center;background:transparent;border:none;color:var(--ink);cursor:pointer}
.nx .hdr-rail-l{border-right:2px solid var(--rule)}
.nx .hdr-rail-r{border-left:2px solid var(--rule)}
.nx .hdr-rail:hover{background:var(--rule-faint)}

/* --- Top bar -------------------------------------------------------------
 * Env, Agent and API were three inline flex rows with a repeated label span
 * and no shared alignment, so the values sat at three different x positions
 * and the whole bar read as loose text. Each is a cell now: a small uppercase
 * label over a monospace value, separated by rules, so the bar reads as an
 * instrument panel and the values line up on their baselines. */
/* --- Top bar -------------------------------------------------------------
 * Stacking a 9px label over a 12px value inside a 44px bar left both lines
 * cramped and the bar read as squashed. Label and value sit inline on one
 * baseline instead, which is how an instrument reads a value out, and the bar
 * gets the height to breathe. Cells are separated by rules and the connection
 * cell carries its own actions. */
.nx .hdr{flex:none;display:flex;align-items:stretch;height:60px;border-bottom:2px solid var(--rule);background:var(--ground)}
.nx .hdr-brand{flex:none;width:calc(var(--sb-w) - var(--ctl-rail));display:flex;align-items:center;gap:11px;padding:0 18px;border-right:2px solid var(--rule);white-space:nowrap;overflow:hidden}
.nx .hdr-brand-logo{width:28px;height:28px;flex:none;object-fit:contain}
.nx .hdr-brand-txt{display:flex;flex-direction:column;gap:2px;line-height:1}
.nx .hdr-brand-name{font-weight:800;font-size:19px;letter-spacing:-.03em}
.nx .hdr-brand-sub{font-size:9px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--mute)}

.nx .hdr-meta{flex:1;min-width:0;display:flex;align-items:stretch;justify-content:space-between}
.nx .hdr-cell{display:flex;align-items:center;gap:var(--gap);padding:0 18px;white-space:nowrap;min-width:0}
.nx .hdr-cell-k{font-size:9px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--mute);flex:none}
.nx .hdr-cell-v{font-family:ui-monospace,monospace;font-size:13px;line-height:1;min-width:0}
.nx .hdr-cell-url{overflow:hidden;text-overflow:ellipsis;color:var(--ink)}
/* Latency and agent name are absent until measured or signed in. An empty
 * element still draws its gap, so it collapses rather than leaving a hole. */
.nx .hdr-cell-lat{color:var(--mute);font-size:12px}
.nx .hdr-cell-lat:empty,.nx .hdr-cell-agent:empty{display:none}
.nx .hdr-cell-conn{flex:1;min-width:0}
.nx .hdr-cell-session{flex:none;border-left:1px solid var(--rule-soft)}
.nx .hdr-health-dot{width:9px;height:9px;flex:none;border-radius:50%;background:var(--idle);transition:background .3s}

.nx .hdr-conn{display:inline-flex;align-items:center;gap:var(--gap-tight);font-size:10px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;padding:0 7px;height:22px;border:1px solid var(--rule-soft);color:var(--mute)}
.nx .hdr-conn[data-state="ok"]{color:var(--ok);border-color:var(--ok)}
.nx .hdr-conn[data-state="bad"]{color:var(--bad);border-color:var(--bad)}
.nx .hdr-conn[data-state="auth"]{color:var(--warn);border-color:var(--warn)}
.nx .hdr-conn[data-state="busy"]{color:var(--accent);border-color:var(--accent)}

/* --- Nexus API stage -----------------------------------------------------
 * The queue counts were stage-rows carrying an inline dot, an inline label and
 * an inline number, so the four rows had no shared column and the numbers did
 * not align. A grid gives the dot, name and count fixed tracks, and the counts
 * are tabular so the digits sit still as they change. */
.nx .api-counts{display:flex;flex-direction:column}
.nx .api-count{display:grid;grid-template-columns:9px 1fr auto;align-items:center;gap:var(--gap);height:var(--ctl-md);border-bottom:1px solid var(--rule-faint)}
.nx .api-count:last-child{border-bottom:none}
.nx .api-count-dot{width:9px;height:9px}
.nx .api-count-k{font-size:12px;letter-spacing:.02em;color:var(--dim)}
.nx .api-count-v{font-family:ui-monospace,monospace;font-weight:800;font-size:16px;font-variant-numeric:tabular-nums;text-align:right}
.nx .api-count[data-tone="running"] .api-count-dot{animation:nx-pulse 1.2s infinite}
.nx .api-hb{display:flex;align-items:baseline;justify-content:space-between;gap:var(--gap);font-size:11px;color:var(--mute);margin-top:auto;padding-top:var(--gap-loose);border-top:1px solid var(--rule-soft)}
.nx .api-hb-k{letter-spacing:.1em;text-transform:uppercase}
.nx .api-hb-v{font-family:ui-monospace,monospace;color:var(--ink);font-variant-numeric:tabular-nums}

/* The USSD preview describes the sequence the button will run, so it belongs to
 * the button rather than floating between the two. Grouping them keeps one gap
 * above the pair instead of a gap on each side of the caption, which is what
 * made deposit and balance mode sit at different distances from the button. */
.nx .n8n-commit{display:flex;flex-direction:column;gap:var(--gap-tight);margin-top:auto}

/* Related controls inside a stage panel. A flat panel gave every control the
 * same gap, so the network switch, its preview, the auto checkbox and the
 * commit button read as four unrelated rows. Grouping tightens what belongs
 * together and lets the panel gap separate the groups. */
.nx .stage-group{display:flex;flex-direction:column;gap:var(--gap-tight)}
.nx .stage-group-end{margin-top:auto}
.nx .stage-sub-label{font-size:9px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--mute)}
