/* admin.css */
*{margin:0;padding:0;box-sizing:border-box}

/* 1. Theme tokens */
/* Source of truth: style.css:2 */
:root{--bg:#111;--bg2:#1c1c1c;--bg3:#252525;--border:#2e2e2e;--text:#eee;--muted:#808080;--orange:#e05a22;--green:#5c9e3a;--red:#c0392b}

/* Source of truth: style.css:5-9 */
[data-theme="minimal"]{
  --bg:#f0e8dc;--bg2:#fdf7ef;--bg3:#e8ddd0;
  --border:#c8b89a;--text:#2a1a0a;--muted:#8a7060;
  --orange:#c05818;--green:#3a7a20;--red:#b02020;
}

/* Source of truth: style.css:10 (font-family) */
body{background:var(--bg);color:var(--text);font-family:'Outfit',-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;min-height:100vh;transition:background .35s,color .35s}

/* 2. Focus ring everywhere */
*:focus-visible{outline:2px solid var(--orange);outline-offset:2px}

/* 3. Shell */
.admin-shell{display:grid;grid-template-columns:240px 1fr;min-height:100vh}
.admin-sidebar{position:sticky;top:0;height:100vh;background:var(--bg2);border-right:.5px solid var(--border);padding:calc(20px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) calc(20px + env(safe-area-inset-bottom)) calc(12px + env(safe-area-inset-left));display:flex;flex-direction:column;overflow-y:auto}
.admin-content{padding:calc(24px + env(safe-area-inset-top)) calc(32px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(32px + env(safe-area-inset-left));max-width:1400px;min-width:0}
/* min-width:0 is load-bearing, not tidiness. A grid item's default
   min-width is auto, so the 1fr column above grows to fit its widest
   child -- an 11-column table -- and the PAGE scrolls sideways instead of
   the table scrolling inside .admin-table-wrap. Without this the wrap has
   nothing to scroll against: measured 1146px of content in a 749px
   viewport with the wrap reporting no overflow of its own. */

/* 4. Sidebar group labels */
.sidebar-group-title{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:8px;font-weight:500;margin-top:16px}
.sidebar-group-title:first-child{margin-top:0}

/* 5. Nav items */
.admin-nav-item{display:flex;align-items:center;gap:8px;height:44px;padding:0 12px;border-radius:10px;font-size:13px;color:var(--muted);cursor:pointer;text-decoration:none;border:none;background:none;width:100%;transition:background .15s,color .15s}
.admin-nav-item:hover{background:var(--bg3);color:var(--text)}
.admin-nav-item.active{background:var(--bg3);color:var(--text);box-shadow:inset 2px 0 0 var(--orange)}

/* 6. Cards */
.admin-card{background:var(--bg2);border:.5px solid var(--border);border-radius:16px;padding:20px 24px;min-width:0;box-shadow:0 8px 32px rgba(0,0,0,0.1)}

/* 7. Tables */
.admin-table{width:100%;border-collapse:collapse;font-size:12px}
.admin-table thead th{position:sticky;top:0;background:var(--bg2);z-index:1;text-align:left;font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted);font-weight:500;padding:8px 8px;border-bottom:.5px solid var(--border);white-space:nowrap}
.admin-table tbody td{padding:8px 8px;border-top:.5px solid var(--border);vertical-align:middle}
.admin-table tbody tr:hover{background:var(--bg3)}
.admin-table tbody tr:focus-visible{outline:2px solid var(--orange);outline-offset:-2px}
.admin-table .controls-bar{display:flex;justify-content:flex-start;gap:4px;margin-bottom:0}
.admin-table .admin-btn{height:28px;padding:0 8px;font-size:11px;border-radius:6px;gap:4px}
.admin-table .pill{height:20px;padding:0 7px;font-size:10px;border-radius:10px}

/* 8. Status pills */
.pill{display:inline-flex;align-items:center;gap:6px;height:22px;padding:0 9px;border-radius:11px;font-size:11px;font-weight:500;border:.5px solid transparent;white-space:nowrap}
.pill::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor}
.pill-ok{color:var(--green);background:color-mix(in srgb,var(--green) 14%,transparent);border-color:color-mix(in srgb,var(--green) 34%,transparent)}
.pill-warn{color:var(--orange);background:color-mix(in srgb,var(--orange) 14%,transparent);border-color:color-mix(in srgb,var(--orange) 34%,transparent)}
.pill-bad{color:var(--red);background:color-mix(in srgb,var(--red) 14%,transparent);border-color:color-mix(in srgb,var(--red) 34%,transparent)}
.pill-idle{color:var(--muted);background:color-mix(in srgb,var(--muted) 12%,transparent);border-color:var(--border)}
.pill-pro{color:#f59e0b;background:color-mix(in srgb,#f59e0b 14%,transparent);border-color:color-mix(in srgb,#f59e0b 34%,transparent)}
.pill-vip{color:#ec4899;background:color-mix(in srgb,#ec4899 14%,transparent);border-color:color-mix(in srgb,#ec4899 34%,transparent)}
.pill-admin{color:#8b5cf6;background:color-mix(in srgb,#8b5cf6 14%,transparent);border-color:color-mix(in srgb,#8b5cf6 34%,transparent)}
/* Channel badges — dev/beta only; stable devices show nothing */
.pill-ch-dev{color:var(--orange);background:color-mix(in srgb,var(--orange) 14%,transparent);border-color:color-mix(in srgb,var(--orange) 34%,transparent)}
.pill-ch-beta{color:#4a90d9;background:color-mix(in srgb,#4a90d9 14%,transparent);border-color:color-mix(in srgb,#4a90d9 34%,transparent)}
.pill-ch-stable{color:var(--green,#22c55e);background:color-mix(in srgb,var(--green,#22c55e) 14%,transparent);border-color:color-mix(in srgb,var(--green,#22c55e) 34%,transparent)}

/* 9. JSON viewer */
.jsonv{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.55;background:var(--bg3);border:.5px solid var(--border);border-radius:12px;padding:14px 16px;max-height:60vh;overflow:auto;white-space:pre}
.jsonv .k{color:var(--text);opacity:.75}
.jsonv .s{color:var(--green)}
.jsonv .n{color:var(--orange)}
.jsonv .b,.jsonv .z{color:var(--muted);font-style:italic}

/* 10. Per-theme overrides */
[data-theme="minimal"] .admin-card{box-shadow:0 4px 16px rgba(0,0,0,.06)}
[data-theme="minimal"] .jsonv{background:#fffaf3}

/* 11. Topbar */
.admin-topbar{height:56px;display:flex;align-items:center;gap:16px;border-bottom:.5px solid var(--border);margin-bottom:24px}

/* 12. Buttons */
.admin-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:36px;padding:0 16px;border-radius:10px;font-size:13px;font-weight:600;border:1px solid var(--border);background:color-mix(in srgb, var(--bg3) 85%, #fff 15%);color:var(--text);cursor:pointer;transition:background .15s, border-color .15s, transform .1s, box-shadow .15s;box-shadow:0 1px 3px rgba(0,0,0,0.2);user-select:none}
.admin-btn:hover:not(:disabled){background:color-mix(in srgb, var(--bg3) 70%, #fff 30%);border-color:color-mix(in srgb, var(--border) 50%, #fff 50%);box-shadow:0 2px 6px rgba(0,0,0,0.3)}
.admin-btn:active:not(:disabled){transform:scale(0.97);box-shadow:0 1px 2px rgba(0,0,0,0.2)}
.admin-btn:disabled{opacity:.35;cursor:not-allowed;box-shadow:none;pointer-events:none}
.admin-btn-primary{background:var(--orange);color:#fff;border-color:var(--orange);font-weight:600;box-shadow:0 1px 4px rgba(224,90,43,0.35)}
.admin-btn-primary:hover:not(:disabled){background:color-mix(in srgb,var(--orange) 88%,#fff);border-color:color-mix(in srgb,var(--orange) 88%,#fff);box-shadow:0 2px 8px rgba(224,90,43,0.5)}
.admin-btn-primary:active:not(:disabled){background:color-mix(in srgb,var(--orange) 80%,#000);transform:scale(0.97)}
.admin-btn-danger{color:var(--red);border-color:color-mix(in srgb,var(--red) 50%,transparent);background:color-mix(in srgb,var(--red) 10%,var(--bg3))}
.admin-btn-danger:hover:not(:disabled){background:color-mix(in srgb,var(--red) 20%,transparent);border-color:var(--red);color:#ff6b6b;box-shadow:0 2px 6px rgba(239,68,68,0.3)}
.admin-btn-danger:active:not(:disabled){transform:scale(0.97)}

/* 13. btn-spinner */
.btn-spinner{width:16px;height:16px;flex-shrink:0;border:2px solid rgba(255,255,255,.35);border-top-color:#fff;border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

/* 15. Skeleton/shimmer */
.shimmer{background:linear-gradient(90deg,var(--bg3) 25%,var(--bg2) 50%,var(--bg3) 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:6px;height:42px}
@keyframes shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}

/* 16. Indeterminate bar */
.admin-progress{height:2px;background:var(--bg2);overflow:hidden;position:relative}
.admin-progress::after{content:'';position:absolute;top:0;left:0;height:100%;width:50%;background:var(--orange);animation:indeterminate 1.5s infinite ease-in-out;transform-origin:0% 50%}
@keyframes indeterminate{0%{transform:translateX(-100%) scaleX(0.2)}50%{transform:translateX(0) scaleX(1)}100%{transform:translateX(200%) scaleX(0.2)}}

/* 17. Write mode bar */
.write-mode-bar{position:fixed;top:env(safe-area-inset-top, 0);left:0;right:0;height:3px;background:var(--red);z-index:999}

/* 18. Modals */
.admin-modal-backdrop{position:fixed;inset:0;background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);z-index:1000;display:flex;align-items:center;justify-content:center;padding:calc(24px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left))}
.admin-modal{width:100%;max-width:500px;background:var(--bg);border-radius:16px;box-shadow:0 16px 48px rgba(0,0,0,0.2);border:.5px solid var(--border);display:flex;flex-direction:column;max-height:90vh;overflow:hidden}
.admin-modal-title{margin:0;padding:24px 24px 12px 24px;font-size:18px;font-weight:600;color:var(--text);letter-spacing:-0.01em}
.admin-modal-body{padding:0 24px 16px 24px;display:flex;flex-direction:column;gap:12px;overflow-y:auto}
.admin-modal .admin-input{margin:0 24px 16px 24px}
.admin-modal-footer{padding:16px 24px;border-top:.5px solid var(--border);display:flex;justify-content:flex-end;gap:12px;background:var(--bg2);border-radius:0 0 16px 16px}

/* Channel Picker */
.channel-picker-modal{display:flex;flex-direction:column;gap:12px}
.channel-current-info{display:flex;align-items:center;gap:8px;font-size:14px;padding:8px 12px;background:var(--bg2);border-radius:8px;border:.5px solid var(--border)}
.channel-options-grid{display:flex;flex-direction:column;gap:8px}
.channel-option-card{display:flex;flex-direction:column;gap:4px;padding:12px 14px;background:var(--bg2);border:1px solid var(--border);border-radius:10px;cursor:pointer;transition:border-color .15s,background-color .15s}
.channel-option-card:hover{background:var(--bg3);border-color:color-mix(in srgb,var(--text) 30%,transparent)}
.channel-option-card.selected{border-color:var(--orange);background:color-mix(in srgb,var(--orange) 8%,var(--bg2))}
.selected{border-color:var(--orange)}
.channel-card-header{display:flex;align-items:center;justify-content:space-between}
.channel-card-desc{font-size:12px;color:var(--muted);margin-top:2px}
.channel-radio-indicator{width:16px;height:16px;border-radius:50%;border:1.5px solid var(--muted);position:relative;transition:border-color .15s,background-color .15s;flex-shrink:0}
.channel-option-card.selected .channel-radio-indicator{border-color:var(--orange);background:var(--orange);box-shadow:inset 0 0 0 3px var(--bg2)}

/* 19. Toast */
.admin-toast-container{position:fixed;bottom:calc(24px + env(safe-area-inset-bottom));right:calc(24px + env(safe-area-inset-right));z-index:2000;display:flex;flex-direction:column;gap:12px;pointer-events:none}
.admin-toast{background:var(--bg2);border:.5px solid var(--border);border-left:4px solid var(--muted);border-radius:8px;padding:16px;box-shadow:0 8px 24px rgba(0,0,0,0.2);pointer-events:auto;min-width:300px;animation:toast-in .3s cubic-bezier(0.175,0.885,0.32,1.275)}
/* One rule per state showToast() can emit, named the way .pill-* is: the JS
   maps success/error/info/warn -> ok/bad/idle/warn and sets
   `admin-toast-<state>`. These were `.admin-toast.success` / `.error` until
   2026-08-22 and so matched nothing the JS produced -- every toast kept the
   grey --muted left border above, which made a failure indistinguishable from
   a success. `idle` restates that grey deliberately: four emitted states, four
   rules, none left falling through to a default that only looks correct. */
.admin-toast-ok{border-left-color:var(--green)}
.admin-toast-warn{border-left-color:var(--orange)}
.admin-toast-bad{border-left-color:var(--red)}
.admin-toast-idle{border-left-color:var(--muted)}
@keyframes toast-in{from{transform:translateX(100%);opacity:0}to{transform:translateX(0);opacity:1}}

/* 20. Mono class */
.mono{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}

/* 21. tabular-nums */
.tnum{font-variant-numeric:tabular-nums}

/* 23. Responsive — tablet (≤900px) and phone (≤480px) */
/* The old block here hid .admin-shell on ≤700px and showed a
   .small-screen-notice that was never created — black screen on every phone.
   Removed 2026-08-09. The console is useful on a phone; it just needs
   tighter padding and a collapsed sidebar. */
@media (max-width: 900px){
  .admin-shell{grid-template-columns:1fr}
  .admin-sidebar{height:auto;position:static;border-right:none;border-bottom:.5px solid var(--border);padding:calc(12px + env(safe-area-inset-top)) calc(12px + env(safe-area-inset-right)) 12px calc(12px + env(safe-area-inset-left));flex-direction:row;flex-wrap:wrap;gap:6px}
  .sidebar-group-title{width:100%;margin:8px 0 4px 0}
  /* Height stays at the 44px touch-target floor (DESIGN-SYSTEM.md §4) — the
     tightening on phones comes out of font-size and padding, never height. */
  .admin-nav-item{width:auto;height:44px;font-size:12px;padding:0 10px}
  .admin-content{padding:calc(16px + env(safe-area-inset-top)) calc(16px + env(safe-area-inset-right)) calc(16px + env(safe-area-inset-bottom)) calc(16px + env(safe-area-inset-left))}
  .admin-topbar{flex-wrap:wrap;height:auto;padding:12px 0;gap:8px}
  .admin-card{padding:16px}
  .admin-modal{max-width:calc(100vw - 32px)}
  .admin-toast{min-width:auto;max-width:calc(100vw - 48px)}
  .raw-card{grid-template-columns:1fr}
  .raw-left-pane{border-right:none;border-bottom:.5px solid var(--border);max-height:200px}
  .admin-cards-container{grid-template-columns:1fr}
  .stats-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
  .kv-grid{grid-template-columns:repeat(auto-fill,minmax(140px,1fr))}
}
@media (max-width: 480px){
  .admin-sidebar{gap:4px;padding:calc(8px + env(safe-area-inset-top)) calc(8px + env(safe-area-inset-right)) 8px calc(8px + env(safe-area-inset-left))}
  .admin-nav-item{height:44px;font-size:11px;padding:0 8px}
  .admin-content{padding:calc(12px + env(safe-area-inset-top)) calc(8px + env(safe-area-inset-right)) calc(12px + env(safe-area-inset-bottom)) calc(8px + env(safe-area-inset-left))}
  .admin-topbar{gap:6px}
  .topbar-title{font-size:16px}
  .admin-table td,.admin-table th{padding:8px 6px;font-size:12px}
  .auth-card{padding:24px 16px}
  .auth-title{font-size:20px}
  .kv-grid{grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px}
}

/* 26. Flash animation */
.row-flash{animation:row-flash 2s ease-out forwards}
@keyframes row-flash{0%{background-color:color-mix(in srgb,var(--green) 30%,transparent)}100%{background-color:transparent}}
/* Substitute a signal, don't remove one (MEMORIES.md): the row is replaced
   by the next re-render ~500ms after this class is added (admin-ops.js /
   admin-feedback.js), so a static hold of the same tint the animation
   starts from is the confirmation for the row you just wrote to, without
   motion. */
@media (prefers-reduced-motion: reduce) { .row-flash{animation:none;background-color:color-mix(in srgb,var(--green) 30%,transparent)} }

/* 27. Shell Components */
.sidebar-group{margin-bottom:24px}
.topbar-title{font-size:20px;font-weight:600;flex:1}
.detail-row{display:flex;justify-content:space-between;padding:8px 0;border-bottom:.5px solid var(--border)}
.detail-row:last-child{border-bottom:none}
.stat-label{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.05em}
.stat-value{font-size:24px;font-weight:600}
.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:48px 24px;text-align:center;color:var(--muted)}
.empty-state .icon{font-size:32px;margin-bottom:16px;opacity:0.5}
.empty-state .message{font-size:15px;font-weight:500;color:var(--text);margin-bottom:4px}
.empty-state .hint{font-size:13px}
.hint{font-size:13px;color:var(--muted)}
.admin-error-text{color:var(--red)}

/* 28. New Shell Components */
.admin-cards-container { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; }
.stats-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; }
.stat-item { display: flex; flex-direction: column; gap: 4px; }
.kv-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 14px 16px; }
.kv-item { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.kv-key { font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kv-val { font-size: 13.5px; font-weight: 500; color: var(--text); overflow-wrap: anywhere; word-break: break-word; min-height: 20px; display: flex; align-items: center; font-variant-numeric: tabular-nums; }
.controls-bar, .audit-controls, .raw-controls { display: flex; justify-content: flex-end; gap: 8px; margin-bottom: 16px; }
.state-footer { font-size: 12px; color: var(--muted); margin-top: 16px; }
.skeleton-table { display: flex; flex-direction: column; gap: 4px; width: 100%; }

/* Error blocks */
.error-401, .error-403, .error-429, .error-500, .error-network, .error-cold-start, .error-node-large, .error-truncated, .error-unknown, .error-message, .route-error {
    padding: 24px; background: var(--bg2); border: .5px solid var(--border); border-radius: 12px; color: var(--text); margin-bottom: 16px;
}
.error-401, .error-403, .error-429, .error-500 { border-left: 4px solid var(--red); }
.error-network, .error-cold-start, .error-node-large, .error-truncated { border-left: 4px solid var(--orange); }

/* Auth Gate */
.auth-gate-container { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: calc(24px + env(safe-area-inset-top)) calc(24px + env(safe-area-inset-right)) calc(24px + env(safe-area-inset-bottom)) calc(24px + env(safe-area-inset-left)); }
.auth-card { background: var(--bg2); border: .5px solid var(--border); border-radius: 16px; padding: 32px; width: 100%; max-width: 420px; box-shadow: 0 16px 48px rgba(0,0,0,0.2); }
.auth-title { font-size: 24px; font-weight: 600; margin-bottom: 8px; }
.auth-subtitle { color: var(--muted); margin-bottom: 24px; }
.auth-error { color: var(--red); margin-bottom: 16px; padding: 12px; background: color-mix(in srgb, var(--red) 10%, transparent); border-radius: 8px; }
.auth-info { color: var(--muted); margin-bottom: 16px; font-size: 13px; }

/* Sidebar components */
.sidebar-link, .sidebar-theme-btn, .sidebar-signout-btn, .sidebar-back-link {
    display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border-radius: 10px; font-size: 13px; color: var(--muted); cursor: pointer; text-decoration: none; border: none; background: none; width: 100%; transition: background .15s, color .15s;
}
.sidebar-link:hover, .sidebar-theme-btn:hover, .sidebar-signout-btn:hover, .sidebar-back-link:hover {
    background: var(--bg3); color: var(--text);
}
.sidebar-footer { margin-top: auto; padding-top: 16px; border-top: .5px solid var(--border); display: flex; flex-direction: column; gap: 8px; }
.sidebar-email { font-size: 13px; color: var(--muted); margin-bottom: 8px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; }

/* Topbar components */
.topbar-right { display: flex; align-items: center; gap: 12px; }
.topbar-refresh { flex-shrink: 0; }
.spinner { width: 16px; height: 16px; border: 2px solid var(--muted); border-top-color: var(--text); border-radius: 50%; animation: spin .8s linear infinite; }
.switch { appearance: none; width: 36px; height: 20px; background: var(--bg3); border-radius: 10px; position: relative; cursor: pointer; outline: none; border: 1px solid var(--border); transition: background .2s; flex-shrink: 0; padding: 0; }
.switch::after { content: ''; position: absolute; top: 1px; left: 1px; width: 16px; height: 16px; background: var(--muted); border-radius: 50%; transition: transform .2s, background .2s; }
.switch[aria-checked="true"] { background: var(--orange); border-color: var(--orange); }
.switch[aria-checked="true"]::after { transform: translateX(16px); background: #fff; }

.write-mode { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 500; white-space: nowrap; }
.write-mode-label { user-select: none; cursor: pointer; }
.write-only:disabled { opacity: 0.35; cursor: not-allowed; pointer-events: none; }
.write-only:not(:disabled) { opacity: 1; cursor: pointer; }

/* Misc */
.msg { padding: 12px; background: var(--bg3); border-radius: 8px; margin-bottom: 16px; font-size: 13px; }
.progress-bar { height: 4px; background: var(--bg3); border-radius: 2px; overflow: hidden; position: relative; }
.progress-bar::after { content: ''; position: absolute; left: 0; top: 0; height: 100%; width: 30%; background: var(--orange); animation: indeterminate 1.5s infinite ease-in-out; }
.confirm-hint { font-size: 13px; color: var(--muted); margin-top: 12px; }
.hover-copy-btn { opacity: 0; transition: opacity .15s; background: none; border: none; color: var(--muted); cursor: pointer; padding: 4px; }
*:hover > .hover-copy-btn { opacity: 1; }
.hover-copy-btn:hover { color: var(--text); }
.admin-modal-error { color: var(--red); font-size: 13px; margin-top: 12px; }

/* Raw DB browser */
.raw-card { background: var(--bg2); border: .5px solid var(--border); border-radius: 12px; display: grid; grid-template-columns: 240px 1fr; overflow: hidden; min-height: 400px; }
.raw-left-pane { border-right: .5px solid var(--border); padding: 16px; overflow-y: auto; }
.raw-right-pane { padding: 16px; overflow-y: auto; background: var(--bg); }
.raw-breadcrumb { font-size: 13px; color: var(--muted); margin-bottom: 16px; display: flex; gap: 4px; }
.raw-keys { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 2px; }
.raw-keys li { padding: 8px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); }
.raw-keys li:hover { background: var(--bg3); }
.raw-root-note { padding: 16px; color: var(--muted); font-size: 13px; }

/* 28. Device-detail structure.
   These replace Tailwind utilities that were deleted with nothing put back --
   `flex justify-between items-center mb-4` on the section header, `grid
   grid-cols-2 gap-4` on the summary, and so on. The layout they carried is
   real, so it lands on a semantic class rather than being dropped: an empty
   className passes the emitted-class test trivially, because there is nothing
   left to check. Tokens only, per DESIGN-SYSTEM.md -- no per-theme overrides. */
.admin-section-title{font-size:20px;font-weight:500;margin-bottom:16px}
.admin-card-title{font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:12px}
.admin-breadcrumb{margin-bottom:16px;font-size:13px;color:var(--muted)}
.admin-link{color:var(--orange);text-decoration:none}
.admin-link:hover{text-decoration:underline}
.cmd-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:8px}

/* 29. Links, and wide-table containment.
   Bare <a> in the content column rendered rgb(0,0,238) -- literal browser blue
   on a #111 background -- because nothing styled it. Scoped to .admin-content
   so it cannot outrank .admin-nav-item / .sidebar-link in the sidebar, which
   set their own colour. --orange is the single accent (DESIGN-SYSTEM.md §1). */
.admin-content a:not(.admin-btn){color:var(--orange);text-decoration:none}
.admin-content a:not(.admin-btn):hover{text-decoration:underline}
/* :not(.admin-btn) is load-bearing. `.admin-content a` is specificity
   (0,1,1) and .admin-btn-primary is (0,1,0), so the link colour won on
   every <a> styled as a button -- the fleet View button rendered
   var(--orange) text on a var(--orange) background and its label
   vanished. Buttons keep their own colour. */
/* Wide tables scroll INSIDE their own container; the page body never scrolls
   horizontally. */
.admin-table-wrap{width:100%;overflow-x:auto}

/* Every section's content scrolls inside the content column, never the page.
   .admin-table-wrap covers the fleet table, but the other five modules render
   a bare <table class="admin-table"> with no wrapper, and 5 of 13 sections
   still scrolled the BODY sideways after the wrap landed. One rule on the
   mount point catches all of them without touching five modules. */
#admin-container{min-width:0;overflow-x:auto}

/* A section header is title-left / actions-right. .controls-bar is
   justify-content:flex-end and right-aligns the TITLE too, which is what it
   did to "Fleet Overview". Different job, different class. */
.admin-section-header{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:16px}
/* ── 30. Device principals, and the write modals that reach the server ──────── */

/* A device: uid is a Pitmaster's own Auth identity, not a status. It gets a
   neutral, structural chip built from --text over --bg3 so it cannot be read
   as one of the four status colours (ok / warn / bad / idle). */
.pill-device{color:var(--text);background:var(--bg3);border-color:var(--border)}

/* Inline checkbox + label in a controls-bar. */
.admin-toggle-label{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:var(--muted);cursor:pointer;user-select:none}
.admin-toggle-label input{accent-color:var(--orange);cursor:pointer}

/* The one-line count under a section title. */
.admin-hint{font-size:12px;color:var(--muted);margin:-8px 0 12px}

/* "This is a device, not a person" -- an explanatory card, not an error. */
.admin-notice{border-color:color-mix(in srgb,var(--orange) 34%,var(--border))}
.admin-notice h2{font-size:15px;margin-bottom:8px}
.admin-notice p{font-size:13px;color:var(--muted);margin-bottom:12px;line-height:1.5}

/* Op detail inside a confirm modal: label / value pairs. */
.confirm-detail{display:grid;grid-template-columns:auto 1fr;gap:4px 12px;margin:10px 0;font-size:12px}
.confirm-detail dt{color:var(--muted)}
.confirm-detail dd{min-width:0;overflow-wrap:anywhere}

/* Before/after for a raw write -- the value leaving the database, above the one
   replacing it. Each pane scrolls itself; the modal never grows to fit JSON. */
.confirm-diff{margin:10px 0}
.confirm-diff-label{font-size:11px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:8px 0 4px}
.confirm-diff .jsonv{max-height:180px;overflow:auto}

/* Number inputs in the set-targets modal. */
.admin-form-grid{display:grid;gap:10px;margin:10px 0}
.admin-form-grid label{display:grid;gap:4px;font-size:12px;color:var(--muted)}
.admin-form-grid input{background:var(--bg3);border:.5px solid var(--border);border-radius:8px;color:var(--text);padding:8px 10px;font:inherit;font-size:13px}

/* The claim-repair fix picker. Matches the number inputs above it. */
.admin-select{background:var(--bg3);border:.5px solid var(--border);border-radius:8px;color:var(--text);padding:8px 10px;font:inherit;font-size:13px;width:100%}

/* The filter box in Users/Cooks shipped as a bare <input> -- browser-chrome
   white on a dark page. Same recipe as the modal inputs. */
.admin-input{background:var(--bg3);border:.5px solid var(--border);border-radius:8px;color:var(--text);padding:7px 10px;font:inherit;font-size:13px;min-width:220px}
.admin-input:focus-visible{outline:2px solid var(--orange);outline-offset:-1px}

/* Session picker on the device page: chips, not a bullet list. */
.session-list{display:flex;flex-wrap:wrap;gap:8px}
.session-chip{font-size:12px}
.session-chip.active{border-color:var(--orange);box-shadow:inset 0 0 0 1px var(--orange)}

/* Every chart on this page gets a fixed-height positioned box -- Chart.js with
   maintainAspectRatio:false grows without bound inside an auto-height parent. */
.chart-wrap{position:relative;height:300px;width:100%}

/* Remote Shell Card & Terminal */
.device-shell-wrap{margin-top:16px}
.shell-card{border-color:var(--border);overflow:hidden;margin-top:16px}
.shell-summary{cursor:pointer;list-style:none;padding:4px 0;user-select:none;display:flex;align-items:center;justify-content:space-between}
.shell-summary::-webkit-details-marker{display:none}
.shell-summary::after{content:'▼';font-size:11px;color:var(--muted);transition:transform .2s ease;margin-left:12px}
details:not([open]) .shell-summary::after{transform:rotate(-90deg)}
.shell-title-wrap{display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.shell-badge{font-size:11px;font-weight:600;padding:2px 8px;border-radius:9999px;letter-spacing:.05em;text-transform:uppercase}
.shell-badge-idle{background:var(--bg3);color:var(--muted);border:.5px solid var(--border)}
.shell-badge-connecting{background:rgba(234,179,8,.15);color:#eab308;border:.5px solid #eab308}
.shell-badge-active{background:rgba(34,197,94,.15);color:#22c55e;border:.5px solid #22c55e}
.shell-badge-error{background:rgba(239,68,68,.15);color:#ef4444;border:.5px solid #ef4444}
.shell-timer{font-size:12px;font-family:monospace;color:var(--muted);margin-left:auto}
.shell-body{padding-top:12px;border-top:.5px solid var(--border);margin-top:10px;display:grid;gap:12px}
.shell-toolbar{display:flex;gap:8px;align-items:center}
.shell-terminal-box,#device-shell-terminal{background:#121212;border:1px solid var(--border);border-radius:8px;padding:10px;min-height:280px;max-height:480px;overflow:hidden;position:relative}
.shell-fallback-pre{color:#f87171;font-family:monospace;font-size:12px;margin:0;padding:12px}

.ota-versions-badge{display:flex;align-items:center;gap:6px;font-size:12px;margin-left:auto}


/* ── 33. xterm.js styling ── */
/**
 * Copyright (c) 2014 The xterm.js authors. All rights reserved.
 * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
 * https://github.com/chjj/term.js
 * @license MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * Originally forked from (with the author's permission):
 *   Fabrice Bellard's javascript vt100 for jslinux:
 *   http://bellard.org/jslinux/
 *   Copyright (c) 2011 Fabrice Bellard
 *   The original design remains. The terminal itself
 *   has been extended to include xterm CSI codes, among
 *   other features.
 */

/**
 *  Default styles for xterm.js
 */

.xterm {
    cursor: text;
    position: relative;
    user-select: none;
    -ms-user-select: none;
    -webkit-user-select: none;
}

.xterm.focus,
.xterm:focus {
    outline: none;
}

.xterm .xterm-helpers {
    position: absolute;
    top: 0;
    /**
     * The z-index of the helpers must be higher than the canvases in order for
     * IMEs to appear on top.
     */
    z-index: 5;
}

.xterm .xterm-helper-textarea {
    padding: 0;
    border: 0;
    margin: 0;
    /* Move textarea out of the screen to the far left, so that the cursor is not visible */
    position: absolute;
    opacity: 0;
    left: -9999em;
    top: 0;
    width: 0;
    height: 0;
    z-index: -5;
    /** Prevent wrapping so the IME appears against the textarea at the correct position */
    white-space: nowrap;
    overflow: hidden;
    resize: none;
}

.xterm .composition-view {
    /* TODO: Composition position got messed up somewhere */
    background: #000;
    color: #FFF;
    display: none;
    position: absolute;
    white-space: nowrap;
    z-index: 1;
}

.xterm .composition-view.active {
    display: block;
}

.xterm .xterm-viewport {
    /* On OS X this is required in order for the scroll bar to appear fully opaque */
    background-color: #000;
    overflow-y: scroll;
    cursor: default;
    position: absolute;
    right: 0;
    left: 0;
    top: 0;
    bottom: 0;
}

.xterm .xterm-screen {
    position: relative;
}

.xterm .xterm-screen canvas {
    position: absolute;
    left: 0;
    top: 0;
}

.xterm .xterm-scroll-area {
    visibility: hidden;
}

.xterm-char-measure-element {
    display: inline-block;
    visibility: hidden;
    position: absolute;
    top: 0;
    left: -9999em;
    line-height: normal;
}

.xterm.enable-mouse-events {
    /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
    cursor: default;
}

.xterm.xterm-cursor-pointer,
.xterm .xterm-cursor-pointer {
    cursor: pointer;
}

.xterm.column-select.focus {
    /* Column selection mode */
    cursor: crosshair;
}

.xterm .xterm-accessibility,
.xterm .xterm-message {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    z-index: 10;
    color: transparent;
    pointer-events: none;
}

.xterm .live-region {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.xterm-dim {
    /* Dim should not apply to background, so the opacity of the foreground color is applied
     * explicitly in the generated class and reset to 1 here */
    opacity: 1 !important;
}

.xterm-underline-1 { text-decoration: underline; }
.xterm-underline-2 { text-decoration: double underline; }
.xterm-underline-3 { text-decoration: wavy underline; }
.xterm-underline-4 { text-decoration: dotted underline; }
.xterm-underline-5 { text-decoration: dashed underline; }

.xterm-overline {
    text-decoration: overline;
}

.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }

.xterm-strikethrough {
    text-decoration: line-through;
}

.xterm-screen .xterm-decoration-container .xterm-decoration {
	z-index: 6;
	position: absolute;
}

.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
	z-index: 7;
}

.xterm-decoration-overview-ruler {
    z-index: 8;
    position: absolute;
    top: 0;
    right: 0;
    pointer-events: none;
}

.xterm-decoration-top {
    z-index: 2;
    position: relative;
}

/* ── Overview section ─────────────────────────────────────────────── */
.admin-wide{grid-column:1/-1}
.admin-status{display:flex;align-items:center;gap:10px;padding:12px 16px;border-radius:12px;border:.5px solid var(--border);background:var(--bg2);margin-bottom:12px;font-size:14px}
.admin-status .dot{width:10px;height:10px;border-radius:50%;background:var(--muted);flex:none}
.admin-status.ok .dot{background:var(--green)}
.admin-status.warn .dot{background:var(--red)}
.admin-status .banner-time{margin-left:auto;color:var(--muted);font-size:12px;white-space:nowrap}
.overview-sub{font-size:11px;font-weight:500;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin:16px 0 4px}
.chart-wrap{position:relative;height:220px;margin-top:12px}
.doughnut-wrap{max-width:520px}
.ver-row{display:flex;align-items:center;gap:8px;margin-top:6px;font-size:13px}
.ver-row .ver-label{min-width:110px}
.ver-row .ver-bar{flex:1;height:8px;background:var(--bg3);border-radius:4px;overflow:hidden}
.ver-row .ver-bar>span{display:block;height:100%;background:var(--orange)}
.ver-row .ver-count{min-width:40px;text-align:right}
.delta-line{color:var(--muted);font-size:13px;margin:8px 0 0}
.muted-note{color:var(--muted);font-size:13px}
.card-link{margin:10px 0 0;font-size:13px}

/* 30. Phone and tablet menu: one sideways strip, so a section starts at the top
   of the screen rather than under a block of links. Here at the end because the
   sidebar rules above come after the first @media block. */
@media (max-width: 900px){
  .admin-shell{grid-template-rows:auto 1fr}
  .admin-sidebar{position:sticky;top:0;z-index:10;flex-wrap:nowrap;align-items:center;overflow-x:auto;gap:4px;padding-top:calc(4px + env(safe-area-inset-top));padding-bottom:4px}
  .sidebar-group{display:flex;flex:none;gap:2px;margin:0}
  .sidebar-group-title,.sidebar-email{display:none}
  .sidebar-footer{flex-direction:row;flex:none;align-items:center;margin:0;padding:0 0 0 8px;border-top:none;border-left:.5px solid var(--border)}
  .sidebar-link,.sidebar-theme-btn,.sidebar-signout-btn,.sidebar-back-link{height:44px;white-space:nowrap}
  .admin-btn{min-height:44px}
}
