:root {
  --bg: #f6f7fb;
  --sidebar-bg: #ffffff;
  --panel: #ffffff;
  --panel-2: #f2f3f8;
  --panel-float: rgba(255, 255, 255, 0.4);
  --border: #e5e7eb;
  --text: #111827;
  --text-dim: #6b7280;
  --text-faint: #9aa1b1;
  --accent: #6366f1;
  --accent-hover: #4f46e5;
  --accent-soft: #eef0fe;
  --danger: #ef4444;
  --danger-soft: #fee2e2;
  --danger-border: #fca5a5;
  --danger-text: #b91c1c;
  --radius: 10px;
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 1px 3px rgba(16, 24, 40, 0.06);
}

[data-theme="dark"] {
  --bg: #0f1117;
  --sidebar-bg: #161822;
  --panel: #161822;
  --panel-2: #1f2230;
  --panel-float: rgba(22, 24, 34, 0.4);
  --border: #2b2f3f;
  --text: #e8e9ee;
  --text-dim: #9a9fb0;
  --text-faint: #6b7080;
  --accent: #818cf8;
  --accent-hover: #6366f1;
  --accent-soft: rgba(129, 140, 248, 0.16);
  --danger: #f87171;
  --danger-soft: rgba(248, 113, 113, 0.14);
  --danger-border: rgba(248, 113, 113, 0.4);
  --danger-text: #fca5a5;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 2px 8px rgba(0, 0, 0, 0.3);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

a { color: var(--accent); }

svg { display: block; }

/* ---------- auth pages ---------- */

.auth-container {
  max-width: 360px;
  margin: 90px auto;
  padding: 32px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow);
}

.auth-container h1 {
  margin-top: 0;
  font-size: 20px;
  letter-spacing: 0.3px;
}

.error-msg {
  background: var(--danger-soft);
  border: 1px solid var(--danger-border);
  color: var(--danger-text);
  padding: 10px 14px;
  border-radius: 8px;
  margin-bottom: 16px;
  font-size: 14px;
}

.empty-state { color: var(--text-faint); text-align: center; padding: 48px 0; font-size: 14px; }

/* ---------- form controls ---------- */

input[type="text"], input[type="password"], input[type="file"] {
  width: 100%;
  padding: 10px 12px;
  margin: 6px 0 16px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 14px;
}

input[type="text"]:focus, input[type="password"]:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--panel);
}

label { font-size: 13px; color: var(--text-dim); }

button, .btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: var(--accent);
  color: white;
  border: none;
  border-radius: 8px;
  padding: 9px 16px;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s;
  font-family: inherit;
}

button:hover, .btn:hover { background: var(--accent-hover); }

button.secondary, .btn.secondary {
  background: var(--panel);
  color: var(--text);
  border: 1px solid var(--border);
}
button.secondary:hover, .btn.secondary:hover { background: var(--panel-2); }

button.danger { background: var(--danger); }
button.danger:hover { background: #dc2626; }

button:disabled, .btn.disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

/* ---------- app shell ---------- */

.app-shell { display: flex; min-height: 100vh; }

/* compact icon-rail sidebar — big icon on top, label below, instead of the old icon-left/
   text-right row layout, so it takes noticeably less horizontal width away from page content
   (the log viewer's chart especially) while every destination is still one glance to identify */
.sidebar {
  width: 76px;
  flex-shrink: 0;
  background: var(--sidebar-bg);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 14px 6px;
}

.sidebar-brand {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 0 16px;
  border-radius: 9px;
  background: var(--accent);
  color: #fff;
  font-weight: 800;
  font-size: 13px;
  letter-spacing: 0.2px;
  text-decoration: none;
  flex-shrink: 0;
}

.sidebar-nav { display: flex; flex-direction: column; align-items: center; gap: 4px; width: 100%; }

.sidebar-nav a, .nav-stub {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 8px 2px 7px;
  border-radius: 10px;
  color: var(--text-dim);
  text-decoration: none;
  font-size: 10px;
  font-weight: 500;
  text-align: center;
}

.sidebar-nav a.active { background: var(--accent-soft); color: var(--accent); }
.sidebar-nav a:hover:not(.active) { background: var(--panel-2); color: var(--text); }
.nav-stub { cursor: default; opacity: 0.5; }

.nav-icon { width: 21px; height: 21px; flex-shrink: 0; }
.nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }

