 
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700;900&display=swap');
/* KOPPEL RMM — UI theme, matched to the support.koppel design system. */
:root {
  --bg:       #0A0A0A;
  --surf:     #111111;
  --surf2:    #161616;
  --surf3:    #1a1a1a;
  --border:   #1f1f1f;
  --border2:  #2a2a2a;
  --orange:      #E8650A;
  --orange-h:    #ff7a1a;
  --orange-d:    rgba(232,101,10,.14);
  --orange-dark: #8B3D06;
  --white:    #FFFFFF;
  --gray:     #777777;
  --gray-l:   #AAAAAA;
  --gray-d:   #444444;
  --green:    #10B981;
  --red:      #EF4444;
  --amber:    #F59E0B;
  --blue:     #3B82F6;
  --hover-1:  rgba(255,255,255,.04);
  --hover-2:  rgba(255,255,255,.07);
  --fd: 'Roboto', sans-serif;
  --fb: 'Roboto', sans-serif;
  --mono: ui-monospace, 'Cascadia Code', 'JetBrains Mono', Consolas, monospace;
  --radius: 6px;
  --shadow: 0 4px 24px rgba(0,0,0,.4);
}
:root[data-theme="light"] {
  --bg:       #F2F2EF;
  --surf:     #FFFFFF;
  --surf2:    #F8F8F6;
  --surf3:    #EFEFEC;
  --border:   #E4E4E0;
  --border2:  #D5D5CF;
  --white:    #1C1C19;
  --gray:     #85847E;
  --gray-l:   #55544E;
  --gray-d:   #CBCBC4;
  --hover-1:  rgba(20,20,15,.045);
  --hover-2:  rgba(20,20,15,.07);
  --shadow: 0 4px 24px rgba(0,0,0,.12);
}
*,*::before,*::after { box-sizing:border-box; margin:0; padding:0; }
html { height:100%; height:100dvh; font-size:14px; }
body { background:var(--bg); color:var(--white); font-family:var(--fb); font-weight:400; line-height:1.6; height:100%; height:100dvh; overflow-x:hidden; -webkit-font-smoothing:antialiased; -webkit-tap-highlight-color:transparent; }
a { color:inherit; text-decoration:none; }
button { cursor:pointer; font-family:var(--fb); }
input,textarea,select { font-family:var(--fb); accent-color:var(--orange); }
.hidden { display:none !important; }
::-webkit-scrollbar { width:5px; height:5px; }
::-webkit-scrollbar-track { background:var(--surf); }
::-webkit-scrollbar-thumb { background:var(--border2); border-radius:3px; }
::-webkit-scrollbar-thumb:hover { background:var(--gray-d); }

