/* No remote @import for Inter here: the CSP this app serves (style-src 'self'
   'unsafe-inline', see securityHeaders in main.go) pins style sources to the
   panel's own origin, so a fonts.googleapis.com @import has never actually
   loaded a font — only ever thrown a CSP violation on every page load. The
   'sans-serif' fallback below is what has been rendering all along; removing
   the dead import changes nothing but the console noise. */

body {
    background-color: #0f172a;
    color: #f8fafc;
    font-family: 'Inter', sans-serif;
    margin: 0;
    padding: 20px;
    overflow-x: hidden;
}

.container { width: 100%; margin: 0; }

.main-layout { display: flex; gap: 24px; align-items: flex-start; }
.left-col { width: 760px; flex-shrink: 0; }
.right-col { flex: 1; position: sticky; top: 20px; }

/* A panel carrying a resize grip. Establishes the containing block the
   absolutely-positioned width grip is placed against.

   Every panel carries a width grip on its right edge, and each one drags the
   whole left column rather than its own panel: the panels share a single flex
   column, so they necessarily share its width (see applyLeftColumnWidth in
   app-config.js). Putting a grip on each simply means the column can be grabbed
   from whichever panel happens to be on screen. Height, by contrast, IS
   per-panel — see .panel-resize-grip-v and applyPanelHeight. */