.nav-badge {
  font-size: 8px;
  padding: 1px 5px;
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--text-faint);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.2px;
}

.sidebar-footer { margin-top: auto; padding-top: 12px; border-top: 1px solid var(--border); width: 100%; }

.sidebar-user { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px 2px; text-align: center; }

.avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--accent);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 13px;
  flex-shrink: 0;
}

.sidebar-user .info { min-width: 0; width: 100%; }
.sidebar-user .username {
  font-size: 11px;
  font-weight: 600;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-user-links { display: flex; flex-direction: column; align-items: center; gap: 2px; margin-top: 2px; }

.linklike {
  background: none;
  border: none;
  padding: 0;
  color: var(--text-faint);
  font-size: 10.5px;
  cursor: pointer;
  font-family: inherit;
}
.linklike:hover { color: var(--danger); background: none; }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; position: relative; }

.main-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 28px;
  border-bottom: 1px solid var(--border);
  gap: 16px;
  background: var(--panel);
}

.main-header h1 { font-size: 18px; margin: 0; }

.search-input-wrap { position: relative; flex: 1; max-width: 420px; }
.search-input {
  width: 100%;
  padding: 9px 14px 9px 36px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  font-size: 14px;
  color: var(--text);
}
.search-input:focus { outline: none; border-color: var(--accent); background: var(--panel); }
.search-icon {
  position: absolute;
  left: 11px;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  color: var(--text-faint);
}

.main-body { padding: 24px 28px; flex: 1; }

/* ---------- log table ---------- */

.log-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  box-shadow: var(--shadow);
}

.log-table thead th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-faint);
  font-weight: 700;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
}

.log-table tbody tr { border-bottom: 1px solid var(--border); transition: opacity 0.1s; }
.log-table tbody tr:last-child { border-bottom: none; }
.log-table tbody tr:hover { background: var(--panel-2); }
.log-table tbody tr.row-dimmed { opacity: 0.3; }
.log-table td { padding: 13px 16px; font-size: 14px; vertical-align: middle; }

.log-title { font-weight: 600; color: var(--text); text-decoration: none; }
.log-title:hover { color: var(--accent); }
.log-sub { font-size: 12px; color: var(--text-faint); margin-top: 2px; }
.col-added, .col-params { color: var(--text-dim); white-space: nowrap; }
.col-actions { text-align: right; white-space: nowrap; }

.row-actions { display: inline-flex; gap: 4px; opacity: 0; transition: opacity 0.12s; }
.log-table tr:hover .row-actions { opacity: 1; }

.icon-btn {
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 7px;
  border: 1px solid transparent;
  background: none;
  color: var(--text-faint);
  cursor: pointer;
  padding: 0;
}
.icon-btn:hover { background: var(--panel-2); color: var(--text); }
.icon-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }

/* ---------- upload modal ---------- */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(17, 24, 39, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s;
}
.modal-overlay.open { opacity: 1; pointer-events: auto; }

.modal {
  background: var(--panel);
  border-radius: 14px;
  width: 480px;
  max-width: 90vw;
  box-shadow: 0 20px 60px rgba(17, 24, 39, 0.25);
  padding: 20px;
}

.modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
.modal-header h2 { font-size: 16px; margin: 0; display: flex; align-items: center; gap: 8px; }

.modal-close {
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-faint);
  width: 28px;
  height: 28px;
  border-radius: 7px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}
.modal-close:hover { background: var(--panel-2); }