/* ─── Buttons ─────────────────────────────────────────── */
.btn { display:inline-flex; align-items:center; justify-content:center; gap:6px; padding:8px 16px; font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; border:none; border-radius:var(--radius); transition:all .2s; white-space:nowrap; }
.btn-primary { background:var(--orange); color:var(--white); }
.btn-primary:hover { background:var(--orange-h); }
.btn-primary.danger { background:#DC2626; }
.btn-primary.danger:hover { background:#EF4444; }
.btn-secondary { background:var(--surf3); color:var(--gray-l); border:1px solid var(--border2); }
.btn-secondary:hover { color:var(--white); border-color:var(--gray-d); }
.btn:disabled { opacity:.45; cursor:not-allowed; }

/* ─── Forms ───────────────────────────────────────────── */
.form-group { margin-bottom:16px; }
.form-label { display:block; font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--gray-l); margin-bottom:6px; }
.form-control { width:100%; background:var(--surf2); border:1px solid var(--border2); border-radius:var(--radius); padding:9px 12px; color:var(--white); font-size:13px; outline:none; transition:border-color .2s; }
.form-control::placeholder { color:var(--gray); }
.form-control:focus { border-color:var(--orange); }
select.form-control option { background:var(--surf); color:var(--white); }

/* ═══ LOGIN ═══════════════════════════════════════════════ */
.login-page { min-height:100vh; min-height:100dvh; background:var(--bg); display:flex; align-items:center; justify-content:center; padding:20px; }
.login-card { width:100%; max-width:420px; }
.login-logo { text-align:center; margin-bottom:36px; }
.login-logo img { max-width:70%; max-height:64px; object-fit:contain; margin:0 auto 14px; display:block; }
.login-logo .logo-sub2 { font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--gray); margin-top:4px; }
.login-box { background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:32px; }
.login-box h2 { font-family:var(--fd); font-size:22px; font-weight:700; letter-spacing:.05em; text-transform:uppercase; margin-bottom:24px; }
.login-footer { text-align:center; margin-top:24px; font-size:12px; color:var(--gray); }
.login-footer .koppel-link { color:#FF6700; font-weight:600; text-decoration:none; }
.err { color:#f87171; font-size:13px; min-height:18px; margin-top:10px; text-align:center; }

/* ═══ APP SHELL ═══════════════════════════════════════════ */
#app { display:flex; height:100vh; height:100dvh; overflow:hidden; }

/* Sidebar */
.sidebar { position:relative; width:var(--sidebar-w,260px); min-width:180px; max-width:480px; background:var(--surf); border-right:1px solid var(--border); display:flex; flex-direction:column; height:100dvh; overflow:hidden; z-index:100; flex-shrink:0; }
.sidebar-logo { padding:14px 16px; border-bottom:1px solid var(--border); display:flex; align-items:center; min-height:64px; }
.sidebar-logo img { width:100%; height:auto; max-height:40px; object-fit:contain; object-position:left center; }
.sidebar-nav { flex:1; padding:8px 0; overflow-y:auto; }
/* Drag handle hugging the sidebar's right edge - widened hit target (8px) over a thin
   visible line (2px, centered) so it's easy to grab without feeling like a fat border. */
.sidebar-resize-handle { position:absolute; top:0; right:-4px; width:8px; height:100%; cursor:col-resize; z-index:101; touch-action:none; }
.sidebar-resize-handle::after { content:''; position:absolute; top:0; left:3px; width:2px; height:100%; background:transparent; }
.sidebar-resize-handle:hover::after, .sidebar-resize-handle.dragging::after { background:var(--orange); }
.nav-section-label { display:flex; align-items:center; justify-content:space-between; font-size:10px; font-weight:600; letter-spacing:.12em; text-transform:uppercase; color:var(--gray); padding:16px 16px 6px; }
.section-add { background:var(--surf3); border:1px solid var(--border2); color:var(--gray-l); width:24px; height:24px; border-radius:5px; font-size:17px; line-height:1; display:flex; align-items:center; justify-content:center; flex:none; }
.section-add:hover { color:var(--white); border-color:var(--orange); }
.nav-item { display:flex; align-items:center; gap:12px; padding:10px 16px; color:var(--gray-l); font-size:13px; font-weight:500; border-left:3px solid transparent; transition:all .15s; cursor:pointer; user-select:none; }
.nav-item:hover { color:var(--white); background:var(--hover-1); }
.nav-item.active { color:var(--white); border-left-color:var(--orange); background:var(--orange-d); }
.nav-item.nested { padding-left:44px; }
.nav-item .st { width:9px; height:9px; border-radius:50%; background:var(--gray-d); flex:none; }
.nav-item .st.online { background:var(--green); box-shadow:0 0 8px var(--green); }
.nav-item .st.offline { background:var(--red); }
.nav-item .st.alert { background:var(--orange); box-shadow:0 0 8px var(--orange); animation:pulse-alert 1.6s ease-in-out infinite; }
@keyframes pulse-alert { 0%,100%{opacity:1} 50%{opacity:.4} }
.nav-item .meta { min-width:0; }
.nav-item .name { font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.self-badge { display:inline-flex; align-items:center; margin-left:6px; font-size:9px; line-height:1; font-weight:700; text-transform:uppercase; letter-spacing:.04em; color:var(--orange-h); background:var(--orange-d); border-radius:4px; padding:2px 5px; vertical-align:middle; }
.nav-item .os { font-size:11px; color:var(--gray); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.empty { color:var(--gray); font-size:13px; padding:14px 16px; }

/* Client / Site tree */
.tree-row { display:flex; align-items:center; gap:8px; padding:9px 16px; cursor:pointer; user-select:none; color:var(--gray-l); }
.tree-row:hover { color:var(--white); background:var(--hover-1); }
.tree-row.site-row { padding-left:32px; }
.tree-chevron { width:16px; flex:none; font-size:20px; line-height:1; display:inline-flex; align-items:center; color:var(--gray); transition:transform .15s; }
.tree-chevron.open { transform:rotate(90deg); }
.tree-name { flex:1; min-width:0; font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.tree-row.site-row .tree-name { font-weight:500; font-size:12.5px; }
.tree-children.hidden { display:none; }
.tree-count { color:var(--gray); font-weight:400; margin-left:4px; }

/* Right-click context menu (clients/sites) */
.context-menu { position:fixed; z-index:300; background:var(--surf2); border:1px solid var(--border2); border-radius:var(--radius); box-shadow:var(--shadow); padding:6px; min-width:180px; display:flex; flex-direction:column; gap:1px; }
.context-menu button { background:none; border:none; color:var(--gray-l); text-align:left; padding:8px 10px; border-radius:6px; font-size:13px; }
.context-menu button:hover { color:var(--white); background:var(--hover-2); }
.context-menu button.danger { color:#f87171; }
.context-menu button.danger:hover { background:rgba(239,68,68,.12); }

/* Overview panel — no max-width, matching the landing dashboard's widget grid, which fills
   .content's full width edge-to-edge rather than sitting in a capped column. */
.card-box { background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:16px 18px; }
.ov-row { display:flex; justify-content:space-between; gap:10px; padding:7px 0; border-bottom:1px solid var(--border); font-size:13px; }
.ov-row:last-child { border-bottom:none; }
.ov-k { color:var(--gray); }
.ov-v { color:var(--white); font-weight:600; text-align:right; word-break:break-word; }
.ov-v-line { margin-bottom:4px; }
.ov-v-line:last-child { margin-bottom:0; }
.ov-stats { display:flex; flex-direction:column; gap:10px; }
.stat-tile { display:flex; align-items:center; justify-content:space-between; }
.stat-label { font-size:11px; color:var(--gray); text-transform:uppercase; letter-spacing:.06em; }
.stat-val { font-size:18px; font-weight:900; font-family:var(--fd); }
.ov-alert { background:rgba(239,68,68,.12); border:1px solid rgba(239,68,68,.35); color:#f87171; border-radius:var(--radius); padding:8px 10px; font-size:12px; margin-top:4px; }
.ov-subhead { font-family:var(--fd); font-size:13px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--orange); margin-bottom:10px; }

/* Hardware inventory */
.btn-sm { padding:5px 12px; font-size:10px; }
.ov-volumes { display:flex; flex-direction:column; }
.hw-empty { color:var(--gray); font-size:12px; }
.disk-bar-row { margin-bottom:12px; }
.disk-bar-row:last-child { margin-bottom:0; }
.disk-bar-label { display:flex; justify-content:space-between; font-size:12px; color:var(--gray-l); margin-bottom:5px; }
.disk-bar-label b { color:var(--white); }
.disk-bar-track { background:var(--surf3); border-radius:4px; height:8px; overflow:hidden; }
.disk-bar-fill { background:var(--orange); height:100%; border-radius:4px; }
.disk-bar-fill.low { background:#f87171; }

/* Landing dashboard (Overview tab, shown when no endpoint is selected) */
.landing-panel { width:100%; }
.stat-widget-num { font-family:var(--fd); font-size:24px; font-weight:900; text-align:center; }
.patch.clickable { cursor:pointer; }
.patch.clickable:hover { border-color:var(--gray-d); }

/* Widget-card grid (landing dashboard) — Android-launcher-style drag/resize.
   Row height here must match ROW_H in app.js. Columns are square fixed-px cells set
   inline by app.js's updateGridCellVars() (however many fit the current width), not a
   fixed count here - this rule is just the pre-JS/mobile fallback. */
.widget-grid-toolbar { display:flex; justify-content:flex-end; margin-bottom:10px; }
.widget-grid {
  position:relative; display:grid;
  grid-auto-rows:28px;
  gap:20px; margin-bottom:20px;
}
.widget-grid-dots {
  position:absolute; inset:0; z-index:0; pointer-events:none; border-radius:var(--radius);
  opacity:0; transition:opacity .15s ease;
  background-image:radial-gradient(circle, var(--orange) 1.5px, transparent 1.5px);
  background-size:var(--cell-w,100px) var(--cell-h,90px);
}
.widget-grid--editing .widget-grid-dots { opacity:1; }
.widget {
  position:relative; z-index:1; display:flex; flex-direction:column;
  transition:box-shadow .15s ease;
}
.widget-wide { max-width:none; }
.widget-grid--editing .widget-head { cursor:grab; touch-action:none; user-select:none; }
.widget.dragging { z-index:50; box-shadow:var(--shadow); cursor:grabbing; }
.widget.dragging .widget-head { cursor:grabbing; }
.widget.swap-preview { outline:2px dashed var(--orange); outline-offset:-2px; }
.widget-resize-handle {
  position:absolute; right:2px; bottom:2px; width:18px; height:18px; z-index:2;
  cursor:nwse-resize; touch-action:none;
  background:
    linear-gradient(135deg, transparent 0 40%, var(--gray-d) 40% 46%, transparent 46% 60%, var(--gray-d) 60% 66%, transparent 66%);
  opacity:.7; border-radius:0 0 var(--radius) 0;
}
.widget-resize-handle:hover { opacity:1; }
.widget-head { flex:none; display:flex; align-items:center; justify-content:space-between; margin-bottom:16px; }
.widget-title { font-family:var(--fd); font-size:13px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:var(--gray-l); }
.widget-count { font-family:var(--fd); font-size:13px; font-weight:900; color:var(--white); background:var(--surf3); border-radius:999px; padding:2px 10px; }
.widget-actions { display:flex; gap:8px; }
/* scrollbar-gutter:stable here too, same reason as .content: without it the gutter is
   only reserved once a card's content actually overflows, so its text visibly shifts
   left the moment that happens (and shifts back if it's resized taller again). */
.widget-body { flex:1; min-height:0; overflow:auto; scrollbar-gutter:stable; }
.widget-body-center { flex:1; min-height:0; display:flex; flex-direction:column; justify-content:center; }
/* Below the sidebar-drawer breakpoint, dragging/resizing a 6-col pixel grid isn't a
   realistic phone interaction — widgets stack full-width in layout order instead.
   Edit Layout is desktop/tablet-only in practice; the button still works but has
   nothing meaningful to drag against here. */
@media (max-width:768px) {
  .widget-grid { grid-template-columns:1fr; grid-auto-rows:auto; }
  .widget { grid-column:auto !important; grid-row:auto !important; }
  .widget-grid-dots, .widget-resize-handle { display:none; }
}

/* Donut chart (Endpoints widget) */
.donut-wrap { position:relative; width:120px; height:120px; margin:0 auto 16px; }
.donut { width:100%; height:100%; transform:rotate(-90deg); }
.donut-track { fill:none; stroke:var(--orange-dark); }
.donut-seg { fill:none; stroke:var(--orange); stroke-linecap:butt; transition:stroke-dashoffset .5s ease; }
.donut-center { position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center; }
.donut-num { font-family:var(--fd); font-size:22px; font-weight:900; }
.donut-sub { font-size:10px; text-transform:uppercase; letter-spacing:.06em; color:var(--gray); }
.donut-legend { display:flex; justify-content:center; gap:20px; font-size:12px; color:var(--gray-l); }
.donut-legend b { color:var(--white); }
.dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:6px; }
.dot.online { background:var(--orange); }
.dot.offline { background:var(--orange-dark); }

/* Bar chart (Alerts by Type widget) */
.bar-chart { display:flex; justify-content:space-around; align-items:flex-end; height:140px; gap:12px; }
.bar-col { display:flex; flex-direction:column; align-items:center; height:100%; flex:1; }
.bar-track { flex:1; width:28px; display:flex; align-items:flex-end; background:var(--surf3); border-radius:4px; overflow:hidden; }
.bar-fill { width:100%; min-height:2px; border-radius:4px 4px 0 0; transition:height .5s ease; }
.bar-fill.tier-critical { background:var(--red); }
.bar-fill.tier-elevated { background:var(--orange); }
.bar-fill.tier-normal { background:var(--amber); }
.bar-fill.tier-low { background:var(--blue); }
.bar-label { margin-top:8px; font-size:11px; color:var(--gray-l); text-align:center; }
.bar-label b { display:block; font-family:var(--fd); font-size:14px; color:var(--white); }

/* 24h alert history graph (landing dashboard) */
/* Real fixed-pixel padding, not just the SVG's internal viewBox-unit padding - the hover
   dot's glow is a fixed-px box-shadow, but the chart scales non-uniformly to fill the
   card (preserveAspectRatio="none"), so viewBox-unit padding alone translates to a
   different (and sometimes too-small) real pixel margin depending on card width. This
   guarantees actual pixel space between the chart and widget-body's overflow:auto edge
   regardless of how the card is sized. */
.alert-hist-wrap { position:relative; display:flex; flex-direction:column; height:100%; padding:14px 16px; }
.alert-hist-chart { flex:1; min-height:0; width:100%; display:block; }
.alert-hist-labels { display:flex; justify-content:space-between; font-size:11px; color:var(--gray); margin-top:2px; }
.alert-hist-hit { cursor:pointer; }
/* Real HTML element, not SVG - positioned in actual pixels at hover time (see
   wireAlertHistTooltip), so it stays a perfect circle regardless of the chart's
   non-uniform preserveAspectRatio="none" scaling. 7px beats the line's 2px stroke
   clearly without overwhelming it. */
.alert-hist-dot {
  position:absolute; width:7px; height:7px; margin:-3.5px 0 0 -3.5px;
  border-radius:50%; background:var(--orange-h);
  box-shadow: 0 0 6px 1px rgba(255,122,26,.9), 0 0 16px 4px rgba(255,122,26,.45);
  pointer-events:none; z-index:4;
}
.alert-hist-tooltip {
  position:absolute; z-index:5; pointer-events:none; max-width:220px;
  background:var(--surf3); border:1px solid var(--border2); border-radius:var(--radius);
  padding:8px 10px; font-size:11.5px; line-height:1.6; box-shadow:var(--shadow);
}
.alert-hist-tooltip-time { font-family:var(--fd); font-size:10px; font-weight:700; letter-spacing:.04em; text-transform:uppercase; color:var(--gray-l); margin-bottom:4px; }
.alert-hist-tooltip-row { color:var(--white); white-space:nowrap; }
.alert-hist-tooltip-row b { color:var(--orange-h); font-weight:600; }
.alert-hist-tooltip-row.empty { color:var(--gray); font-style:italic; }

/* Deploy Agent panel */
.deploy-panel { max-width:520px; }
.deploy-title { font-family:var(--fd); font-size:20px; font-weight:900; letter-spacing:.02em; text-transform:uppercase; margin-bottom:8px; }
.deploy-sub { color:var(--gray); font-size:13px; margin-bottom:22px; line-height:1.6; }
.deploy-note { color:var(--gray); font-size:11px; margin-top:16px; line-height:1.7; }

/* Settings panel (topbar cog) - static 3-col grid, no drag/resize (unlike the
   dashboard/overview widget grids) since this is a small, fixed set of cards. */
.settings-panel { display:grid; grid-template-columns:repeat(3, 1fr); gap:20px; align-items:start; }
.settings-section { margin-bottom:0; }
@media (max-width:1100px) {
  .settings-panel { grid-template-columns:repeat(2, 1fr); }
}
@media (max-width:768px) {
  .settings-panel { grid-template-columns:1fr; }
}
.tier-metric { margin-bottom:16px; }
.tier-metric-label { font-size:12px; font-weight:600; color:var(--gray-l); text-transform:uppercase; letter-spacing:.05em; margin-bottom:8px; }
.tier-metric-note { text-transform:none; letter-spacing:0; font-weight:400; color:var(--gray); margin-left:6px; }
.tier-inputs { display:grid; grid-template-columns:repeat(4,1fr); gap:8px; }
.tier-input label { display:block; font-size:10px; color:var(--gray); text-transform:uppercase; letter-spacing:.04em; margin-bottom:4px; }
.tier-input .form-control { margin-bottom:0; padding:7px 8px; font-size:12px; }
@media (max-width:480px) { .tier-inputs { grid-template-columns:repeat(2,1fr); } }
.cs-row { display:flex; gap:8px; margin-bottom:8px; align-items:center; }
.cs-row .form-control { margin-bottom:0; }
.cs-row .icon-btn { background:var(--surf3); border:1px solid var(--border2); color:var(--gray-l); border-radius:var(--radius); padding:7px 10px; font-size:12px; font-weight:600; flex:none; }
.cs-row .icon-btn:hover { color:var(--white); border-color:var(--gray-d); }
.cs-row .icon-btn.danger:hover { color:#f87171; border-color:#f87171; }

/* Scripts panel (endpoint tab, next to Patch Management) */
.scripts-head { display:flex; align-items:center; gap:14px; margin-bottom:14px; }
.spacer { flex:1; }
.script-list { display:flex; flex-direction:column; gap:10px; }
.script-card { background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:14px 16px; }
.script-card-head { display:flex; align-items:center; gap:10px; }
.script-card .s-name { font-weight:700; font-size:14px; }
.script-card .s-desc { color:var(--gray); font-size:12px; margin-top:2px; }
.script-card .s-actions { margin-left:auto; display:flex; gap:6px; flex:none; }
.script-card .icon-btn { background:var(--surf3); border:1px solid var(--border2); color:var(--gray-l); border-radius:var(--radius); padding:7px 10px; font-size:12px; font-weight:600; }
.script-card .icon-btn:hover { color:var(--white); border-color:var(--gray-d); }
.script-card .icon-btn.danger:hover { color:#f87171; border-color:#f87171; }
.script-output { margin-top:12px; background:#000; border:1px solid var(--border); border-radius:var(--radius); padding:10px 12px; font-family:var(--mono); font-size:12px; white-space:pre-wrap; max-height:220px; overflow:auto; color:#d6d6d6; }
.script-output .err { color:#f87171; }

/* Files panel (endpoint tab, next to Scripts) */
/* A real fixed-position element (see wireFilesDragDrop in app.js), not a CSS inset:0
   pseudo-element on #endpointContent - that element has scrollbar-gutter:stable, which
   reserves space between its padding edge and border edge for a scrollbar. Absolutely
   positioned children are contained within the PADDING edge, so inset:0 there left a
   scrollbar-gutter-width gap uncovered on the right. Measuring the real rect in JS and
   using position:fixed sidesteps that entirely. */
.files-drop-overlay { position:fixed; z-index:50; display:none;
  place-items:center; background:rgba(232,101,10,.10); border:2px dashed var(--orange);
  border-radius:var(--radius); color:var(--orange-h); font-size:15px; font-weight:600; pointer-events:none; }
.files-drop-overlay.show { display:grid; }
.files-head { display:flex; align-items:center; gap:10px; margin-bottom:14px; }
.files-head #fPath { flex:1; font-family:var(--mono); height:40px; }
.files-head #fGo, .files-head #fUp, .files-head #fVolume, .files-head #fRefresh { height:40px; }
.files-head #fGo { font-size:12px; letter-spacing:.04em; }
.files-head #fRefresh { font-size:18px; padding:0 12px; }
.file-list { display:flex; flex-direction:column; gap:6px; }
.file-row { display:flex; align-items:center; gap:12px; background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:10px 14px; cursor:pointer; user-select:none; }
.file-row:hover { border-color:var(--gray-d); }
.file-row.selected { border-color:var(--orange); background:var(--orange-d); }
.file-row .f-icon { flex:none; font-size:15px; color:var(--gray-l); width:18px; text-align:center; }
.file-row .f-name { flex:1; min-width:0; font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.file-row .f-meta { flex:none; font-size:11px; color:var(--gray); margin-right:6px; }

/* Grid view (toggle in .files-head) - same rows, reflowed into square tiles.
   Fixed sizes throughout (icon, name block) rather than letting content decide -
   a short filename renders as 1 line vs. a long one wrapping to 2, and without a
   pinned height on .f-name that difference made aspect-ratio:1/1 on the tile
   itself unreliable (the row grew to fit whichever tile had the tallest content
   instead of staying uniformly square). */
.file-list.grid-view { display:grid; grid-template-columns:repeat(auto-fill,minmax(110px,1fr)); gap:10px; }
.file-list.grid-view .file-row { flex-direction:column; justify-content:center; align-items:center; text-align:center; gap:6px; padding:16px 8px; aspect-ratio:1/1; }
.file-list.grid-view .f-icon { flex:none; font-size:32px; width:auto; height:38px; line-height:38px; }
.file-list.grid-view .f-name {
  flex:none; width:100%; white-space:normal; word-break:break-word; font-size:12px; line-height:1.3;
  height:calc(1.3em * 2); display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden;
}
.file-list.grid-view .f-meta { display:none; }

/* Modal */
.modal-bg { position:fixed; inset:0; background:rgba(0,0,0,.6); display:grid; place-items:center; z-index:200; padding:20px; }
.modal { width:100%; max-width:520px; background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:26px; box-shadow:var(--shadow); max-height:90vh; overflow:auto; }
.modal h3 { font-family:var(--fd); font-size:18px; font-weight:700; letter-spacing:.03em; text-transform:uppercase; margin-bottom:18px; }
.modal .modal-actions { display:flex; gap:10px; margin-top:18px; }
.modal .modal-actions .btn { flex:1; }
.confirm-message { color:var(--gray-l); font-size:13.5px; line-height:1.6; white-space:pre-line; }
textarea.form-control.code { font-family:var(--mono); font-size:12.5px; min-height:180px; resize:vertical; }
.logo-preview-wrap { display:flex; justify-content:center; padding:20px; background:var(--bg); border:1px solid var(--border); border-radius:var(--radius); margin-bottom:16px; }
.logo-preview-wrap img { max-width:70%; max-height:70px; object-fit:contain; }
.avatar-preview-wrap { display:flex; justify-content:center; margin-bottom:16px; }
.avatar-preview { width:80px; height:80px; border-radius:50%; background:var(--orange-d); color:var(--orange); display:flex; align-items:center; justify-content:center; font-size:28px; font-weight:700; overflow:hidden; }
.avatar-preview img { width:100%; height:100%; object-fit:cover; }
.sidebar-footer { padding:12px; border-top:1px solid var(--border); }
.sidebar-user { display:flex; align-items:center; gap:10px; padding:8px; border-radius:var(--radius); cursor:pointer; transition:background .15s; }
.sidebar-user:hover { background:var(--hover-2); }
.sidebar-user .avatar { width:34px; height:34px; border-radius:50%; background:var(--orange-d); flex:none; display:flex; align-items:center; justify-content:center; font-weight:700; color:var(--orange); font-size:13px; overflow:hidden; }
.sidebar-user .avatar img { width:100%; height:100%; object-fit:cover; }
.sidebar-user .user-info { min-width:0; flex:1; }
.sidebar-user .user-name { font-size:13px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sidebar-user .user-role { font-size:11px; color:var(--gray); text-transform:capitalize; }

/* Main */
.main { position:relative; flex:1; display:flex; flex-direction:column; overflow:hidden; min-width:0; }
.topbar { height:64px; background:var(--surf); border-bottom:1px solid var(--border); display:flex; align-items:center; padding:0 20px; gap:14px; flex-shrink:0; }
.topbar-toggle { background:none; border:none; color:var(--gray-l); font-size:20px; padding:6px; border-radius:var(--radius); display:none; }
.topbar-toggle:hover { color:var(--white); background:var(--hover-2); }
.topbar-title { font-family:var(--fd); font-size:18px; font-weight:900; letter-spacing:.02em; text-transform:uppercase; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.topbar-right { margin-left:auto; display:flex; align-items:center; gap:14px; }
.conn { display:flex; align-items:center; gap:7px; height:32px; font-size:12px; color:var(--gray); text-transform:uppercase; letter-spacing:.06em; background:none; border:none; font-family:var(--fb); padding:0 8px; border-radius:var(--radius); cursor:pointer; transition:background .15s,color .15s; }
.conn:hover { background:var(--hover-2); color:var(--gray-l); }
.conn .live { width:8px; height:8px; border-radius:50%; background:var(--red); flex:none; }
.conn.on .live { background:var(--green); box-shadow:0 0 8px var(--green); }
.topbar-text-btn { display:flex; align-items:center; height:32px; font-size:12px; color:var(--gray); text-transform:uppercase; letter-spacing:.06em; background:none; border:none; font-family:var(--fb); padding:0 10px; border-radius:var(--radius); cursor:pointer; transition:background .15s,color .15s; flex:none; }
.topbar-text-btn:hover { background:var(--hover-2); color:var(--gray-l); }
.topbar-text-btn.logout-btn { color:var(--orange-h); }
.topbar-text-btn.logout-btn:hover { color:var(--orange-h); background:var(--orange-d); }

/* Tab bar */
.tabbar { display:flex; align-items:center; gap:2px; padding:0 20px; background:var(--surf); border-bottom:1px solid var(--border); flex-shrink:0; overflow-x:auto; -webkit-overflow-scrolling:touch; scrollbar-width:none; }
.tabbar::-webkit-scrollbar { display:none; }
.tab { padding:14px 18px; font-size:12px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--gray-l); border:none; background:none; border-bottom:2px solid transparent; transition:all .15s; flex:0 0 auto; white-space:nowrap; }
.tab:hover { color:var(--white); }
.tab.active { color:var(--orange); border-bottom-color:var(--orange); }

/* Content + panels */
/* scrollbar-gutter:stable reserves the scrollbar's width whether or not content is
   actually tall enough to need it, so the page never reflows a few px narrower the
   moment it starts overflowing (which used to read as the cards' right padding no
   longer matching the left). The scrollbar itself is thin and transparent by default,
   only turning visible on hover/while scrolling - a "disappearing" scrollbar rather
   than either an always-visible one or no indicator at all. */
.content {
  /* No bottom padding - padding on a scrolling element is always part of its scrollable
     area, so a bottom pad here meant scrolling to the very end revealed nothing but
     that padding: a small, real, but pointless-looking scroll range even when all the
     actual content already fit. Top/side padding don't have this problem since they're
     never "the last thing" being scrolled into view. */
  flex:1; min-height:0; overflow:auto; padding:20px 24px 0;
  scrollbar-gutter:stable;
  scrollbar-width:thin; scrollbar-color:transparent transparent;
}
.content:hover { scrollbar-color:var(--gray-d) transparent; }
.panel.hidden { display:none; }
.placeholder { display:grid; place-items:center; height:100%; min-height:340px; color:var(--gray); text-align:center; }

/* Same disappearing-scrollbar treatment for individual widget cards, which get their
   own internal scroll once resized shorter than their content. */
.widget-body { scrollbar-width:thin; scrollbar-color:transparent transparent; }
.widget-body:hover { scrollbar-color:var(--gray-d) transparent; }

.content::-webkit-scrollbar, .widget-body::-webkit-scrollbar { width:10px; }
.content::-webkit-scrollbar-track, .widget-body::-webkit-scrollbar-track { background:transparent; }
.content::-webkit-scrollbar-thumb, .widget-body::-webkit-scrollbar-thumb {
  background-color:transparent; background-clip:padding-box;
  border:3px solid transparent; border-radius:8px; transition:background-color .2s ease;
}
.content:hover::-webkit-scrollbar-thumb, .widget-body:hover::-webkit-scrollbar-thumb {
  background-color:var(--gray-d); background-clip:padding-box;
}
.content:active::-webkit-scrollbar-thumb, .widget-body:active::-webkit-scrollbar-thumb,
.content:focus-within::-webkit-scrollbar-thumb, .widget-body:focus-within::-webkit-scrollbar-thumb {
  background-color:var(--gray); background-clip:padding-box;
}

/* Remote desktop */
.rd-toolbar { display:flex; flex-wrap:wrap; align-items:center; gap:8px; margin-bottom:14px; }
.chip { display:inline-flex; align-items:center; gap:6px; background:var(--surf3); border:1px solid var(--border2); color:var(--gray-l); border-radius:var(--radius); padding:8px 13px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; transition:all .15s; }
.chip:hover { color:var(--white); border-color:var(--gray-d); }
.chip.toggle.on { border-color:var(--orange); color:var(--orange-h); background:var(--orange-d); }
.chip.info { text-transform:none; letter-spacing:0; color:var(--gray); cursor:default; }
.screen-wrap { background:#000; border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; position:relative; }
#screen { width:100%; display:block; touch-action:none; }
.screen-hint { position:absolute; inset:0; display:grid; place-items:center; color:var(--gray); font-size:13px; pointer-events:none; }

/* Console */
.console { display:flex; flex-direction:column; height:100%; min-height:420px; }
.console-out { flex:1; background:#000; border:1px solid var(--border); border-radius:var(--radius); padding:14px; font-family:var(--mono); font-size:12.5px; line-height:1.5; white-space:pre-wrap; overflow:auto; color:#d6d6d6; }
.console-out .cmd { color:var(--orange-h); } .console-out .err { color:#f87171; }
.console-in { display:flex; gap:8px; margin-top:10px; }
.console-in .form-control { font-family:var(--mono); }
.console-in select { max-width:130px; }

/* Patches */
.patch-head { display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:14px; }
.patch-list { display:flex; flex-direction:column; gap:8px; }
.patch { display:flex; align-items:center; gap:12px; background:var(--surf); border:1px solid var(--border); border-radius:var(--radius); padding:12px 14px; }
.patch input { width:17px; height:17px; accent-color:var(--orange); flex:none; }
.patch .t { flex:1; min-width:0; }
.patch .t .title { font-size:13px; font-weight:600; }
.patch .t .kb { font-size:11px; color:var(--gray); }
.badge { font-size:10px; font-weight:700; padding:3px 8px; border-radius:4px; text-transform:uppercase; letter-spacing:.04em; flex:none; }
.badge.win { background:rgba(16,185,129,.15); color:var(--green); }
.badge.tp { background:rgba(245,166,35,.15); color:var(--amber); }
.badge.err { background:rgba(239,68,68,.15); color:var(--red); }
.badge.muted { background:var(--surf3); color:var(--gray); }
.chip.danger { color:#f87171; border-color:rgba(239,68,68,.35); }
.chip.danger:hover { background:rgba(239,68,68,.12); border-color:#f87171; }
.patch-search-row { display:flex; align-items:center; gap:8px; margin-bottom:14px; }
.patch-search-row #pSearch { flex:1; }
.patch-search-row #pSearch, .patch-search-row #pRefreshBtn { height:36px; }
.patch.selectable { cursor:pointer; user-select:none; }
.patch.selectable:hover { border-color:var(--gray-d); }
.patch.selected { border-color:var(--orange); background:var(--orange-d); }

/* Toast progress bar (Install Custom / bulk program actions / file upload). Fixed width,
   not the old min/max range - a shrink-wrapped toast resizes as its label text changes
   length (e.g. the upload toast's percent/speed suffix growing and shrinking), which
   also visibly changes the bar-track's own width every tick since it's a block child. */
.toast.progress { width:340px; max-width:340px; }
.toast .bar-track { width:100%; height:6px; border-radius:3px; background:var(--surf3); margin-top:8px; overflow:hidden; }
.toast .bar-fill { height:100%; background:var(--orange); transition:width .2s; }
.toast.fade-out { opacity:0; transition:opacity .5s; }
.toast-progress-row { display:flex; align-items:center; justify-content:space-between; gap:10px; }
.toast-progress-label { flex:1; min-width:0; }
/* Two separate lines, not one truncated string - a long filename eating the whole line
   was hiding the percent/speed that came after it in the same string entirely. The
   filename can safely truncate; the status line (short, fixed-ish length) never does,
   so percent/speed stays visible regardless of how long the filename is. */
.toast-progress-filename { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.toast-progress-meta { color:var(--gray-l); font-size:11px; margin-top:2px; }
.toast-progress-cancel { flex:none; cursor:pointer; background:none; border:1px solid var(--border2); color:var(--gray-l);
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.04em; padding:3px 8px; border-radius:4px; }
.toast-progress-cancel:hover { color:var(--white); border-color:var(--gray-d); background:var(--hover-2); }

/* Toasts */
.toasts { position:fixed; bottom:18px; right:18px; display:flex; flex-direction:column; gap:8px; z-index:400; }
.toast { background:var(--surf2); border:1px solid var(--border2); border-left:3px solid var(--orange); border-radius:var(--radius); padding:11px 14px; font-size:13px; box-shadow:var(--shadow); max-width:320px; }
.toast.err { border-left-color:var(--red); } .toast.ok { border-left-color:var(--green); }

/* Mobile */
.sidebar-backdrop { display:none; position:fixed; inset:0; background:rgba(0,0,0,.5); z-index:90; }
@media (max-width:768px) {
  .topbar-toggle { display:inline-flex; align-items:center; justify-content:center; }
  .sidebar { position:fixed; top:0; left:0; height:100dvh; width:min(260px,85vw); transform:translateX(-100%); transition:transform .25s ease; box-shadow:0 0 50px rgba(0,0,0,.6); }
  .sidebar.open { transform:translateX(0); }
  .sidebar-backdrop.show { display:block; }
  .sidebar-resize-handle { display:none; } /* fixed-width overlay drawer here, resizing doesn't apply */
  .content { padding:16px; }
  .conn span.txt { display:none; }
  /* Touch target sizing — bumped up from the mouse-cursor-sized defaults, gated to
     mobile only so desktop density stays as-is. */
  .btn { min-height:44px; }
  .chip { min-height:40px; }
  .topbar-text-btn { min-height:40px; }
  .context-menu button { padding:12px 10px; }
}

/* Phone-in-portrait — narrower than the drawer-nav breakpoint above; console input
   row and modal padding get cramped well before 768px does. */
@media (max-width:480px) {
  .console-in { flex-wrap:wrap; }
  .console-in #cShell { flex:1 1 100%; }
  .console-in #cInput { flex:1 1 auto; }
  .modal-bg { padding:12px; }
  .modal { padding:18px; }
}