.panel-resizable { position: relative; }
.panel-resize-grip {
    position: absolute;
    top: 0;
    right: -2px;
    width: 10px;
    height: 100%;
    cursor: ew-resize;
    border-radius: 0 12px 12px 0;
    background: transparent;
    transition: background-color 0.15s;
}
.panel-resize-grip::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 3px;
    width: 3px;
    height: 34px;
    margin-top: -17px;
    border-radius: 2px;
    background: rgba(148, 163, 184, 0.35);
    transition: background-color 0.15s;
}
.panel-resize-grip:hover::after,
body.panel-resizing .panel-resize-grip::after { background: #38bdf8; }
.panel-resize-grip:hover { background: rgba(56, 189, 248, 0.08); }
/* While dragging, stop the pointer selecting text across the page. */
body.panel-resizing { cursor: ew-resize; user-select: none; }

/* Horizontal twin of the above: sits under a panel's content and drags its
   height. Unlike the column grip this one takes layout space, so the panel
   grows by its height rather than the grip overlapping the content. */
.panel-resize-grip-v {
    height: 12px;
    margin: 6px -8px -8px -8px;
    cursor: ns-resize;
    border-radius: 0 0 12px 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    transition: background-color 0.15s;
}
.panel-resize-grip-v::after {
    content: "";
    width: 34px;
    height: 3px;
    border-radius: 2px;
    background: rgba(148, 163, 184, 0.35);
    transition: background-color 0.15s;
}
.panel-resize-grip-v:hover::after,
body.panel-resizing-v .panel-resize-grip-v::after { background: #38bdf8; }
.panel-resize-grip-v:hover { background: rgba(56, 189, 248, 0.08); }
body.panel-resizing-v { cursor: ns-resize; user-select: none; }

h1 {
    font-size: 2.3rem;
    font-weight: 800;
    color: #7dd3fc;
    margin: 0 0 5px 0;
    text-align: center;
}

p.subtitle { color: #94a3b8; margin: 0; font-size: 1rem; }

.card {
    background: rgba(30, 41, 59, 0.7);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 12px;
    padding: 20px 24px;
    margin-bottom: 20px;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
}

h2 {
    margin: 0 0 16px 0;
    font-size: 1.2rem;
    color: #e2e8f0;
    border-bottom: 1px solid rgba(255,255,255,0.1);
    padding-bottom: 10px;
}

/* --- Buttons: icon + label --- */
.act {
    display: inline-block;
    padding: 7px 12px;
    margin: 0 6px 6px 0;
    font-size: 0.82rem;
    font-weight: 600;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    color: #fff;
    font-family: 'Inter', sans-serif;
    transition: all 0.15s;
}
.act:hover { transform: translateY(-1px); box-shadow: 0 6px 12px -3px rgba(0,0,0,0.4); }
.act:disabled { opacity: 0.4; cursor: not-allowed; transform: none; box-shadow: none; }

.btn-artifact { background: #2563eb; }
.btn-s3up { background: #059669; }
.btn-s3down { background: #6d28d9; }
.btn-jenkins { background: #0891b2; }
.btn-setup { background: #d97706; }
.btn-install-all { background: #047857; }
.btn-start-all { background: #5b21b6; }
.btn-stop-all { background: #b45309; }
.btn-restart-all { background: #0e7490; }
.btn-status { background: #475569; }
.btn-custom-action { background: #4f46e5; }
.btn-custom-snap { background: #9d174d; }

.sm-collect { background: #2563eb; }
.sm-install { background: #059669; }
.sm-start { background: #7c3aed; }
.sm-stop { background: #b45309; }
.sm-restart { background: #0e7490; }
.sm-status { background: #475569; }
.sm-log { background: #334155; }
.sm-exec { background: #0d9488; }
.sm-danger { background: #b91c1c; }

/* --- Guided workflow strip --- */
.workflow { font-size: 0; }
.wstep {
    display: inline-block; vertical-align: top;
    width: 48%;
    box-sizing: border-box;
    margin-bottom: 12px;
    padding: 12px;
    font-size: 0.85rem;
    background: rgba(15, 23, 42, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 8px;
}
.wstep:nth-child(odd) { margin-right: 3%; }
.wnum {
    display: inline-block; width: 22px; height: 22px; line-height: 22px;
    text-align: center; border-radius: 50%; background: #334155;
    color: #e2e8f0; font-size: 0.78rem; font-weight: 600; margin-bottom: 8px;
}
.wlabel { font-weight: 600; color: #e2e8f0; margin-bottom: 10px; font-size: 0.95rem; }
.src-select { display: block; margin: 0 0 10px 0; padding: 6px 8px; border-radius: 6px; background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569; font-family: 'Inter', sans-serif; font-size: 0.8rem; }
body.theme-light .src-select { background: #ffffff; color: #0f172a; border: 1px solid #cbd5e1; }
body.theme-matrix .src-select { background: #000000; color: #00ff00; border: 1px solid #00ff00; }
.danger-zone { margin-top: 6px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,0.08); }

/* --- Toolbar --- */
.toolbar { display: flex; flex-wrap: wrap; align-items: center; margin-bottom: 14px; }
.filter-input {
    padding: 8px 12px; border-radius: 6px; border: 1px solid #475569;
    background: rgba(15,23,42,0.6); color: #f8fafc; font-family: 'Inter', sans-serif;
    font-size: 0.85rem; width: 200px; margin: 0 12px 6px 0;
}
.selall { font-size: 0.85rem; color: #cbd5e1; margin: 0 14px 6px 0; cursor: pointer; }

/* --- Service cards (inline-block for reliable IE wrapping) --- */
.grid { display: flex; flex-wrap: wrap; }
.service-item {
    flex: 1 1 185px; min-width: 175px; max-width: 280px;
    box-sizing: border-box;
    background: rgba(15, 23, 42, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.05);
    border-radius: 8px;
    padding: 14px;
    margin: 0 12px 12px 0;
    font-size: 0.85rem;
}
.svc-head { margin-bottom: 8px; }
.svc-name { display: block; font-weight: 600; color: #e2e8f0; font-size: 0.92rem; cursor: pointer; word-break: break-all; flex: 1; padding-right: 8px; }
.svc-name input { vertical-align: middle; margin-right: 6px; }
.svc-port { display: inline-block; font-size: 0.75rem; color: #38bdf8; background: rgba(56, 189, 248, 0.15); padding: 3px 8px; border-radius: 6px; border: 1px solid rgba(56, 189, 248, 0.3); font-weight: 600; margin-top: 0; }
.svc-statusrow { margin-bottom: 12px; }
.service-actions { line-height: 1; }
.service-actions .act, .toolbar .jbtn { width: 40px; height: 32px; padding: 0; text-align: center; line-height: 32px; }
.toolbar .btn-status { width: auto; padding: 0 12px; height: 32px; line-height: 32px; }

.pill { display: inline-block; font-size: 0.72rem; font-weight: 600; padding: 3px 9px; border-radius: 10px; }
.st-run { background: rgba(16,185,129,0.18); color: #34d399; }
.st-stop { background: rgba(245,158,11,0.18); color: #fbbf24; }
.st-none { background: rgba(148,163,184,0.18); color: #94a3b8; }
.st-unknown { background: rgba(148,163,184,0.10); color: #64748b; }

/* --- Summary chips --- */
.chip { display: inline-block; font-size: 0.75rem; font-weight: 600; padding: 4px 10px; border-radius: 10px; margin-left: 6px; }

/* --- Merged status pill --- */
#summary { text-align: center; }
.status-merged {
    display: inline-flex; align-items: stretch;
    background: rgba(15, 23, 42, 0.65);
    border: 1px solid rgba(255, 255, 255, 0.14);
    border-radius: 999px; overflow: hidden;
    font-size: 0.82rem; font-weight: 600; color: #cbd5e1;
    box-shadow: 0 3px 12px rgba(0, 0, 0, 0.28);
}
/* Note: Trident has no flexbox `gap`, so spacing uses explicit margins. */
.status-merged .seg { display: inline-flex; align-items: center; padding: 6px 18px; }
.status-merged .seg + .seg { border-left: 1px solid rgba(255, 255, 255, 0.1); }
.status-merged .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; margin-right: 8px; }
.status-merged .seg-num { font-weight: 800; font-size: 0.95rem; margin-right: 5px; }
.status-merged .seg-lbl { color: #94a3b8; }
.status-merged .d-run { background: #34d399; box-shadow: 0 0 7px rgba(52, 211, 153, 0.75); }
.status-merged .d-stop { background: #fbbf24; box-shadow: 0 0 7px rgba(251, 191, 36, 0.75); }
.status-merged .d-none { background: #94a3b8; }
.status-merged .seg-run .seg-num { color: #34d399; }
.status-merged .seg-stop .seg-num { color: #fbbf24; }
.status-merged .seg-none .seg-num { color: #cbd5e1; }
body.theme-light .status-merged .seg-lbl { color: #64748b; }
body.theme-light .status-merged .seg-none .seg-num { color: #334155; }
body.theme-light .status-merged { background: #fff; border-color: #e2e8f0; color: #334155; }
body.theme-light .status-merged .seg + .seg { border-left-color: #e2e8f0; }
body.theme-matrix .status-merged { background: #000; border-color: #00ff00; color: #00ff00; border-radius: 0; }
body.theme-matrix .status-merged .seg + .seg { border-left-color: #00ff00; }
body.theme-matrix .status-merged .dot { border-radius: 0; }

/* Services panel header: title on the left, status-count pill on the right,
   sharing one underline (the pill was moved here from the console header). */
.svc-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 10px; }
.svc-head h2 { margin: 0; border: none; padding: 0; }
body.theme-light .svc-head { border-bottom-color: #cbd5e1; }
body.theme-matrix .svc-head { border-bottom: 1px dashed #00ff00; }

/* --- Console --- */
.console-card { height: calc(100vh - 130px); display: flex; flex-direction: column; margin-bottom: 0; }
.console-box {
    background: #020617; border: 1px solid #334155; border-radius: 8px;
    padding: 14px; font-family: 'Consolas', 'Courier New', monospace;
    font-size: 0.84rem; color: #10b981; flex: 1; overflow-y: auto;
    white-space: pre-wrap; box-shadow: inset 0 2px 4px rgba(0,0,0,0.5);
}
#run-indicator { display: none; align-items: center; font-size: 0.78rem; color: #fbbf24; margin-left: 12px; }
#tail-indicator { display: none; align-items: center; font-size: 0.78rem; color: #34d399; margin-left: 12px; }

/* Console header spacing — the inline `gap` on these flex rows is ignored by
   Trident (HTA/IE11), which collapsed labels together (e.g. "CPU0%"). Restore
   the spacing with explicit margins. */
#console-metrics-panel > div + div { margin-left: 8px; }
#console-metrics-panel > div > span + span { margin-left: 6px; }
#console-log-lines { margin-left: 8px; }
.console-card .util-btn + .util-btn { margin-left: 8px; }

/* Sticky so the environment/theme/role controls and the Settings/Reload
   buttons stay reachable while a long panel list is scrolled. top:0 with no
   gap above it means it rides down with the page for the first 20px (matching
   body's own padding) and then pins flush to the viewport top — no jump.

   The negative side margins cancel body's 20px side padding so the bar reaches
   the viewport edges once stuck, rather than leaving a gap at each side that
   would show scrolled-past content through it. body has overflow-x:hidden
   (see top of file) specifically so this can't introduce a horizontal
   scrollbar. The 600px breakpoint below matches body's padding change there. */
.header-container {
    display: flex; align-items: center; gap: 16px;
    position: sticky; top: 0; z-index: 40;
    background-color: #0f172a;
    margin: 0 -20px 16px -20px;
    padding: 14px 20px;
}
body.theme-light .header-container { background-color: #f1f5f9; }
body.theme-matrix .header-container { background-color: #000000; }
.header-center { flex: 1 1 auto; text-align: center; min-width: 0; }
.logo-img {
    flex-shrink: 0;
    width: 64px; height: 64px;
    border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.3);
}
/* --- Unified Header Controls --- */
.header-controls-panel {
    flex-shrink: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 8px;
    background: rgba(30, 41, 59, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.08);
    padding: 8px 12px;
    border-radius: 10px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    backdrop-filter: blur(8px);
}

/* --- Compact-bar header controls (Option B) --- */
.hc-field {
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(15, 23, 42, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 8px;
    padding: 2px 8px;
}
.hc-field:hover { border-color: rgba(255, 255, 255, 0.3); }
.hc-tag {
    font-size: 0.6rem; font-weight: 700; letter-spacing: 0.06em;
    text-transform: uppercase; color: #94a3b8; line-height: 1;
    padding-right: 2px; border-right: 1px solid rgba(255,255,255,0.12);
}
body.theme-light .hc-tag { color: #64748b; border-right-color: rgba(15,23,42,0.12); }
body.theme-matrix .hc-tag { color: #00aa00; border-right-color: #00aa00; }
.hc-field select {
    background-color: #1e293b;
    border: none;
    color: #f8fafc;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    outline: none;
    padding: 4px 2px;
}
/* Trident draws the open dropdown list using the select/option background, not the field's —
   without an explicit dark fill the popup falls back to white and the white text vanishes. */
.hc-field select option { background-color: #1e293b; color: #f8fafc; }
body.theme-light .hc-field { background: #fff; border: 1px solid #cbd5e1; }
body.theme-light .hc-field select { background-color: #fff; color: #0f172a; }
body.theme-light .hc-field select option { background-color: #fff; color: #0f172a; }
body.theme-matrix .hc-field { background: #000; border: 1px solid #00ff00; border-radius: 0; }
body.theme-matrix .hc-field select { background-color: #000; color: #00ff00; }
body.theme-matrix .hc-field select option { background-color: #000; color: #00ff00; }

.hc-divider { width: 1px; align-self: stretch; background: rgba(255,255,255,0.12); margin: 2px 1px; }
body.theme-light .hc-divider { background: rgba(15,23,42,0.12); }
body.theme-matrix .hc-divider { background: #00ff00; }

.ico-btn {
    width: 38px; height: 34px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    line-height: 1;
}
.ico-btn svg { display: block; }

.toggle {
    display: inline-flex; align-items: center; gap: 6px;
    cursor: pointer; user-select: none;
    color: #94a3b8; font-size: 0.75rem; font-weight: 600;
}
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track {
    position: relative; width: 32px; height: 18px; flex: none;
    border-radius: 10px; background: rgba(255,255,255,0.2);
    transition: background 0.15s;
}
.toggle-track::after {
    content: ''; position: absolute; top: 2px; left: 2px;
    width: 14px; height: 14px; border-radius: 50%; background: #fff;
    transition: left 0.15s;
}
.toggle input:checked + .toggle-track { background: #10b981; }
.toggle input:checked + .toggle-track::after { left: 16px; }
body.theme-light .toggle { color: #64748b; }
body.theme-matrix .toggle { color: #00aa00; }
body.theme-matrix .toggle input:checked + .toggle-track { background: #00ff00; }
body.theme-light .header-controls-panel {
    background: rgba(255, 255, 255, 0.7);
    border: 1px solid rgba(15, 23, 42, 0.08);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
body.theme-matrix .header-controls-panel {
    background: #000;
    border: 1px solid #00ff00;
    box-shadow: 0 0 8px rgba(0, 255, 0, 0.2);
}

.controls-row {
    display: flex;
    gap: 20px;
    width: 100%;
}

.control-group {
    display: flex;
    align-items: center;
    gap: 6px;
}
.control-group label {
    font-size: 0.75rem;
    color: #94a3b8;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
body.theme-light .control-group label { color: #64748b; }
body.theme-matrix .control-group label { color: #00aa00; }

.control-group select {
    padding: 4px 8px;
    border-radius: 6px;
    background: rgba(15, 23, 42, 0.6);
    color: #f8fafc;
    border: 1px solid rgba(255, 255, 255, 0.15);
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    cursor: pointer;
    outline: none;
    transition: border-color 0.15s;
}
.control-group select:hover {
    border-color: rgba(255, 255, 255, 0.3);
}
body.theme-light .control-group select {
    background: #fff;
    color: #0f172a;
    border: 1px solid #cbd5e1;
}
body.theme-light .control-group select:hover {
    border-color: #94a3b8;
}
body.theme-matrix .control-group select {
    background: #000;
    color: #00ff00;
    border: 1px solid #00ff00;
}

.controls-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 0.8rem;
}

.util-btn {
    padding: 5px 10px;
    font-size: 0.75rem;
    font-weight: 600;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.07);
    color: #f8fafc;
    border: 1px solid rgba(255, 255, 255, 0.12);
    cursor: pointer;
    font-family: 'Inter', sans-serif;
    transition: all 0.15s;
}
.util-btn:hover {
    background: rgba(255, 255, 255, 0.15);
    border-color: rgba(255, 255, 255, 0.25);
}
body.theme-light .util-btn {
    background: rgba(15, 23, 42, 0.05);
    color: #334155;
    border: 1px solid rgba(15, 23, 42, 0.12);
}
body.theme-light .util-btn:hover {
    background: rgba(15, 23, 42, 0.1);
    border-color: rgba(15, 23, 42, 0.2);
}
body.theme-matrix .util-btn {
    background: #000;
    color: #00ff00;
    border: 1px solid #00ff00;
    border-radius: 0;
}
body.theme-matrix .util-btn:hover {
    background: #00ff00;
    color: #000;
}

.auto-reload-label {
    display: flex;
    align-items: center;
    gap: 4px;
    color: #94a3b8;
    cursor: pointer;
    user-select: none;
    font-size: 0.75rem;
}
.auto-reload-label input {
    cursor: pointer;
    margin: 0;
}
body.theme-light .auto-reload-label { color: #64748b; }
body.theme-matrix .auto-reload-label { color: #00aa00; }

body.theme-light #server-time, body.theme-light #metric-cpu, body.theme-light #metric-ram, body.theme-light #metric-disk { color: #0f172a !important; }
body.theme-matrix #server-time, body.theme-matrix #metric-cpu, body.theme-matrix #metric-ram, body.theme-matrix #metric-disk { color: #00ff00 !important; }
/* Metric labels (CPU:/RAM:/Disk:) inherit a faint gray inline color — darken on the light card */
body.theme-light #console-metrics-panel { color: #475569 !important; }
body.theme-matrix #console-metrics-panel { color: #00aa00 !important; }

/* Modal Styles */
.modal-overlay { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); z-index: 1000; align-items: center; justify-content: center; }
.modal-content { position: relative; background: #1e293b; border: 1px solid #475569; border-radius: 12px; width: 1100px; max-width: 94vw; height: 640px; max-height: 88vh; display: flex; flex-direction: column; overflow: hidden; box-shadow: 0 20px 25px -5px rgba(0,0,0,0.5); }

/* Resize handles — added to every .modal-content by resizable-modal.js.
   Corner handles get a visible bracket so the drag affordance is discoverable
   against the dark theme; edge handles are slim, functional strips.
   .modal-content has border-radius:12px + overflow:hidden, and that rounded
   clip also swallows hit-testing right at the pixel corner (0,0) — a handle
   placed flush in the corner is visually there but never receives the
   mousedown, it falls through to the box itself. Insetting by 6px keeps the
   whole handle inside the curve (6,6 is within the 12px radius) so it's
   actually clickable. */
.resize-handle { position: absolute; z-index: 5; }
.resize-corner { width: 14px; height: 14px; }
.resize-corner::after {
    content: "";
    position: absolute;
    width: 9px;
    height: 9px;
    border: 2px solid #38bdf8;
    opacity: 0.6;
    border-radius: 2px;
    transition: opacity 0.15s, background 0.15s;
}
.resize-corner:hover::after { opacity: 1; background: rgba(56, 189, 248, 0.35); }
.rh-nw { top: 6px; left: 6px; }
.rh-nw::after { top: 0; left: 0; border-right: none; border-bottom: none; }
.rh-ne { top: 6px; right: 6px; }
.rh-ne::after { top: 0; right: 0; border-left: none; border-bottom: none; }
.rh-sw { bottom: 6px; left: 6px; }
.rh-sw::after { bottom: 0; left: 0; border-right: none; border-top: none; }
.rh-se { bottom: 6px; right: 6px; }
.rh-se::after { bottom: 0; right: 0; border-left: none; border-top: none; }
.rh-n, .rh-s { left: 16px; right: 16px; height: 6px; }
.rh-n { top: 0; }
.rh-s { bottom: 0; }
.rh-e, .rh-w { top: 16px; bottom: 16px; width: 6px; }
.rh-e { right: 0; }
.rh-w { left: 0; }
.rh-n:hover, .rh-s:hover, .rh-e:hover, .rh-w:hover { background: rgba(56, 189, 248, 0.25); }
body.theme-light .resize-corner::after { border-color: #0284c7; }
body.theme-light .rh-n:hover, body.theme-light .rh-s:hover, body.theme-light .rh-e:hover, body.theme-light .rh-w:hover { background: rgba(2, 132, 199, 0.2); }
body.theme-matrix .resize-corner::after { border-color: #00ff00; }
body.theme-matrix .rh-n:hover, body.theme-matrix .rh-s:hover, body.theme-matrix .rh-e:hover, body.theme-matrix .rh-w:hover { background: rgba(0, 255, 0, 0.2); }
.modal-header { flex: 0 0 auto; padding: 16px 24px; border-bottom: 1px solid #334155; display: flex; justify-content: space-between; align-items: center; background: #1e293b; z-index: 2; }
.modal-header h2 { margin: 0; border: none; padding: 0; }
.modal-body { flex: 1 1 auto; padding: 24px; overflow-y: auto; }
.modal-footer { flex: 0 0 auto; padding: 16px 24px; border-top: 1px solid #334155; display: flex; justify-content: flex-end; gap: 12px; background: #1e293b; z-index: 2; }
.env-btn { padding: 8px 12px; font-size: 0.85rem; border-radius: 6px; cursor: pointer; border: none; color: white; font-weight: 600; }
.btn-test { background: #0891b2; }
.btn-del { background: #b91c1c; }
.btn-add { background: #059669; }
.btn-save { background: #2563eb; }
.btn-primary { background: #2563eb; }
.btn-cancel { background: #475569; }
.test-res { width: 24px; text-align: center; font-size: 1.1rem; }

/* Light Theme */
body.theme-light { background-color: #f1f5f9; color: #334155; }
body.theme-light h1 { color: #2563eb; }
body.theme-light .card { background: #ffffff; border: 1px solid #e2e8f0; color: #1e293b; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1); }
body.theme-light h2 { color: #0f172a; border-bottom: 1px solid #cbd5e1; }
body.theme-light .service-item { background: #f8fafc; border: 1px solid #e2e8f0; }
body.theme-light .svc-name { color: #0f172a; }
body.theme-light .wstep { background: #f1f5f9; border: 1px solid #e2e8f0; }
body.theme-light .wlabel { color: #0f172a; }
body.theme-light .console-box { background: #020617; color: #10b981; border: 1px solid #cbd5e1; box-shadow: inset 0 2px 4px rgba(0,0,0,0.3); }
body.theme-light p.subtitle { color: #64748b; }
body.theme-light .theme-selector select { background: #ffffff; color: #0f172a; border: 1px solid #cbd5e1; }
body.theme-light .filter-input { background: #ffffff; color: #0f172a; border: 1px solid #cbd5e1; }

/* Matrix Theme */
body.theme-matrix { background-color: #000000; color: #00ff00; }
body.theme-matrix h1 { background: none; -webkit-text-fill-color: #00ff00; color: #00ff00; text-shadow: 0 0 5px #00ff00; }
body.theme-matrix p.subtitle { color: #00aa00; }
body.theme-matrix .card { background: #000000; border: 1px solid #00ff00; box-shadow: 0 0 10px rgba(0,255,0,0.1); }
body.theme-matrix h2 { color: #00ff00; border-bottom: 1px dashed #00ff00; }
body.theme-matrix .service-item, body.theme-matrix .wstep { background: #000000; border: 1px solid #00aa00; }
body.theme-matrix .svc-name, body.theme-matrix .wlabel { color: #00ff00; }
body.theme-matrix .act { background: #000000; border: 1px solid #00ff00; color: #00ff00; border-radius: 0; }
body.theme-matrix .act:hover { background: #00ff00; color: #000000; }
body.theme-matrix .console-box { background: #000000; color: #00ff00; border: 1px solid #00ff00; box-shadow: none; font-family: monospace; }
body.theme-matrix .theme-selector select, body.theme-matrix .filter-input { background: #000000; color: #00ff00; border: 1px solid #00ff00; }

/* ===================== Customization: roles, settings tabs, color pickers ===================== */

/* Header role chip */
.role-chip {
    display: inline-flex; align-items: center; gap: 4px; white-space: nowrap;
    padding: 6px 10px; font-size: 0.72rem; font-weight: 700;
    border-radius: 8px; cursor: pointer; font-family: 'Inter', sans-serif;
    border: 1px solid transparent; transition: all 0.15s;
}
/* Scoped to the chip: applyBodyClasses puts "role-admin" / "role-viewer" on
   <body> too, so an unscoped .role-admin painted the whole page with the chip's
   green background. */
.role-chip.role-admin { background: rgba(16,185,129,0.15); color: #34d399; border-color: rgba(16,185,129,0.35); }
.role-chip.role-admin:hover { background: rgba(16,185,129,0.25); }
.role-viewer-chip { background: rgba(148,163,184,0.18); color: #cbd5e1; border-color: rgba(148,163,184,0.35); }
.role-viewer-chip:hover { background: rgba(148,163,184,0.28); }
body.theme-light .role-viewer-chip { color: #475569; background: rgba(15,23,42,0.06); border-color: rgba(15,23,42,0.18); }
body.theme-light .role-chip.role-admin { color: #059669; }
body.theme-matrix .role-chip { border-radius: 0; }

/* The update chip. Blue rather than the role chip's green: it is a notice, not
   a statement about who you are, and it sits next to the role chip where two
   things the same colour would read as one control. Urgent is amber for a
   mandatory release -- red is what the panel uses for something that has
   already gone wrong, and an update that is merely required has not. */
.update-chip { background: rgba(56,189,248,0.15); color: #38bdf8; border-color: rgba(56,189,248,0.4); }
.update-chip:hover { background: rgba(56,189,248,0.28); }
.update-chip-urgent { background: rgba(245,158,11,0.18); color: #fbbf24; border-color: rgba(245,158,11,0.45); }
.update-chip-urgent:hover { background: rgba(245,158,11,0.3); }
body.theme-light .update-chip { color: #0369a1; }
body.theme-light .update-chip-urgent { color: #b45309; }

/* The edition badge: which product this licence is, Pro or Enterprise.
   Deliberately the quietest chip in the row. It states a standing fact rather
   than asking for anything, so it must not compete with the role chip (who you
   are) or the update chip (something to do) -- slate for Pro, and a restrained
   violet for Enterprise so the two tiers are told apart at a glance without the
   header turning into a colour chart. Trial borrows the update chip's amber:
   it is the one edition with a deadline attached. */
.edition-chip { background: rgba(148,163,184,0.16); color: #cbd5e1; border-color: rgba(148,163,184,0.32); cursor: default; }
.edition-chip:hover { background: rgba(148,163,184,0.24); }
.edition-enterprise { background: rgba(167,139,250,0.16); color: #c4b5fd; border-color: rgba(167,139,250,0.38); }
.edition-enterprise:hover { background: rgba(167,139,250,0.26); }
.edition-trial { background: rgba(245,158,11,0.16); color: #fbbf24; border-color: rgba(245,158,11,0.4); }
.edition-trial:hover { background: rgba(245,158,11,0.26); }
body.theme-light .edition-chip { color: #475569; background: rgba(15,23,42,0.06); border-color: rgba(15,23,42,0.18); }
body.theme-light .edition-enterprise { color: #6d28d9; background: rgba(167,139,250,0.14); border-color: rgba(167,139,250,0.4); }
body.theme-light .edition-trial { color: #b45309; }

/* Hide admin-only controls for Viewer and Operator. Before role-operator was
   ever emitted on <body>, an operator session collapsed to "role-viewer" here
   (see the fix in app-config.js), so this already hid admin-only controls for
   operator -- listing role-operator explicitly keeps that same outcome now
   that its own body class exists, rather than the class going unhandled and
   admin-only controls (bulk install/uninstall, artifact deployment) becoming
   newly visible to operator. Deployer keeps seeing them, exactly as it did
   when it collapsed to "role-admin". */
body.role-viewer .admin-only,
body.role-operator .admin-only { display: none !important; }

/* Settings tabs (modal is always dark) */
/* Wraps rather than overflowing. The row is a single flex line by default, so
   once the tab count outgrew the modal's width the last tabs were pushed
   outside it with no way to reach them -- and the modal is resizable, so any
   fixed assumption about how many fit is wrong at some width. */
.settings-tabs { flex: 0 0 auto; display: flex; flex-wrap: wrap; gap: 4px 4px; padding: 0 20px; border-bottom: 1px solid #334155; background: #1e293b; }
.stab {
    background: transparent; border: none; border-bottom: 2px solid transparent;
    color: #94a3b8; font-family: 'Inter', sans-serif; font-size: 0.85rem; font-weight: 600;
    padding: 10px 14px; cursor: pointer;
}
.stab:hover { color: #e2e8f0; }
.stab.active { color: #7dd3fc; border-bottom-color: #7dd3fc; }

/* Settings fields */
.sfield { display: block; margin-bottom: 16px; }
.sfield > span { display: block; font-size: 0.72rem; font-weight: 700; color: #94a3b8; margin-bottom: 6px; text-transform: uppercase; letter-spacing: 0.05em; }
.sfield input[type=text], .sfield select, .sfield textarea {
    width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 6px;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Inter', sans-serif; font-size: 0.85rem;
}
/* Textareas in the dialog (the email template editor) were left to the
   browser default: a narrow, white, serif box next to the styled inputs. */
.sfield textarea { display: block; font-family: 'Consolas', monospace; font-size: 0.8rem; line-height: 1.5; }
.sfield-row { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; color: #cbd5e1; cursor: pointer; }
.sfield-row input { margin: 0; }

.logo-uploader { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
#cfg-logo-preview { width: 40px; height: 40px; border-radius: 8px; background: rgba(255,255,255,0.05); border: 1px solid #475569; }

/* Custom color rows */
.color-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.color-label { flex: 0 0 110px; font-size: 0.8rem; color: #cbd5e1; }
.color-swatch { width: 26px; height: 26px; border-radius: 5px; border: 1px solid rgba(255,255,255,0.35); flex: none; cursor: pointer; transition: transform 0.1s, box-shadow 0.1s; }
.color-swatch:hover { transform: scale(1.1); box-shadow: 0 0 0 2px rgba(125,211,252,0.4); }
.color-hex {
    flex: 1; padding: 6px 8px; border-radius: 6px; font-family: 'Consolas', monospace; font-size: 0.8rem;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
}

/* Roles panel */
.roles-current { font-size: 0.95rem; margin: 4px 0 12px; color: #f8fafc; }
.roles-note { font-size: 0.85rem; color: #cbd5e1; margin: 12px 0 8px; }
.roles-hint { font-size: 0.75rem; color: #94a3b8; margin-top: 14px; line-height: 1.5; }
/* A hint explaining the checkbox directly above it belongs close to it, but
   .sfield-row is a flex row with no bottom margin — so the 14px above was
   tightened at each call site with an inline margin-top:-8px, which pulled the
   text 8px *into* the label and overlapped it. The relationship is structural,
   so the rule is too: no inline override, and a new hint after a checkbox is
   spaced correctly without anyone remembering to do it. */
.sfield-row + .roles-hint { margin-top: 4px; }
.roles-input {
    display: block; width: 100%; box-sizing: border-box; margin-bottom: 8px; padding: 8px 10px;
    border-radius: 6px; background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Inter', sans-serif; font-size: 0.85rem;
}
.roles-sep { height: 1px; background: #334155; margin: 16px 0; }

/* Environment editor cards */
.env-card { border: 1px solid #334155; border-radius: 8px; padding: 12px; margin-bottom: 12px; background: rgba(15,23,42,0.4); }
.env-card-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.efld { display: flex; flex-direction: column; gap: 4px; flex: 1 1 130px; min-width: 120px; }
.efld > span { font-size: 0.72rem; font-weight: 600; color: #94a3b8; letter-spacing: 0; }
.efld input[type=text], .efld select {
    padding: 7px 9px; border-radius: 6px; background: rgba(15,23,42,0.6); color: #f8fafc;
    border: 1px solid #475569; font-family: 'Inter', sans-serif; font-size: 0.82rem; width: 100%; box-sizing: border-box;
}
/* An environment credential field: a plain text input masked by CSS rather than
   a type=password one, so the browser never recognises a credential form and
   never offers to save an infrastructure password into a personal, cloud-synced
   profile. See envSecretAttrs() in app-settings.js — it only emits this class
   when it has confirmed the property is supported, so there is no case where
   the rule is missing and the value shows in clear. */
.efld input.env-secret {
    -webkit-text-security: disc;
    text-security: disc;
}

/* Rows of the config-diff file picker: what the "Is a directory" dead end was
   replaced with. Clickable, so they read as targets rather than as text. */
.cfgdiff-row {
    padding: 5px 10px; font-size: 0.78rem; color: #e2e8f0; cursor: pointer;
    font-family: ui-monospace, Consolas, monospace; border-bottom: 1px solid rgba(51,65,85,0.5);
}
.cfgdiff-row:last-child { border-bottom: none; }
.cfgdiff-row:hover { background: rgba(56,189,248,0.12); }
.efld-check { flex-direction: row; align-items: center; gap: 6px; flex-basis: 100%; }
.efld-check span { font-size: 0.8rem; color: #cbd5e1; text-transform: none; letter-spacing: 0; font-weight: 400; }
.env-adv { margin-top: 12px; padding-top: 12px; border-top: 1px dashed #334155; }
.env-adv-title { font-size: 0.72rem; color: #94a3b8; margin-bottom: 8px; font-style: italic; }
.env-card-actions { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.efld-inline { display: flex; gap: 6px; align-items: stretch; }
.efld-inline input { flex: 1; min-width: 0; }
.efld-browse { padding: 0 14px; white-space: nowrap; font-size: 0.78rem; flex: 0 0 auto; }
/* An environment's colour (Settings -> Environments). The swatch is the native
   picker, kept to the height of the field beside it; the hex box takes a typed
   value; Clear removes the key. */
.env-colour-pick {
    flex: 0 0 auto; width: 44px; padding: 2px; cursor: pointer;
    background: transparent; border: 1px solid #475569; border-radius: 4px;
}
.env-colour-hex { font-family: ui-monospace, Consolas, monospace; }
.env-colour-note { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* The header chip as it will actually be drawn -- the one thing that tells you
   whether the colour you picked is legible. */
.env-colour-preview {
    display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto;
    height: 28px; padding: 0 11px; border-radius: 6px;
    border: 1px solid #475569; background: rgba(30, 41, 59, 0.4);
    font-size: 12px; font-weight: 600; color: #94a3b8;
}
/* Green, always: the preview shows the colour you are choosing, and the dot is
   the reachability report it never replaces. */
.env-colour-preview-dot {
    width: 7px; height: 7px; border-radius: 50%; flex: none;
    background: #3fb950; box-shadow: 0 0 6px #3fb950;
}
/* Fields that hold a full file path (e.g. the bastion key) get their own row so
   the input has room and the Browse button sits cleanly beside it. */
.efld-wide { flex-basis: 100%; }
.efld-readonly { opacity: 0.6; cursor: not-allowed; }

/* Environment accordion list */
.env-toolbar { display: flex; gap: 8px; align-items: center; margin-bottom: 12px; position: sticky; top: 0; background: #1e293b; padding-bottom: 8px; z-index: 3; }
.env-search { flex: 1; padding: 8px 11px; border-radius: 6px; background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569; font-family: 'Inter', sans-serif; font-size: 0.82rem; box-sizing: border-box; }
.env-toolbar .btn-add { flex: 0 0 auto; white-space: nowrap; }
.env-row { border: 1px solid #334155; border-radius: 8px; margin-bottom: 8px; background: rgba(15,23,42,0.4); overflow: hidden; }
.env-row.open { border-color: #475569; background: rgba(15,23,42,0.6); }
.env-row-head { display: flex; align-items: center; gap: 10px; padding: 11px 13px; cursor: pointer; user-select: none; }
.env-row-head:hover { background: rgba(148,163,184,0.06); }
.env-row-chev { color: #94a3b8; font-size: 0.7rem; width: 12px; flex: 0 0 auto; }
.env-row-name { font-weight: 700; color: #f8fafc; font-size: 0.9rem; }
.env-row-host { color: #94a3b8; font-size: 0.78rem; font-family: monospace; margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 45%; }
.env-badge { font-size: 0.64rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; padding: 2px 8px; border-radius: 999px; flex: 0 0 auto; }
.env-badge-docker { background: rgba(56,189,248,0.15); color: #7dd3fc; }
.env-badge-win { background: rgba(129,140,248,0.15); color: #a5b4fc; }
.env-badge-linux { background: rgba(74,222,128,0.15); color: #86efac; }
.env-cred-saved { color: #86efac; font-weight: 600; }
.env-row-body { padding: 2px 13px 13px; }
.env-empty { color: #94a3b8; font-size: 0.85rem; text-align: center; padding: 18px; }

/* File Explorer right-click context menu */
.ctx-menu { position: fixed; z-index: 2000; min-width: 210px; background: #1e293b; border: 1px solid #475569; border-radius: 8px; padding: 5px; box-shadow: 0 12px 28px -6px rgba(0,0,0,0.55); font-size: 0.82rem; }
.ctx-item { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: 5px; color: #e2e8f0; cursor: pointer; user-select: none; white-space: nowrap; }
.ctx-item:hover { background: rgba(56,189,248,0.14); }
.ctx-item.ctx-danger { color: #f87171; }
.ctx-item.ctx-danger:hover { background: rgba(248,113,113,0.15); }
.ctx-ico { width: 18px; text-align: center; flex: 0 0 auto; }
.ctx-sep { height: 1px; background: #334155; margin: 5px 6px; }

/* Properties / Permissions modal tables */
.props-table { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
.props-table td { padding: 6px 8px; border-bottom: 1px solid rgba(255,255,255,0.06); vertical-align: top; }
.props-k { color: #94a3b8; width: 130px; white-space: nowrap; }
.props-v { color: #f1f5f9; font-family: monospace; word-break: break-all; }
.props-note { color: #94a3b8; font-size: 0.8rem; margin: 12px 0 0; }
.props-edit { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.props-edit label { color: #94a3b8; font-size: 0.82rem; }
.props-edit input { width: 90px; padding: 7px 9px; border-radius: 6px; background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569; font-family: monospace; font-size: 0.85rem; }

/* Multi-split live log viewer */
.split-overlay { position: fixed; inset: 0; z-index: 1500; background: #0b1120; display: flex; flex-direction: column; overflow: hidden; }
.split-bar { flex: 0 0 auto; display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; background: #1e293b; border-bottom: 1px solid #334155; position: sticky; top: 0; z-index: 10; }
.split-title { font-weight: 700; color: #f8fafc; font-size: 0.95rem; }
.split-grid { flex: 1 1 auto; display: grid; gap: 8px; padding: 8px; overflow: hidden; }
.split-pane { display: flex; flex-direction: column; border: 1px solid #334155; border-radius: 8px; overflow: hidden; background: #0f172a; min-height: 0; min-width: 0; }
.split-pane-head { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: #1e293b; border-bottom: 1px solid #334155; color: #e2e8f0; font-size: 0.8rem; font-weight: 600; }
.split-pane-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.split-dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 6px #22c55e; flex: 0 0 auto; }
.split-pane-count { margin-left: auto; flex: 0 0 auto; background: rgba(56,189,248,0.18); color: #7dd3fc; border-radius: 999px; padding: 1px 8px; font-size: 0.7rem; font-weight: 700; display: none; }
.split-pane-body { position: relative; flex: 1 1 auto; margin: 0; padding: 8px 10px; overflow: auto; font-family: monospace; font-size: 0.72rem; line-height: 1.35; color: #cbd5e1; white-space: pre-wrap; word-break: break-word; }

/* Split log search box + highlight */
.split-search-wrap { display: flex; align-items: center; gap: 6px; margin-left: auto; margin-right: 12px; }
.split-search { width: 260px; max-width: 40vw; padding: 6px 10px; border-radius: 6px; background: rgba(15,23,42,0.85); color: #f8fafc; border: 1px solid #475569; font-size: 0.82rem; }
.split-case { padding: 5px 9px; border-radius: 6px; background: rgba(15,23,42,0.85); color: #94a3b8; border: 1px solid #475569; cursor: pointer; font-size: 0.75rem; font-weight: 700; line-height: 1; }
.split-case.active { color: #0b1120; background: #7dd3fc; border-color: #7dd3fc; }
.split-match-count { color: #94a3b8; font-size: 0.78rem; min-width: 72px; white-space: nowrap; }
mark.log-hit { background: #fde047; color: #0b1120; border-radius: 2px; padding: 0 1px; }

/* ===================== Responsive layout (narrow windows) ===================== */

/* The header controls bar and its action row wrap instead of overflowing the
   header (which used to push them over the title). */
.header-controls-panel { flex-wrap: wrap; min-width: 0; }
.controls-actions { flex-wrap: wrap; min-width: 0; }

/* Console header: its three groups (title, metrics chips, Lines + save/copy/
   clear buttons) wrap onto extra rows when the column is narrow — previously
   the row was clipped and the copy button became unreachable. The row itself
   is inline-styled in index.html, which sets no flex-wrap, so it lands here.
   Matched by class rather than :first-child: layout edit mode inserts a drag
   handle ahead of it, which would otherwise steal the rule. */
.console-card > .console-head-row { flex-wrap: wrap; row-gap: 8px; }
#console-metrics-panel { flex-wrap: wrap; row-gap: 6px; min-width: 0; }

/* ===================== Dashboard templates ===================== */

/* Header chip showing the active template (app-templates.js keeps it in sync). */
.tpl-chip { background: rgba(125,211,252,0.14); color: #7dd3fc; border-color: rgba(125,211,252,0.35); }
.tpl-chip:hover { background: rgba(125,211,252,0.24); }
body.theme-light .tpl-chip { color: #0369a1; background: rgba(2,132,199,0.10); border-color: rgba(2,132,199,0.30); }

/* Template picker (Settings -> Templates). The modal is always dark. */
.tpl-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; margin-bottom: 14px; }
.tpl-card {
    border: 1px solid #334155; border-radius: 8px; padding: 10px 12px; cursor: pointer;
    background: rgba(15,23,42,0.5); transition: border-color 0.15s, background 0.15s;
}
.tpl-card:hover { border-color: #475569; background: rgba(15,23,42,0.8); }
.tpl-card.tpl-active { border-color: #38bdf8; background: rgba(56,189,248,0.10); }
/* Wraps, because a card can carry two badges at once.
   =================================================

   The cards are a minmax(230px, 1fr) grid, and a template that is both active
   and edited puts icon + name + "active" + "edited" on one row. Without
   wrapping that row is wider than the track, and since nothing here clips, the
   last badge was drawn outside the card's own border.

   Wrapping rather than truncating, and in this order deliberately: the badges
   move to a second line first, so the name -- the one thing on the card that
   identifies it -- keeps the full width before anything is taken from it. The
   ellipsis below is the second line of defence, for a template someone has
   renamed to something long enough that it does not fit even alone. */
.tpl-head { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; color: #f8fafc; font-size: 0.9rem; }
/* min-width:0 is what makes the ellipsis reachable at all: a flex item defaults
   to min-width:auto, which refuses to shrink below its text and is what pushed
   the badge out of the card in the first place. */
.tpl-head b { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-icon { font-size: 1rem; }
.tpl-badge {
    margin-left: auto; font-size: 0.6rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.05em; color: #38bdf8; border: 1px solid rgba(56,189,248,0.35);
    background: rgba(56,189,248,0.12); border-radius: 10px; padding: 1px 7px;
}
.tpl-desc { font-size: 0.76rem; color: #94a3b8; margin: 6px 0 0; line-height: 1.45; }
.tpl-panels { font-size: 0.7rem; color: #64748b; margin: 6px 0 0; line-height: 1.4; }

/* "edited" sits next to "active", so it cannot also claim margin-left:auto —
   only the first badge pushes to the right edge. */
.tpl-badge + .tpl-badge { margin-left: 4px; }
.tpl-badge-edited { color: #fbbf24; border-color: rgba(251,191,36,0.35); background: rgba(251,191,36,0.12); }

/* Per-card actions. Kept quiet until the card is hovered or active: every card
   carries them, and four sets of loud buttons would drown out the descriptions
   that make the picker readable. */
.tpl-actions { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; opacity: 0.45; transition: opacity 0.15s; }
.tpl-card:hover .tpl-actions, .tpl-card.tpl-active .tpl-actions { opacity: 1; }
.tpl-act {
    font-family: inherit; font-size: 0.68rem; font-weight: 600; cursor: pointer;
    padding: 3px 8px; border-radius: 5px; color: #cbd5e1;
    background: rgba(148,163,184,0.12); border: 1px solid rgba(148,163,184,0.28);
}
.tpl-act:hover { background: rgba(148,163,184,0.22); color: #f8fafc; }
.tpl-act-danger { color: #fca5a5; border-color: rgba(248,113,113,0.35); background: rgba(248,113,113,0.10); }
.tpl-act-danger:hover { background: rgba(248,113,113,0.20); color: #fecaca; }

/* ===================== API Client panel ===================== */
.api-line { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; }
.api-method {
    flex: 0 0 auto; padding: 7px 8px; border-radius: 6px; font-weight: 700; font-size: 0.78rem;
    background: rgba(15,23,42,0.6); color: #7dd3fc; border: 1px solid #475569; font-family: 'Inter', sans-serif;
}
.api-url {
    flex: 1; min-width: 0; padding: 7px 10px; border-radius: 6px;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Consolas', monospace; font-size: 0.82rem;
}
/* The primary action of the panel, so it carries the accent rather than the
   default .act grey the secondary buttons use. */
.api-send {
    flex: 0 0 auto; width: auto; margin: 0; padding: 7px 16px;
    background: #2563eb; color: #ffffff; font-weight: 600;
}
.api-send:hover { background: #1d4ed8; }
.api-send[disabled] { opacity: 0.55; cursor: default; background: #2563eb; }

.api-opts { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: center; margin-bottom: 6px; }
.api-opt { display: flex; align-items: center; gap: 6px; font-size: 0.75rem; color: #94a3b8; white-space: nowrap; }
.api-opt select, .api-num {
    padding: 4px 7px; border-radius: 5px; background: rgba(15,23,42,0.6);
    color: #f8fafc; border: 1px solid #475569; font-size: 0.75rem; font-family: 'Inter', sans-serif;
}
.api-num { width: 52px; text-align: right; font-family: 'Consolas', monospace; }
.api-note { font-size: 0.72rem; color: #64748b; margin: 4px 0 10px; line-height: 1.5; }
/* A note about something that will not work as set — an empty credential, say.
   Same note, read as a warning rather than as background. */
.api-note.api-warn { color: #fbbf24; }

.api-tabs { display: flex; align-items: center; gap: 4px; border-bottom: 1px solid #334155; margin-bottom: 10px; }
.api-tab {
    background: transparent; border: none; border-bottom: 2px solid transparent; cursor: pointer;
    color: #94a3b8; font-family: 'Inter', sans-serif; font-size: 0.78rem; font-weight: 600; padding: 6px 10px;
}
.api-tab:hover { color: #e2e8f0; }
.api-tab.api-tab-on { color: #7dd3fc; border-bottom-color: #7dd3fc; }
.api-tabs-spacer { flex: 1; }
.api-mini { width: auto; margin: 0; padding: 3px 9px; font-size: 0.72rem; }
.api-pane { margin-bottom: 12px; }

.api-hdrs { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.api-hdr-row { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
.api-hdr-row input {
    flex: 1; min-width: 0; padding: 5px 8px; border-radius: 5px;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Consolas', monospace; font-size: 0.78rem;
}
.api-hdr-name { flex: 0 0 34% !important; }
.api-authrow { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.api-body {
    width: 100%; box-sizing: border-box; min-height: 120px; resize: vertical; padding: 8px 10px;
    border-radius: 6px; background: rgba(2,6,23,0.6); color: #e2e8f0; border: 1px solid #475569;
    font-family: 'Consolas', monospace; font-size: 0.78rem; line-height: 1.5;
}

.api-saved { border-top: 1px solid #334155; padding-top: 8px; margin-bottom: 12px; }
.api-saved-head {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.06em; color: #64748b; margin-bottom: 6px;
}
.api-saved-row { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.api-saved-btn {
    flex: 1; text-align: left; cursor: pointer; padding: 5px 8px; border-radius: 5px;
    background: rgba(15,23,42,0.5); border: 1px solid #334155; color: #cbd5e1;
    font-family: 'Inter', sans-serif; font-size: 0.76rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.api-saved-btn:hover { border-color: #475569; background: rgba(15,23,42,0.9); }
.api-meth-tag {
    display: inline-block; min-width: 42px; margin-right: 8px; text-align: center;
    font-family: 'Consolas', monospace; font-size: 0.66rem; font-weight: 700; color: #7dd3fc;
    background: rgba(56,189,248,0.12); border: 1px solid rgba(56,189,248,0.3); border-radius: 4px; padding: 0 5px;
}

.api-resp { border: 1px solid #334155; border-radius: 8px; overflow: hidden; }
.api-resp-head {
    display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
    padding: 7px 11px; background: rgba(30,41,59,0.6); border-bottom: 1px solid #334155;
}
.api-status { font-family: 'Consolas', monospace; font-size: 0.78rem; font-weight: 700; padding: 2px 8px; border-radius: 10px; }
.api-status-ok   { color: #34d399; background: rgba(16,185,129,0.14); border: 1px solid rgba(16,185,129,0.35); }
.api-status-info { color: #7dd3fc; background: rgba(56,189,248,0.14); border: 1px solid rgba(56,189,248,0.35); }
.api-status-warn { color: #fbbf24; background: rgba(249,115,22,0.14); border: 1px solid rgba(249,115,22,0.35); }
.api-status-err  { color: #f87171; background: rgba(239,68,68,0.14); border: 1px solid rgba(239,68,68,0.35); }
.api-resp-meta { font-size: 0.72rem; color: #94a3b8; }
.api-resp-body {
    margin: 0; padding: 11px; max-height: 340px; overflow: auto; background: rgba(2,6,23,0.6);
    font-family: 'Consolas', monospace; font-size: 0.76rem; line-height: 1.55;
    color: #cbd5e1; white-space: pre-wrap; word-break: break-word;
}
.api-resp-hdrs { width: 100%; border-collapse: collapse; font-family: 'Consolas', monospace; font-size: 0.74rem; }
.api-resp-hdrs td { padding: 3px 8px 3px 0; vertical-align: top; word-break: break-all; }
.api-resp-hdrs td:first-child { color: #7dd3fc; white-space: nowrap; width: 1%; }
/* Sidebar tree rows: the ⋯ that opens a row's menu stays out of the way until
   the row is under the pointer, so the tree reads as a tree. It is kept visible
   on touch, where there is no hover and no right-click to fall back on. */
.api-tree-menu { cursor: pointer; color: #64748b; font-size: 14px; line-height: 1; padding: 0 2px;
                 visibility: hidden; }
.api-tree-row:hover .api-tree-menu { visibility: visible; }
.api-tree-menu:hover { color: #e2e8f0; }
@media (hover: none) { .api-tree-menu { visibility: visible; } }

.api-ctxmenu { position: fixed; z-index: 10001; min-width: 190px; padding: 4px;
               background: #1e293b; border: 1px solid #334155; border-radius: 8px;
               box-shadow: 0 10px 28px rgba(0,0,0,.45); }
.api-ctxmenu hr { border: 0; border-top: 1px solid #334155; margin: 4px 0; }
.api-ctxmenu-item { display: block; width: 100%; text-align: left; border: 0; background: none;
                    color: #e2e8f0; font: inherit; font-size: 0.78rem; padding: 6px 10px;
                    border-radius: 5px; cursor: pointer; white-space: nowrap; }
.api-ctxmenu-item:hover { background: rgba(56,189,248,0.14); }
.api-ctxmenu-danger { color: #f87171; }
.api-ctxmenu-danger:hover { background: rgba(248,113,113,0.14); }

/* The request line above the headers on the Sent tab. */
.api-sent-line { font-family: 'Consolas', monospace; font-size: 0.74rem; color: #e2e8f0; margin: 0 0 8px;
                 white-space: pre-wrap; word-break: break-all; }

/* The generic key/value table: query params, headers, form fields and
   environment variables all render through renderApiKvTable, so they share one
   grid rather than each laying itself out. */
.api-kv { display: flex; flex-direction: column; gap: 4px; margin-bottom: 8px; }
.api-kv-row { display: flex; align-items: center; gap: 6px; }
.api-kv-head {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.06em; color: #64748b;
}
.api-kv-on { flex: 0 0 20px; text-align: center; }
.api-kv-x  { flex: 0 0 34px; text-align: right; }
.api-kv-row .api-kv-k { flex: 0 0 26%; }
.api-kv-row .api-kv-v { flex: 1 1 auto; min-width: 0; }
.api-kv-row .api-kv-d { flex: 0 0 22%; }
.api-kv-row .api-kv-type { flex: 0 0 74px; }
.api-kv input[type=text], .api-kv input[type=password], .api-kv select {
    padding: 5px 8px; border-radius: 5px;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Consolas', monospace; font-size: 0.78rem; min-width: 0;
}
.api-kv input[readonly] { color: #94a3b8; background: rgba(15,23,42,0.35); }
.api-kv-file { display: flex; align-items: center; gap: 6px; }
.api-kv-file input[type=file] { max-width: 190px; font-size: 0.7rem; color: #94a3b8; }
.api-kv-filename { font-size: 0.72rem; color: #94a3b8; word-break: break-all; }

.api-subhead {
    font-size: 0.66rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.06em; color: #64748b; margin: 12px 0 6px;
}
.api-bodymodes { display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center; margin-bottom: 10px; }
.api-radio { display: flex; align-items: center; gap: 5px; font-size: 0.75rem; color: #94a3b8; white-space: nowrap; }
.api-script { font-size: 0.76rem; }
.api-var-summary { font-size: 0.68rem; color: #64748b; margin-top: 5px; }
.api-secretrow { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; font-size: 0.7rem; color: #64748b; margin: 6px 0; }
.api-authfield { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.api-authfield label { flex: 0 0 130px; font-size: 0.75rem; color: #94a3b8; }
.api-authfield input, .api-authfield select {
    flex: 1; min-width: 0; padding: 5px 8px; border-radius: 5px;
    background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
    font-family: 'Consolas', monospace; font-size: 0.78rem;
}
/* The resolved address under the URL bar. Monospace and dim: it is what the
   request line means, not a second thing to read. */
.api-url-preview {
    font-family: 'Consolas', monospace; font-size: 0.72rem; color: #64748b;
    margin: -4px 0 8px; word-break: break-all; line-height: 1.5;
}
.api-var-missing { color: #fbbf24; }
body.theme-light .api-url-preview { color: #64748b; }

/* JSON syntax colours for the response body. Property names apart from string
   values is the distinction that matters — finding a field in a nested object
   is most of what reading a response involves. */
.api-j-key  { color: #7dd3fc; }
.api-j-str  { color: #86efac; }
.api-j-num  { color: #fbbf24; }
.api-j-bool { color: #c084fc; }
.api-j-null { color: #94a3b8; font-style: italic; }

/* The light theme takes the darker, higher-contrast set that reads on white —
   the same convention every JSON viewer uses there. */
body.theme-light .api-j-key  { color: #a31515; }
body.theme-light .api-j-str  { color: #0451a5; }
body.theme-light .api-j-num  { color: #098658; }
body.theme-light .api-j-bool { color: #0000c0; }
body.theme-light .api-j-null { color: #6b7280; font-style: italic; }

/* The transient "Saved …" line beside the Save buttons. It reserves no space
   when empty, so nothing shifts when it appears and goes. */
.api-flash { font-size: 0.72rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 44%; }
.api-flash-ok   { color: #34d399; }
.api-flash-warn { color: #fbbf24; }
.api-flash-err  { color: #f87171; }

.api-resp-badge {
    font-size: 0.62rem; padding: 0 5px; border-radius: 8px; color: #a3e635;
    background: rgba(163,230,53,0.12); border: 1px solid rgba(163,230,53,0.3);
}
.api-bodylib { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.api-bodylib-row {
    display: flex; align-items: center; gap: 6px; padding: 4px 6px; border-radius: 5px;
    background: rgba(15,23,42,0.5); border: 1px solid #334155; font-size: 0.76rem; color: #cbd5e1;
}

/* The environment editor takes the whole panel while it is open. */
.api-envedit { display: flex; flex-direction: column; width: 100%; }
.api-envlist { flex: 0 0 210px; display: flex; flex-direction: column; gap: 3px; }
.api-envvars { flex: 1; min-width: 0; }
.api-envrow {
    display: flex; justify-content: space-between; align-items: center; gap: 8px;
    padding: 6px 8px; border-radius: 5px; cursor: pointer; font-size: 0.78rem; color: #cbd5e1;
    background: rgba(15,23,42,0.5); border: 1px solid #334155;
}
.api-envrow:hover { border-color: #475569; }
.api-envrow-on { background: rgba(56,189,248,0.1); border-color: #38bdf8; color: #e0f2fe; }
.api-envcount { font-size: 0.68rem; color: #64748b; }

body.theme-light .api-url, body.theme-light .api-method, body.theme-light .api-hdr-row input,
body.theme-light .api-body, body.theme-light .api-opt select, body.theme-light .api-num,
body.theme-light .api-kv input, body.theme-light .api-kv select,
body.theme-light .api-authfield input, body.theme-light .api-authfield select {
    background: #ffffff; color: #0f172a; border: 1px solid #cbd5e1;
}
body.theme-light .api-resp-body { background: #f8fafc; color: #1e293b; }
body.theme-light .api-envrow, body.theme-light .api-bodylib-row { background: #f8fafc; color: #1e293b; border-color: #cbd5e1; }

/* Email alert templates (Settings -> Alerts). Same card idiom as the dashboard
   template picker, laid out tighter because the descriptions are shorter. */
.mailtpl-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 8px; margin-bottom: 12px; }
.mailtpl-card {
    border: 1px solid #334155; border-radius: 8px; padding: 9px 11px; cursor: pointer;
    background: rgba(15,23,42,0.5); transition: border-color 0.15s, background 0.15s;
}
.mailtpl-card:hover { border-color: #475569; background: rgba(15,23,42,0.8); }
.mailtpl-card.mailtpl-active { border-color: #38bdf8; background: rgba(56,189,248,0.10); }
.mailtpl-head { display: flex; align-items: center; gap: 7px; color: #f8fafc; font-size: 0.85rem; }
.mailtpl-tag {
    font-size: 0.58rem; font-weight: 700; letter-spacing: 0.05em; color: #c4b5fd;
    border: 1px solid rgba(167,139,250,0.4); background: rgba(167,139,250,0.14);
    border-radius: 9px; padding: 1px 6px;
}
.mailtpl-preview { border: 1px solid #334155; border-radius: 8px; background: rgba(2,6,23,0.6); margin-bottom: 12px; overflow: hidden; }
.mailtpl-previewhead {
    padding: 6px 11px; font-size: 0.7rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.05em; color: #94a3b8; border-bottom: 1px solid #334155; background: rgba(30,41,59,0.6);
}
.mailtpl-preview pre {
    margin: 0; padding: 11px; max-height: 220px; overflow: auto;
    font-family: 'Consolas', monospace; font-size: 0.72rem; line-height: 1.5;
    color: #cbd5e1; white-space: pre-wrap; word-break: break-word;
}
.mailtpl-customhead { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
.mailtpl-vars { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 4px 14px; }
.mailtpl-var { display: flex; align-items: baseline; gap: 8px; font-size: 0.72rem; color: #94a3b8; }
.mailtpl-var code { color: #7dd3fc; font-family: 'Consolas', monospace; white-space: nowrap; }

/* Layout edit mode: a drag handle on each panel plus a bar pinned to the
   bottom of the window. Handles exist only while editing — a permanent one on
   every card header would compete with the collapse-on-click headers. */
body.layout-editing .card { outline: 1px dashed rgba(56,189,248,0.45); outline-offset: 3px; }
.layout-grip {
    display: flex; align-items: center; gap: 8px; cursor: move; user-select: none;
    margin: -8px -10px 10px -10px; padding: 5px 10px; border-radius: 6px;
    background: rgba(56,189,248,0.12); border: 1px solid rgba(56,189,248,0.3);
    color: #7dd3fc; font-size: 0.72rem; font-weight: 700; font-family: 'Inter', sans-serif;
}
.layout-grip-dots { font-size: 0.95rem; line-height: 1; color: #38bdf8; }
.layout-grip-off { margin-left: auto; color: #64748b; font-weight: 500; }
.card.layout-drop-target { outline: 2px solid #38bdf8; outline-offset: 3px; }
.layout-edit-bar {
    position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 9000;
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap; max-width: 92vw;
    background: #0f172a; border: 1px solid #38bdf8; border-radius: 10px;
    padding: 10px 16px; box-shadow: 0 10px 30px rgba(0,0,0,0.5);
    font-family: 'Inter', sans-serif; font-size: 0.8rem; color: #e2e8f0;
}
.layout-edit-title { font-weight: 700; color: #7dd3fc; white-space: nowrap; }
.layout-edit-hint { color: #94a3b8; font-size: 0.75rem; }
.layout-edit-off { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; color: #94a3b8; font-size: 0.75rem; }
.layout-edit-bar .env-btn { margin: 0; }

/* Below the two-column width (760px left column + a usable console), stack
   everything: full-width cards, header controls on their own centered row,
   and the console loses its sticky pin (it would cover the stacked cards). */
@media (max-width: 1200px) {
    .main-layout { flex-direction: column; }
    .left-col { width: 100%; flex-shrink: 1; }
    .right-col { position: static; width: 100%; }
    /* Stacked layout: the column is already full width, so there is nothing
       to drag. applyLeftColumnWidth also clears its inline width here, which
       would otherwise beat this rule. */
    .panel-resize-grip { display: none; }
    .console-card { height: 60vh; min-height: 320px; }

    .header-container { flex-wrap: wrap; justify-content: center; }
    .header-controls-panel { flex-basis: 100%; justify-content: center; }
}

/* Small screens: shrink the title so "Control Panel" doesn't wrap mid-word. */
@media (max-width: 600px) {
    body { padding: 12px; }
    h1 { font-size: 1.5rem; }
    p.subtitle { font-size: 0.85rem; }
    .card { padding: 14px 16px; }
    /* Match the negative margin to body's smaller padding at this width, or
       the sticky header would overshoot the viewport edge by 8px. */
    .header-container { margin: 0 -12px 16px -12px; padding: 14px 12px; }
}

.api-client-maximized, .panel-maximized { position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; z-index: 9999 !important; background: #0f172a !important; margin: 0 !important; border-radius: 0 !important; border: none !important; height: 100% !important; max-height: none !important; width: 100% !important; max-width: none !important; padding: 20px !important; box-sizing: border-box !important; overflow: hidden !important; display: flex !important; flex-direction: column !important; }
body.theme-light .api-client-maximized, body.theme-light .panel-maximized { background: #ffffff !important; }
/* position:sticky makes the right column a stacking context, which traps the
   Console card's z-index inside it — maximized, it painted *under* the sticky
   page header, hiding its own Restore button. Dropping the stickiness while a
   panel is maximized lets the fixed card reach the top of the page's stack.
   The column is scrolled off-screen by the full-window panel anyway. */
body.panel-is-maximized .right-col { position: static !important; }
.api-client-maximized #api-client-content { flex: 1; min-height: 0; height: 100%; display: flex; flex-direction: column; }
.api-client-maximized > h2 { display: none; }
.api-client-maximized .panel-resize-grip, .api-client-maximized .panel-resize-grip-v, .panel-maximized .panel-resize-grip, .panel-maximized .panel-resize-grip-v { display: none; }

/* One child of a maximized card grows to fill the window; everything else
   (headers, toolbars, the console input row) keeps its natural height. The
   growing child is tagged .panel-max-body by togglePanelMaximized rather than
   being matched structurally — cards carry different numbers of header and
   footer rows, and the structural version stretched whichever ones it failed
   to recognise. */
.panel-maximized > * {
    flex: 0 0 auto !important;
    height: auto !important;
    min-height: 0 !important;
    overflow-y: visible !important;
}
.panel-maximized > .panel-max-body {
    flex: 1 1 auto !important;
    height: auto !important;
    min-height: 0 !important;
    max-height: none !important;
    overflow-y: auto !important;
}
/* Inner scroll boxes are capped in px for the normal layout (the file list at
   250px, the JMeter run history at 200px). Those caps would leave most of a
   maximized panel empty, so they are lifted for as long as it is maximized. */
.panel-maximized .panel-max-body #browser-list-container,
.panel-maximized .panel-max-body #jmeter-reports-list,
.panel-maximized .panel-max-body #jmeter-live-runs,
.panel-maximized .panel-max-body .grid,
.panel-maximized .panel-max-body #metrics-dash-cards {
    max-height: none !important;
}
/* The console body is a fixed viewport-height box in the normal layout. */
.panel-maximized > #console-output.panel-max-body {
    height: auto !important;
    max-height: none !important;
}


/* ---------- collection runner ---------------------------------------------
   The report is a table people read top-to-bottom looking for the failures, so
   the rows stay quiet and only the FAIL badges and the failed-assertion lines
   carry colour. */
.api-runner { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.api-runner-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.api-runner-head h3 { margin: 0; font-size: 0.95rem; color: #e2e8f0; }
.api-run-summary { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.api-run-table { width: 100%; border-collapse: collapse; font-size: 0.8rem; }
.api-run-table th {
    text-align: left; font-weight: 600; color: #94a3b8; font-size: 0.72rem;
    text-transform: uppercase; letter-spacing: 0.04em;
    padding: 6px 8px; border-bottom: 1px solid rgba(148,163,184,0.25);
}
.api-run-table td { padding: 6px 8px; border-bottom: 1px solid rgba(148,163,184,0.12); vertical-align: top; }
/* The URL under each name can be long; let it wrap rather than stretch the
   table past the pane. */
.api-run-table td .api-note { word-break: break-all; }

/* ---------------- Settings -> Team ----------------
   The workspace's people. Built on the same .env-row list the Environments tab
   uses, so a member row and an environment row read as the same kind of thing;
   what is added here is the badge strip that carries a member's three axes
   (role, persona, environment scope) at a glance, and the counts above it. */
/* .sfield sizes input[type=text] and select but not the typed inputs added
   since; an email field left at the browser default renders half the width of
   the selects beside it. */
.sfield input[type=email] {
  width: 100%; box-sizing: border-box; padding: 8px 10px; border-radius: 6px;
  background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
  font-family: 'Inter', sans-serif; font-size: 0.85rem;
}

.team-counts { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.team-count {
  font-size: 0.75rem; color: #cbd5e1; background: rgba(15,23,42,0.55);
  border: 1px solid #334155; border-radius: 999px; padding: 3px 11px;
}
.team-count b { color: #f8fafc; }
/* The seat chip is the one count that can become a problem, so it is the one
   that changes colour when it does. */
.team-count-seats.is-full {
  background: rgba(248,113,113,0.13); border-color: rgba(248,113,113,0.4); color: #fca5a5;
}
.team-count-seats.is-full b { color: #fca5a5; }

.team-status { font-size: 0.8rem; margin: 0 0 12px; min-height: 1em; }
.team-status.is-ok { color: #4ade80; }
.team-status.is-err { color: #f87171; }
.team-status:empty { margin: 0; min-height: 0; }

.team-badges { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; margin-left: auto; }
.team-badge {
  font-size: 0.66rem; font-weight: 600; letter-spacing: .01em;
  padding: 2px 8px; border-radius: 999px; white-space: nowrap;
  background: rgba(148,163,184,0.13); border: 1px solid #475569; color: #cbd5e1;
}
.team-badge-role { background: rgba(56,189,248,0.15); border-color: rgba(56,189,248,0.4); color: #7dd3fc; }
.team-badge-warn { background: rgba(248,113,113,0.13); border-color: rgba(248,113,113,0.4); color: #fca5a5; }
/* Absence of a second factor is worth showing but is not an error: it is the
   row an access review asks about, not a fault to shout at. */
.team-badge-dim { background: transparent; border-color: #334155; color: #94a3b8; }

.team-you {
  font-size: 0.62rem; font-weight: 600; color: #94a3b8;
  border: 1px solid #475569; border-radius: 999px; padding: 1px 7px;
  vertical-align: 1px; margin-left: 4px;
}
.team-joined { font-size: 0.72rem; color: #94a3b8; margin-left: auto; }

/* One row per environment: its name, then the role this member holds there.
   A grid rather than a flex wrap so the selects line up under each other -- a
   column of ragged controls is unreadable once there are more than three. */
.team-envs { display: grid; grid-template-columns: minmax(90px, max-content) 1fr; gap: 6px 12px; align-items: center; }
.team-env { display: contents; }
.team-env-name { font-size: 0.8rem; color: #cbd5e1; }
.team-envs select {
  width: 100%; box-sizing: border-box; padding: 6px 9px; border-radius: 6px;
  background: rgba(15,23,42,0.6); color: #f8fafc; border: 1px solid #475569;
  font-family: 'Inter', sans-serif; font-size: 0.8rem;
}

/* The member list stacks the badges under the name when the modal is narrow,
   rather than squeezing the email into nothing beside them. */
@media (max-width: 720px) {
  .env-row-head:has(.team-badges) { flex-wrap: wrap; }
  .team-badges { margin-left: 22px; width: 100%; }
}