.dropzone {
  border: 2px dashed var(--border);
  border-radius: 12px;
  padding: 40px 20px;
  text-align: center;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.dropzone:hover, .dropzone.dragover { border-color: var(--accent); background: var(--accent-soft); }
.dropzone svg { width: 30px; height: 30px; color: var(--text-faint); margin: 0 auto 10px; }
.dropzone.has-file svg { color: var(--accent); }
.dropzone .dz-title { font-weight: 600; font-size: 14px; color: var(--text); }
.dropzone.has-file .dz-title { color: var(--accent); }
.dropzone .dz-sub { font-size: 12px; color: var(--text-faint); margin-top: 4px; }

.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }
.modal input[type="text"] { margin: 6px 0 0; }
.modal label { display: block; margin-top: 14px; }

.formula-hint { font-size: 12px; color: var(--text-faint); margin-top: 8px; line-height: 1.5; }
.formula-hint code {
  background: var(--panel-2);
  border-radius: 4px;
  padding: 1px 5px;
  font-size: 11.5px;
  color: var(--text-dim);
}
.formula-channel-list {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 5px;
  margin-top: 10px;
  max-height: 280px;
  overflow-y: auto;
}
.formula-channel-chip {
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel-2);
  color: var(--text-dim);
  font-size: 11px;
  cursor: pointer;
  user-select: none;
  transition: opacity 0.1s;
}
.formula-channel-chip:hover { border-color: var(--accent); color: var(--accent-hover); }
/* search dims non-matches instead of removing them — same pattern as the main channel list */
.formula-channel-chip.dimmed { opacity: 0.3; }

.formula-color-input {
  margin-top: 6px;
  width: 44px;
  height: 30px;
  padding: 2px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  cursor: pointer;
}

/* outline-styled danger action (delete) inside a modal — distinct from the solid .danger
   button used for one-shot destructive confirms elsewhere, since this sits alongside plain
   .secondary buttons and shouldn't visually shout louder than the primary submit action */
button.danger-outline, .btn.danger-outline {
  background: var(--panel);
  color: var(--danger-text);
  border: 1px solid var(--danger-border);
}
button.danger-outline:hover, .btn.danger-outline:hover { background: var(--danger-soft); }

/* ---------- log viewer ---------- */

.viewer { display: flex; flex-direction: column; height: 100vh; }

.viewer-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--panel);
  gap: 16px;
  flex-shrink: 0;
}

.breadcrumb { font-size: 14px; display: flex; align-items: center; gap: 6px; min-width: 0; }
.breadcrumb a { color: var(--text-dim); text-decoration: none; }
.breadcrumb a:hover { color: var(--accent); }
.breadcrumb .sep { color: var(--text-faint); }
.breadcrumb .current {
  color: var(--text);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }

.tabs-segment { display: flex; background: var(--panel-2); border-radius: 9px; padding: 3px; gap: 2px; }
.tabs-segment button {
  background: none;
  color: var(--text-dim);
  border: none;
  padding: 7px 14px;
  font-size: 13px;
  font-weight: 600;
  border-radius: 7px;
  box-shadow: none;
}
.tabs-segment button.active { background: var(--panel); color: var(--accent); box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08); }
.tabs-segment button:disabled { opacity: 0.4; cursor: not-allowed; }
.tabs-segment button:hover:not(.active):not(:disabled) { color: var(--text); background: none; }

.viewer-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 24px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  flex-shrink: 0;
}

/* Comparison has its own secondary toolbar: the primary graph controls stay stable while the
   overlay's offset and remove actions appear immediately underneath them. */
.compare-toolbar {
  display: flex;
  align-items: center;
  gap: 9px;
  min-height: 42px;
  padding: 7px 24px;
  border-bottom: 1px solid var(--border);
  color: var(--text-dim);
  font-size: 13px;
}
.compare-toolbar-log { display: inline-flex; align-items: center; gap: 6px; margin-right: 8px; color: var(--text); font-weight: 600; }
.compare-toolbar-line { width: 16px; border-top: 2px dashed var(--text-dim); }
.compare-toolbar input[type="range"] { width: 140px; }
.compare-toolbar input[type="number"] { width: 64px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 7px; font-size: 13px; background: var(--panel); color: var(--text); }
.compare-toolbar #compareOffsetResetBtn { padding: 5px 10px; font-size: 12px; }

.ai-review-popover {
  position: fixed;
  z-index: 220;
  width: min(390px, calc(100vw - 24px));
  max-height: min(70vh, 650px);
  overflow-y: auto;
  padding: 14px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: var(--shadow), 0 14px 36px rgba(16, 24, 40, 0.16);
}
.ai-review-head { display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px; color:var(--text); }
.ai-review-head .icon-btn { width:26px; height:26px; padding:0; font-size:20px; line-height:1; }
.ai-review-content { display:flex; flex-direction:column; gap:10px; }
.ai-review-status { color:var(--text); font-size:13px; font-weight:600; }
.ai-review-muted { color:var(--text-faint); font-size:12.5px; line-height:1.45; }
.ai-review-error { color:var(--danger); }
.ai-review-summary { color:var(--text); font-size:13px; line-height:1.5; }
.ai-review-finding { border-left:3px solid var(--accent); border-radius:6px; background:var(--panel-2); padding:9px 10px; color:var(--text-dim); font-size:12.5px; line-height:1.45; }
.ai-review-finding.warning { border-left-color:#f59e0b; }
.ai-review-finding.critical { border-left-color:var(--danger); }
.ai-review-finding strong { display:block; color:var(--text); font-size:13px; margin-bottom:3px; }
.ai-review-range { display:block; color:var(--text-faint); font-size:11.5px; margin-bottom:5px; }
.ai-review-action { align-self:flex-start; margin-top:2px; }

.axis-select { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-dim); }
.axis-select select {
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 6px 10px;
  font-size: 13px;
  background: var(--panel);
  color: var(--text);
  font-family: inherit;
}

.checkbox-label { display: flex; align-items: center; gap: 6px; cursor: pointer; user-select: none; }
.checkbox-label input[type="checkbox"] { accent-color: var(--accent); cursor: pointer; }

/* no vertical scroll in Single mode on purpose — mouse wheel over the chart zooms the Y axis
   instead (see attachWheelZoom in log.js/compare.js), so a scrollbar competing for the same
   gesture would be actively confusing. Split mode has no such wheel-zoom (each per-channel box
   is a separate small chart) and stacks an arbitrary number of boxes taller than the viewport,
   so it needs the opposite — .split-scroll (toggled by mode in log.js) re-enables it there. */
.chart-scroll {
  flex: 1;
  overflow: hidden;
  padding: 20px 24px;
  min-height: 0;
  /* Set by centerYStretchControls(): the chart deliberately claims part of the former
     control gutter, while the Y control stays centered in the smaller remaining gap. */
  margin-left: calc(-1 * var(--y-gutter-reduction, 0px));
}
.chart-scroll.split-scroll { overflow-y: auto; overflow-x: hidden; }

/* per-axis stretch sliders (see attachYStretch/attachXStretch in log.js) — chart-frame holds
   the vertical Y slider to the left of the chart itself, chart-stretch-x is the horizontal X
   slider below the whole frame. Hidden outside Single mode via inline display, same pattern as
   axisSelectWrap/smartScaleWrap (Split's multiple boxes don't have one shared Y to stretch). */
.chart-frame { display: flex; flex: 1; min-height: 0; }
.chart-frame .chart-scroll { padding-left: 8px; }
.chart-stretch-y {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 28px;
  flex-shrink: 0;
  padding: 24px 0 32px;
  /* margin, not padding — box-sizing:border-box means padding would eat into the fixed 28px
     width and squeeze the slider/button instead of shifting the whole column. Matches
     .chart-scroll's own right padding (24px) so the reset button sits as far from the sidebar
     as the plotted area sits from the window edge, instead of flush against it. */
  margin-left: 24px;
  position: relative;
  transform: translateX(var(--y-control-offset, 0px));
}
/* a plain horizontal range input, rotated -90deg, rather than writing-mode:vertical-lr —
   vertical-oriented range inputs via writing-mode render inconsistently across engines (the
   track/thumb pseudo-elements don't reliably pick up the same thin sizing that works for the
   normal horizontal orientation), so this instead reuses the *exact* horizontal rendering the
   user already confirmed looks right, just turned on its side. Its "width" becomes the visual
   vertical length after rotation, so it's sized in JS (see sizeYStretchSlider in log.js) to
   match the wrap's actual available height rather than a fixed guess. Absolutely positioned so
   its pre-rotation (wide, short) layout box can't push the 28px-wide column wider than it.
*/
.stretch-slider-y {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%) rotate(-90deg);
}
.chart-stretch-x {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 2px 24px 12px 76px; /* left padding lines the slider up under the chart, past the sidebar gap + Y stretch column + axis gutter */
  position: relative;
}
/* Docked panel: center the track in the strip between the chart card and the panel. */
.chart-stretch-x:not(.clear-floating-panel) { transform: translateY(-4px); }
/* In floating mode the channel card protrudes into the bottom edge of the viewer. Move the
   slider to a real clear strip above that card — a z-index alone still leaves it visually
   bisected by the card's top border. */
.chart-stretch-x.clear-floating-panel {
  transform: translateY(var(--x-control-offset, -18px));
  /* The expanded floating channel panel must cover this secondary control. */
  z-index: 39;
}
.stretch-slider-x { flex: 1; }

/* deliberately muted/thin — this is a secondary, occasional-use control that shouldn't compete
   for attention with the chart itself, unlike the accent-colored primary toolbar controls */
.stretch-slider {
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  cursor: pointer;
}
.stretch-slider::-webkit-slider-runnable-track { height: 3px; background: var(--border); border-radius: 2px; }
.stretch-slider::-moz-range-track { height: 3px; background: var(--border); border-radius: 2px; }
.stretch-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 9px;
  height: 9px;
  margin-top: -3px;
  border-radius: 50%;
  background: var(--text-faint);
  border: none;
}
.stretch-slider::-moz-range-thumb { width: 9px; height: 9px; border-radius: 50%; background: var(--text-faint); border: none; }
.stretch-slider:hover::-webkit-slider-thumb, .stretch-slider:focus::-webkit-slider-thumb { background: var(--text-dim); }
.stretch-slider:hover::-moz-range-thumb, .stretch-slider:focus::-moz-range-thumb { background: var(--text-dim); }
.stretch-reset {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--panel);
  color: var(--text-dim);
  box-shadow: none;
}
.stretch-reset:hover { color: var(--accent-hover); border-color: var(--accent); }

/* click-and-drag panning (see attachDragPan) — grab cursor invites the drag, grabbing cursor
   confirms it's active; applied to uPlot's .u-over overlay, which is what receives the drag */
.u-over { cursor: grab; }
.u-over.tunelog-grabbing { cursor: grabbing; }
.u-over.tunelog-select-mode { cursor: crosshair; }

/* rectangle drawn while dragging in "Выделение" cursor mode (see attachRegionSelect) */
.region-select-box {
  position: absolute;
  z-index: 50;
  border: 1px solid var(--accent);
  /* The temporary drag band must not veil the chart itself (especially in the light theme,
     where --accent-soft is nearly white). The graph stays readable until mouseup, matching
     the persistent selected band shown with the context menu. */
  background: rgba(99, 102, 241, 0.04);
  pointer-events: none;
}

/* Once the mouse is released, keep the chosen time slice visible until its context menu is
   dismissed. The two masks darken only the surrounding data, so the selection reads as a
   focused window instead of a second opaque rectangle over the graph. */
.region-selection-overlay {
  position: absolute;
  inset: 0;
  z-index: 50;
  pointer-events: none;
}
.region-selection-mask {
  position: absolute;
  top: 0;
  bottom: 0;
  background: var(--panel);
  opacity: 0.78;
}
.region-selection-band {
  position: absolute;
  top: 0;
  bottom: 0;
  border-left: 1px solid var(--accent);
  border-right: 1px solid var(--accent);
  background: rgba(99, 102, 241, 0.04);
}

/* context menu shown after releasing a region selection — same popup pattern as .pill-scale-menu,
   but a vertical list of full-width text actions instead of a row of short scale options */
.chart-context-menu {
  position: fixed;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(17, 24, 39, 0.18);
  padding: 5px;
  min-width: 220px;
}
.chart-context-menu-item {
  text-align: left;
  padding: 7px 10px;
  border-radius: 6px;
  border: none;
  background: none;
  color: var(--text);
  font-size: 12.5px;
  font-weight: 500;
  box-shadow: none;
}
.chart-context-menu-item:hover { background: var(--panel-2); color: var(--accent-hover); }

.compare-popover {
  position: fixed;
  z-index: 210;
  width: min(320px, calc(100vw - 24px));
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 28px rgba(17, 24, 39, 0.22);
}
.compare-popover[hidden] { display: none; }
.compare-popover input {
  margin: 0 0 7px;
  padding: 8px 10px;
  font-size: 13px;
}
.compare-popover-list { max-height: 260px; overflow-y: auto; }
.compare-popover-log {
  display: block;
  width: 100%;
  padding: 8px 10px;
  overflow: hidden;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font-size: 13px;
  font-weight: 500;
  text-align: left;
  text-overflow: ellipsis;
  white-space: nowrap;
  box-shadow: none;
}
.compare-popover-log:hover, .compare-popover-log:focus { background: var(--panel-2); color: var(--accent-hover); }
.compare-popover-status { padding: 7px 10px; color: var(--text-faint); font-size: 12px; }

/* While a region action is being chosen, the graph and its zoom controls cannot receive a
   second interaction. The menu lives directly under body, so it remains clickable. */
body.region-menu-open .viewer { pointer-events: none; }

/* transient bottom-right confirmation (see showToast in log.js) — e.g. "участок сохранён" with
   an "Открыть" action, so a background save doesn't need a blocking modal to confirm it worked */
.tunelog-toast {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 300;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: 380px;
  padding: 12px 12px 12px 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(15, 18, 28, 0.3);
  animation: tunelog-toast-in 0.18s ease;
}
@keyframes tunelog-toast-in {
  from { transform: translateY(8px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}
.tunelog-toast-text { font-size: 13px; color: var(--text); line-height: 1.4; }
.tunelog-toast-action {
  flex-shrink: 0;
  padding: 6px 12px;
  font-size: 12.5px;
  font-weight: 600;
  border-radius: 7px;
}
.tunelog-toast-close {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text-faint);
  box-shadow: none;
}
.tunelog-toast-close:hover { background: var(--panel-2); color: var(--text-dim); }

.chart-box {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 8px 16px 14px;
  margin-bottom: 14px;
  box-shadow: var(--shadow);
}
.chart-box .chart-title { font-size: 12.5px; font-weight: 600; color: var(--text-dim); margin: 6px 0 2px; }

.channel-bar { border-top: 1px solid var(--border); background: var(--panel); padding: 10px 24px; flex-shrink: 0; max-height: 45vh; overflow-y: auto; }

.channel-bar-head { display: flex; justify-content: center; margin: -4px 0 2px; }
.channel-bar-collapse-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 16px;
  padding: 0;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text-faint);
  box-shadow: none;
}
.channel-bar-collapse-btn:hover { color: var(--text-dim); background: var(--panel-2); }
.channel-bar-collapse-btn svg { transition: transform 0.18s ease; }
/* pinned (docked) = upright pin; collapsed/floating = the same pin tilted loose, echoing "it's
   not holding the panel down anymore" rather than an arbitrary arrow flip */
.channel-bar-collapse-btn.flipped svg { transform: rotate(-35deg); }

/* collapsed: the panel leaves normal flow entirely (see setPanelCollapsed in log.js toggling
   this class) so chart-frame/chart-stretch-x — its flex siblings in .viewer — expand into the
   freed space, same as if it were removed from the page. It still exists positioned off the
   bottom edge of .main (which needs position:relative for this to anchor correctly rather than
   to the viewport, which would ignore the sidebar's width). Styled as a distinct floating card
   — inset on all four sides (not flush against the window edge, so its own rounded bottom
   border is actually visible instead of getting cut off by the viewport), translucent with a
   background blur — reinforcing that it's no longer part of the page flow, it's overlaid on
   top of whatever's under it, not a docked layout piece. */
.channel-bar.floating {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 16px;
  z-index: 40;
  /* resting position leaves a small sliver poking up above the bottom edge — a constant, wordless
     hint that hovering there reveals more, instead of a fully-hidden panel with no visual trace.
     Percentage transforms are relative to the element's own box, not the viewport, so this still
     works the same regardless of the bottom:16px inset above. */
  transform: translateY(calc(100% - 12px));
  transition: transform 0.16s ease;
  background: var(--panel-float);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  border: 1px solid var(--border);
  border-radius: 14px;
  box-shadow: 0 12px 32px rgba(15, 18, 28, 0.4);
}
.channel-bar.floating.peek { transform: translateY(0); }

/* the resting sliver (see transform above) already catches hover on its own — this strip just
   widens that hit target below/around it (covering the 16px gap below the sliver too) so the
   cursor doesn't have to land exactly on a 12px-tall strip that isn't even flush with the edge */
.channel-bar-hover-zone { display: none; }
.channel-bar-hover-zone.active {
  display: block;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 40px;
  z-index: 39;
}
.channel-search {
  width: 100%;
  max-width: 280px;
  margin-bottom: 8px;
  padding: 5px 10px;
  border-radius: 6px;
  border: 1px solid var(--border);
  font-size: 12px;
  background: var(--panel-2);
}
.channel-search:focus { outline: none; border-color: var(--accent); background: var(--panel); }

/* max-height is set dynamically by JS (adjustPillsHeight) based on channel count and viewport size */
.channel-pills { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 6px; overflow-y: auto; transition: max-height 0.15s ease; }

.channel-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px 4px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel);
  font-size: 11.5px;
  line-height: 1.4;
  font-weight: 500;
  color: var(--text-dim);
  cursor: pointer;
  user-select: none;
  transition: border-color 0.1s, color 0.1s, background 0.1s, opacity 0.1s;
}
/* search dims non-matches instead of removing them — keeps the full list in place so
   nothing jumps around, and staying results/counts stay legible at a glance */
.channel-pill.dimmed { opacity: 0.3; }
.channel-pill .dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
/* a soft tinted fill instead of a solid block of color — reads clearly as "selected" without
   being the loudest thing in the row; the dot alone still carries the channel's own color */
.channel-pill.active { background: var(--accent-soft); color: var(--accent-hover); border-color: var(--accent); }
.channel-pill:hover:not(.active) { border-color: var(--text-faint); color: var(--text); }
/* computed (formula) channels get a dashed border plus a leading ƒ mark so they read as
   "synthetic" at a glance, distinct from a channel that came straight off the ECU */
.channel-pill.computed { border-style: dashed; }
.channel-pill.computed::before {
  content: 'ƒ';
  font-style: italic;
  font-weight: 700;
  opacity: 0.65;
  margin-right: -2px;
}
.channel-pill .pill-scale-badge {
  font-size: 10px;
  font-weight: 700;
  opacity: 0.7;
  letter-spacing: -0.2px;
}
/* pencil icon, hover-revealed on computed pills only — opens the formula edit modal, which
   has its own delete action, so a plain × isn't needed on the pill itself anymore */
.channel-pill .pill-edit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.1s;
  margin-left: -2px;
}
.channel-pill .pill-edit svg { width: 11px; height: 11px; }
.channel-pill:hover .pill-edit { opacity: 1; }
.channel-pill .pill-edit:hover { background: rgba(0, 0, 0, 0.12); }
.channel-pill.active .pill-edit:hover { background: rgba(0, 0, 0, 0.1); }

.pill-scale-menu {
  position: fixed;
  z-index: 200;
  display: flex;
  gap: 4px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(17, 24, 39, 0.18);
  padding: 5px;
}
.pill-scale-option {
  padding: 5px 9px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
}
.pill-scale-option:hover { border-color: var(--accent); color: var(--accent-hover); }
.pill-scale-option.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-hover); }

.create-channel-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 12px;
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px dashed var(--accent);
  background: var(--accent-soft);
  color: var(--accent-hover);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.create-channel-btn:hover { background: var(--accent); border-style: solid; color: #fff; }

/* floating cursor tooltip — appended into uPlot's .u-over (already position:absolute), so top:0/left:0 + transform lines up with u.cursor.left/top exactly */
.chart-tooltip {
  position: absolute;
  top: 0;
  left: 0;
  display: none;
  pointer-events: none;
  background: rgba(17, 24, 39, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.08);
  color: #fff;
  font-size: 12px;
  padding: 8px 10px;
  border-radius: 8px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
  z-index: 20;
  white-space: nowrap;
  line-height: 1.6;
}
.chart-tooltip .tt-time { font-weight: 600; font-size: 11px; color: #9aa3b5; margin-bottom: 3px; }
.chart-tooltip .tt-row { display: flex; align-items: center; gap: 7px; }
.chart-tooltip .tt-row .tt-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.chart-tooltip .tt-row b { margin-left: 12px; font-weight: 600; }

/* channel presets */
.preset-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.preset-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.preset-bar-actions { display: flex; gap: 6px; margin-left: auto; }
.preset-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 6px 3px 10px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
}
.preset-chip:hover { border-color: var(--accent); color: var(--accent-hover); }
/* only "active" when the current channel selection exactly matches this preset's saved set — otherwise it'd claim to be selected while the chart no longer reflects it */
.preset-chip.active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-hover); }
.preset-chip .preset-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  font-size: 13px;
  line-height: 1;
  opacity: 0;
  transition: opacity 0.1s;
}
.preset-chip:hover .preset-remove { opacity: 1; }
.preset-chip .preset-remove:hover { background: rgba(79, 70, 229, 0.18); }
.preset-save-btn {
  padding: 4px 10px;
  font-size: 11.5px;
  border-radius: 999px;
}

.preset-input-popup {
  position: fixed;
  z-index: 200;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 8px 24px rgba(17, 24, 39, 0.18);
  padding: 6px;
}
.preset-name-input {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 12.5px;
  width: 200px;
  outline: none;
  color: var(--text);
}
.preset-name-input:focus { border-color: var(--accent); }

/* uPlot legend: default is bold/large — tone it down so it reads as a legend, not a second heading */
.u-legend {
  font-size: 12px;
  margin-top: 4px;
  padding-top: 8px;
  border-top: 1px solid var(--border);
}
.u-legend .u-label { font-weight: 500; font-size: 12px; color: var(--text-dim); }
.u-legend .u-value { font-weight: 600; font-size: 12px; color: var(--text); }
.u-legend .u-marker { border-radius: 3px; }
.u-legend th, .u-legend td { padding: 3px 6px; }
