/* === Light Theme === */
:root {
    --bg: #f5f5f7;
    --card: #ffffff;
    --surface: #f3f3f6;
    --text: #1d1d1f;
    --text2: #5b5b63;
    --accent: #0071e3;
    --accent-hover: #0077ed;
    --accent-light: rgba(0, 113, 227, 0.10);
    --success: #34c759;
    --warning: #ff9f0a;
    --danger: #ff3b30;
    --border: rgba(0, 0, 0, 0.06);
    --border-strong: rgba(0, 0, 0, 0.15);
    --shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
    --shadow-lg: 0 8px 30px rgba(0, 0, 0, 0.12);
    --radius: 16px;
    --hover-bg: rgba(0, 0, 0, 0.04);
    --input-bg: var(--card);
    /* Caret drawn into every <select> (their native arrow is stripped with appearance:none). */
    --select-caret: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236e6e73' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
}

/* === Dark Theme === */
/* Lifted off pure black with a hint of cool grey, brighter borders/surfaces and softer,
   wider shadows so cards read as gently elevated rather than floating on a void. */
[data-theme="dark"] {
    --bg: #131316;
    --card: #1e1e22;
    --surface: #26262b;
    --text: #f2f2f5;
    --text2: #a7a7b0;
    --accent: #2f93ff;
    --accent-hover: #57a8ff;
    --accent-light: rgba(47, 147, 255, 0.18);
    --success: #34d164;
    --warning: #ffab2e;
    --danger: #ff5a4d;
    --border: rgba(255, 255, 255, 0.11);
    --border-strong: rgba(255, 255, 255, 0.22);
    --shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.55);
    --radius: 16px;
    --hover-bg: rgba(255, 255, 255, 0.07);
    --input-bg: rgba(255, 255, 255, 0.07);
    --select-caret: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a7a7b0' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
}

/* === Smooth Theme Transition === */
html.theme-transition,
html.theme-transition *,
html.theme-transition *::before,
html.theme-transition *::after {
    transition: background-color 0.35s ease, border-color 0.35s ease, color 0.35s ease, box-shadow 0.35s ease !important;
}

/* === Base === */
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
    --font-sans: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif;
    --font-mono: 'JetBrains Mono', 'SF Mono', 'Roboto Mono', 'Menlo', 'Consolas', monospace;
    /* Physical insets of the device (notch, home indicator); 0 everywhere else. */
    --safe-t: env(safe-area-inset-top, 0px);
    --safe-b: env(safe-area-inset-bottom, 0px);
    --safe-l: env(safe-area-inset-left, 0px);
    --safe-r: env(safe-area-inset-right, 0px);
}
html {
    /* Phones must not re-scale our type when rotated to landscape. */
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
}
body {
    font-family: var(--font-sans);
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    letter-spacing: -0.011em;
    font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
    overscroll-behavior-y: none;
}
h1, h2, h3, .card h2 { letter-spacing: -0.02em; }

/* Selection reads as a deliberate accent wash in both themes instead of the OS blue block. */
::selection { background: color-mix(in srgb, var(--accent) 26%, transparent); color: var(--text); }
::-moz-selection { background: color-mix(in srgb, var(--accent) 26%, transparent); color: var(--text); }

/* Grey flash on tap is replaced by our own :active states below. */
a, button, select, input, textarea, label, .cs-option, .ct-head, tr {
    -webkit-tap-highlight-color: transparent;
}
button, .btn, nav button, .subtab, .seg-picker label, .lang-btn, .theme-toggle {
    touch-action: manipulation; /* kills the 300ms double-tap-zoom delay */
}
/* Chrome, labels and controls are not text; values are, and stay copyable. */
nav, .subtabs, .seg-picker, .lang-switch, .toggle, .btn, .status,
th, td::before, .mon-stat-label, .form-group-title, .img-side-title { user-select: none; }
code, .probe-kv b, .mon-stat-value, .q-msg-body, .task-detail-row span:last-child {
    user-select: text; -webkit-user-select: text;
}

/* === Login === */
#login-overlay {
    position: fixed;
    inset: 0;
    background: radial-gradient(ellipse at 50% 0%, rgba(0, 113, 227, 0.06) 0%, var(--bg) 60%);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}
[data-theme="dark"] #login-overlay {
    background: radial-gradient(ellipse at 50% 0%, rgba(10, 132, 255, 0.06) 0%, var(--bg) 60%);
}
#login-overlay.hidden { display: none; }
.login-card {
    background: var(--card);
    border-radius: 24px;
    padding: 48px 40px;
    width: 380px;
    text-align: center;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08), 0 0 0 1px var(--border);
    animation: fadeUp 0.4s ease;
}
[data-theme="dark"] .login-card {
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4), 0 0 0 1px var(--border);
}
.login-icon { margin-bottom: 20px; }
.login-card h1 { font-size: 28px; font-weight: 700; margin-bottom: 6px; }
.login-card p { color: var(--text2); font-size: 15px; margin-bottom: 28px; }
.login-card input[type="text"],
.login-card input[type="password"] {
    width: 100%;
    padding: 14px 16px;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    font-size: 16px;
    outline: none;
    transition: all 0.2s ease;
    font-family: inherit;
    background: var(--input-bg);
    color: var(--text);
}
.login-card input[type="text"]:focus,
.login-card input[type="password"]:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 4px var(--accent-light);
}
.login-card input[type="text"] { margin-bottom: 10px; }
.login-card button[type="submit"] {
    width: 100%;
    padding: 14px;
    background: var(--accent);
    color: var(--on-accent);
    border: none;
    border-radius: 12px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    margin-top: 16px;
    font-family: inherit;
}
.login-card button[type="submit"]:hover { background: var(--accent-hover); }
.login-card button[type="submit"]:active { transform: scale(0.98); }
#login-error { color: var(--danger); font-size: 13px; margin-top: 14px; min-height: 20px; }

/* === Global Controls === */
#global-controls {
    position: fixed;
    top: 12px;
    right: 16px;
    z-index: 1100;
    display: flex;
    gap: 8px;
    align-items: center;
}
.lang-switch {
    display: flex;
    background: var(--hover-bg);
    border-radius: 9px;
    padding: 3px;
    gap: 2px;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
.lang-btn {
    padding: 5px 11px;
    border: none;
    background: transparent;
    border-radius: 7px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text2);
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
    letter-spacing: 0.3px;
}
.lang-btn.active {
    background: var(--card);
    color: var(--text);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
[data-theme="dark"] .lang-btn.active {
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.theme-toggle {
    width: 34px;
    height: 34px;
    border: none;
    background: var(--hover-bg);
    border-radius: 9px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    color: var(--text2);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
.theme-toggle:hover { color: var(--text); background: rgba(0, 0, 0, 0.08); }
[data-theme="dark"] .theme-toggle:hover { background: rgba(255, 255, 255, 0.1); }
.theme-toggle .icon-sun { display: none; }
.theme-toggle .icon-moon { display: block; }
[data-theme="dark"] .theme-toggle .icon-sun { display: block; }
[data-theme="dark"] .theme-toggle .icon-moon { display: none; }
#logout-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 6px 14px;
    background: transparent;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text2);
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
#logout-btn:hover { background: var(--hover-bg); color: var(--text); }
/* The icon only earns its place once the label is dropped on small screens. */
#logout-btn .logout-icon { display: none; }

/* Compact font switcher (next to lang/theme) */
select.font-select {
    height: 34px;
    min-width: auto;
    width: auto;
    padding: 0 26px 0 10px;
    border: none;
    border-radius: 9px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.2px;
    background-color: var(--hover-bg);
    background-image: var(--select-caret);
    background-repeat: no-repeat;
    background-position: right 8px center;
    background-size: 13px 13px;
    color: var(--text2);
    cursor: pointer;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    transition: color 0.2s ease, background 0.2s ease;
}
select.font-select:hover { color: var(--text); background-color: var(--hover-bg); border: none; }
select.font-select:focus { outline: none; box-shadow: none; border: none; }

/* === Header === */

/* Только шапка страницы: селектор `header` без уточнения ловил и <header>
   внутри карточек — шапку строки текстов бота и шапку вида заявки, — и вешал на
   них заливку, sticky и высоту 52px. */
#app > header {
    background: rgba(255, 255, 255, 0.72);
    backdrop-filter: saturate(180%) blur(20px);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 100;
    height: 52px;
}
[data-theme="dark"] #app > header {
    background: rgba(28, 28, 30, 0.72);
}
.header-inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: 0 24px;
    height: 100%;
    display: flex;
    align-items: center;
}
#app > header h1 { font-size: 18px; font-weight: 600; }

/* === Layout === */
.container { max-width: 1200px; margin: 0 auto; padding: 24px; }
#app { display: none; }
#app.visible { display: block; }

/* === Navigation (Segmented Control) === */
nav {
    display: inline-flex;
    background: var(--hover-bg);
    border-radius: 10px;
    padding: 3px;
    margin-bottom: 24px;
}
nav button {
    padding: 8px 18px;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    font-size: 14px;
    font-weight: 500;
    color: var(--text2);
    transition: all 0.2s ease;
    font-family: inherit;
    white-space: nowrap;
}
nav button:hover { color: var(--text); }
nav button.active {
    background: var(--card);
    color: var(--text);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
}
[data-theme="dark"] nav button.active {
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25);
}

/* === Cards === */
.card {
    background: var(--card);
    border-radius: var(--radius);
    padding: 28px;
    margin-bottom: 20px;
    box-shadow: var(--shadow);
    border: 1px solid var(--border);
}
.card h2 { font-size: 20px; font-weight: 600; margin-bottom: 20px; }

/* === Tables === */
/* Wide tables scroll inside their card rather than stretching the page. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
table { width: 100%; border-collapse: separate; border-spacing: 0; }
th {
    font-weight: 500;
    color: var(--text2);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    padding: 8px 16px;
    text-align: left;
    border-bottom: 1px solid var(--border);
}
td {
    padding: 12px 16px;
    font-size: 14px;
    border-bottom: 1px solid var(--border);
    font-variant-numeric: tabular-nums;
    transition: background 0.15s ease;
}
tr:last-child td { border-bottom: none; }
tbody tr:hover td { background: var(--hover-bg); }
/* Строка целиком ведёт в карточку: подсветка на наведение уже есть, не хватало
   только курсора — без него строка выглядит как текст, а не как ссылка. */
tr.row-link td { cursor: pointer; }
/* Jobs page: group separators and today's-run highlight */
tr.job-group-row td {
    background: var(--hover-bg);
    color: var(--text2);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 8px 16px;
    border-top: 1px solid var(--border);
}
tr.job-group-toggle td { cursor: pointer; user-select: none; }
tbody tr.job-group-toggle:hover td { background: var(--accent-light); color: var(--text); }
tr.job-group-row .caret {
    display: inline-block;
    width: 14px;
    font-size: 10px;
    opacity: 0.7;
}
tr.job-group-row .count { opacity: 0.7; font-weight: 500; }
tr.job-row-today td {
    background: var(--accent-light);
    border-bottom-color: color-mix(in srgb, var(--accent) 18%, transparent);
}
tr.job-row-today td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
tbody tr.job-row-today:hover td { background: color-mix(in srgb, var(--accent) 16%, var(--card)); }
[data-theme="dark"] tr.job-row-today td { background: color-mix(in srgb, var(--accent) 14%, var(--card)); }
[data-theme="dark"] tbody tr.job-row-today:hover td { background: color-mix(in srgb, var(--accent) 22%, var(--card)); }
td code {
    font-family: var(--font-mono);
    font-size: 13px;
    background: var(--hover-bg);
    padding: 2px 8px;
    border-radius: 5px;
}
/* Action cells keep their buttons on one line on desktop; the phone rules let them wrap. */
td.cell-actions { white-space: nowrap; }

/* === Buttons === */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 9px 20px;
    border: none;
    border-radius: 10px;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    font-family: inherit;
    gap: 6px;
}
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:disabled { background: #a0c4f1; cursor: default; transform: none; }
[data-theme="dark"] .btn-primary:disabled { background: rgba(10, 132, 255, 0.3); color: rgba(255,255,255,0.5); }
.btn-success { background: var(--success); color: #fff; }
.btn-success:hover { opacity: 0.9; }
.btn-secondary { background: var(--hover-bg); color: var(--text); }
.btn-secondary:hover { background: rgba(0, 0, 0, 0.08); }
[data-theme="dark"] .btn-secondary:hover { background: rgba(255, 255, 255, 0.1); }
.btn-danger { background: var(--danger); color: #fff; }
.btn-danger:hover { opacity: 0.9; }
.btn-sm { padding: 5px 12px; font-size: 12px; border-radius: 8px; }

/* === Status Badges === */
.status {
    display: inline-block;
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.3px;
}
.status-CREATED { background: rgba(142, 142, 147, 0.12); color: var(--text2); }
.status-RUNNING { background: rgba(255, 159, 10, 0.14); color: #c67d00; }
.status-COMPLETED { background: rgba(52, 199, 89, 0.14); color: #1b8a33; }
.status-FAILED { background: rgba(255, 59, 48, 0.14); color: #d32f2f; }
.status-CANCELLED { background: rgba(142, 142, 147, 0.12); color: #6e6e73; }
[data-theme="dark"] .status-RUNNING { background: rgba(255, 159, 10, 0.18); color: #ffb340; }
[data-theme="dark"] .status-COMPLETED { background: rgba(48, 209, 88, 0.18); color: #30d158; }
[data-theme="dark"] .status-FAILED { background: rgba(255, 69, 58, 0.18); color: #ff6961; }
[data-theme="dark"] .status-CANCELLED { background: rgba(255, 255, 255, 0.08); color: #636366; }

/* === Job Progress Bar === */
.job-progress { min-width: 180px; }
.job-progress-track {
    background: var(--hover-bg);
    border-radius: 6px;
    height: 8px;
    overflow: hidden;
    position: relative;
}
.job-progress-fill {
    height: 100%;
    border-radius: 6px;
    transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
    width: 0%;
    position: relative;
}
.job-progress-fill.running {
    background: linear-gradient(90deg, #ff9f0a, #ffb340);
}
.job-progress-fill.running::after {
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
    animation: shimmer 1.5s infinite;
}
.job-progress-fill.indeterminate {
    width: 35% !important;
    background: linear-gradient(90deg, #ff9f0a, #ffb340);
    animation: indeterminate 1.3s ease-in-out infinite;
}
.job-progress-fill.indeterminate::after { display: none; }
@keyframes indeterminate {
    0% { margin-left: -35%; }
    100% { margin-left: 100%; }
}
.job-progress-fill.completed { background: linear-gradient(90deg, #34c759, #30d158); }
.job-progress-fill.failed { background: linear-gradient(90deg, #ff3b30, #ff6961); }
.job-progress-fill.cancelled { background: var(--text3); }
.job-progress-info {
    display: flex;
    justify-content: space-between;
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--text2);
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}
.job-progress-pct {
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}
@keyframes shimmer {
    0% { transform: translateX(-100%); }
    100% { transform: translateX(100%); }
}

/* === Forms === */
.form-row {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-bottom: 16px;
    flex-wrap: wrap;
}
/* Native selects render an OS-styled control that ignores the theme (and looks alien in dark
   mode). appearance:none strips it; the caret is drawn as an inline SVG background instead. */
select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 10px 38px 10px 14px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    font-size: 14px;
    font-weight: 500;
    outline: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
    background-color: var(--input-bg);
    background-image: var(--select-caret);
    background-repeat: no-repeat;
    background-position: right 13px center;
    background-size: 15px 15px;
    color: var(--text);
    font-family: inherit;
    min-width: 200px;
    cursor: pointer;
}
select:hover { border-color: var(--accent); }
select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
select:disabled { opacity: 0.55; cursor: default; }
/* The popup itself is OS-drawn; setting option colours is the only cross-browser lever. */
select option { background: var(--card); color: var(--text); }

/* === Custom Select === */
.cs {
    position: relative;
    min-width: 200px;
}
.cs-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 10px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 12px;
    background: var(--input-bg);
    font-size: 14px;
    font-family: inherit;
    color: var(--text);
    cursor: pointer;
    transition: all 0.2s ease;
    gap: 10px;
    min-height: 42px;
}
.cs-trigger:hover { border-color: var(--text2); }
.cs.open .cs-trigger {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
.cs-placeholder { color: var(--text2); }
/*
 * Подпись переносится, а не обрезается. Обрезанное «Заявки: назначать застряв…»
 * не отличить от соседнего пункта, а выбирают тут право, которое потом никто не
 * перечитывает. Ширину это не ломает: рамка растёт вниз, а не вбок.
 */
.cs-label {
    flex: 1;
    min-width: 0;
    text-align: left;
    white-space: normal;
    overflow-wrap: anywhere;
}
.cs-arrow {
    flex-shrink: 0;
    transition: transform 0.25s ease;
    color: var(--text2);
}
.cs.open .cs-arrow { transform: rotate(180deg); }
.cs-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-lg);
    opacity: 0;
    transform: translateY(-8px) scale(0.98);
    pointer-events: none;
    transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
    z-index: 1000;
    overflow: hidden;
}
.cs.open .cs-dropdown {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}
.cs-search {
    padding: 8px 8px 4px;
}
.cs-search input {
    width: 100%;
    padding: 9px 12px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    font-size: 13px;
    font-family: inherit;
    background: var(--bg);
    color: var(--text);
    outline: none;
    transition: border-color 0.2s ease;
}
.cs-search input::placeholder { color: var(--text2); }
.cs-search input:focus { border-color: var(--accent); }
.cs-list {
    max-height: 220px;
    overflow-y: auto;
    padding: 4px;
}
.cs-list::-webkit-scrollbar { width: 6px; }
.cs-list::-webkit-scrollbar-track { background: transparent; }
.cs-list::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.12); border-radius: 3px; }
[data-theme="dark"] .cs-list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.12); }
.cs-option {
    padding: 9px 12px;
    font-size: 14px;
    line-height: 1.35;
    border-radius: 9px;
    cursor: pointer;
    transition: background 0.12s ease;
    white-space: normal;
    overflow-wrap: anywhere;
}
.cs-option:hover { background: var(--hover-bg); }
.cs-option.selected {
    background: var(--accent-light);
    color: var(--accent);
    font-weight: 500;
}
.cs-empty {
    padding: 16px;
    text-align: center;
    color: var(--text2);
    font-size: 13px;
}

/* === Drop Zone === */
.drop-zone {
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius);
    padding: 48px 24px;
    text-align: center;
    transition: all 0.2s ease;
    cursor: pointer;
}
.drop-zone:hover { border-color: var(--text2); }
.drop-zone.dragover {
    border-color: var(--accent);
    background: var(--accent-light);
}
.drop-zone-icon { margin-bottom: 12px; }
.drop-zone-icon svg { color: var(--text2); }
.drop-zone-text { font-size: 15px; font-weight: 500; color: var(--text); margin-bottom: 4px; }
.drop-zone-hint { font-size: 13px; color: var(--text2); }
.file-info {
    margin-top: 16px;
    padding: 12px 16px;
    background: var(--accent-light);
    border-radius: 10px;
    display: none;
    font-size: 14px;
    color: var(--text);
}
.file-info .file-name { font-weight: 500; }
.file-info .file-size { color: var(--text2); margin-left: 8px; }

/* === Progress Bar === */
.progress-container { margin-top: 20px; display: none; }
.progress-track {
    background: var(--hover-bg);
    border-radius: 6px;
    height: 6px;
    overflow: hidden;
}
.progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent), var(--accent-hover));
    border-radius: 6px;
    transition: width 0.3s ease;
    width: 0%;
}
.progress-info {
    display: flex;
    justify-content: space-between;
    margin-top: 8px;
    font-size: 13px;
    color: var(--text2);
}

/* === Messages === */
.msg {
    padding: 14px 18px;
    border-radius: 12px;
    margin-top: 16px;
    font-size: 14px;
    font-weight: 500;
}
.msg-ok { background: rgba(52, 199, 89, 0.12); color: #1b8a33; }
.msg-err { background: rgba(255, 59, 48, 0.12); color: #d32f2f; }
.msg-warn { background: rgba(255, 149, 0, 0.12); color: #b25c00; }
[data-theme="dark"] .msg-ok { background: rgba(48, 209, 88, 0.15); color: #30d158; }
[data-theme="dark"] .msg-err { background: rgba(255, 69, 58, 0.15); color: #ff6961; }
[data-theme="dark"] .msg-warn { background: rgba(255, 159, 10, 0.15); color: #ffb340; }

/* === Loading === */
.spinner {
    width: 28px;
    height: 28px;
    border: 3px solid var(--hover-bg);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
    margin: 48px auto;
}
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes fadeUp {
    from { opacity: 0; transform: translateY(12px); }
    to { opacity: 1; transform: translateY(0); }
}

/* === Upload Overlay === */
#upload-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: none;
    align-items: center;
    justify-content: center;
    z-index: 2000;
}
[data-theme="dark"] #upload-overlay { background: rgba(0, 0, 0, 0.65); }
.upload-overlay-content {
    background: var(--card);
    border-radius: 20px;
    padding: 48px 40px;
    text-align: center;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.2);
    min-width: 320px;
}
[data-theme="dark"] .upload-overlay-content {
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5);
}
.upload-overlay-content .spinner { margin: 0 auto 20px; }
.upload-overlay-text { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
.upload-overlay-sub { font-size: 14px; color: var(--text2); }

/* === Global Notification === */
.global-notification {
    position: fixed;
    top: 16px;
    right: 16px;
    z-index: 3000;
    padding: 14px 20px;
    border-radius: 14px;
    font-size: 14px;
    font-weight: 500;
    display: flex;
    align-items: center;
    gap: 12px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
    animation: slideIn 0.35s cubic-bezier(0.16, 1, 0.3, 1);
    max-width: 500px;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
@keyframes slideIn {
    from { opacity: 0; transform: translateX(40px); }
    to { opacity: 1; transform: translateX(0); }
}
.global-notification button {
    background: none;
    border: none;
    font-size: 18px;
    cursor: pointer;
    opacity: 0.6;
    padding: 0 0 0 8px;
    line-height: 1;
    color: inherit;
}
.global-notification button:hover { opacity: 1; }
.global-notification-ok { background: rgba(52, 199, 89, 0.92); color: #fff; }
.global-notification-err { background: rgba(255, 59, 48, 0.92); color: #fff; }
.global-notification-warn { background: rgba(255, 159, 10, 0.92); color: #fff; }
[data-theme="dark"] .global-notification-ok { background: rgba(48, 209, 88, 0.9); }
[data-theme="dark"] .global-notification-err { background: rgba(255, 69, 58, 0.9); }
[data-theme="dark"] .global-notification-warn { background: rgba(255, 159, 10, 0.9); }

/* === Form inputs === */
input[type="email"], input[type="text"], input[type="number"], input[type="password"] {
    padding: 10px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    font-size: 14px;
    outline: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
}
input[type="email"]::placeholder, input[type="text"]::placeholder, input[type="number"]::placeholder { color: var(--text2); }
input[type="email"]:focus, input[type="text"]:focus, input[type="number"]:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
input[type="number"] { font-variant-numeric: tabular-nums; }

/* Keyboard focus ring for interactive controls (mouse clicks stay clean via :focus-visible) */
.btn:focus-visible, .subtab:focus-visible, nav button:focus-visible,
.cs-trigger:focus-visible, .seg-picker label:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* === Toggle Switch === */
.toggle { position: relative; display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: 14px; }
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-track { width: 44px; height: 24px; background: rgba(0,0,0,0.15); border-radius: 12px; transition: background 0.2s; flex-shrink: 0; position: relative; }
[data-theme="dark"] .toggle-track { background: rgba(255,255,255,0.2); }
.toggle-track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; background: #fff; border-radius: 50%; transition: transform 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.15); }
.toggle input:checked + .toggle-track { background: var(--success); }
.toggle input:checked + .toggle-track::after { transform: translateX(20px); }
.toggle-label { color: var(--text); font-weight: 500; user-select: none; }

/* === Segmented Picker === */
.seg-picker { display: inline-flex; background: var(--hover-bg); border-radius: 10px; padding: 3px; gap: 2px; }
.seg-picker input { display: none; }
.seg-picker label { padding: 7px 16px; border-radius: 8px; font-size: 13px; font-weight: 500; color: var(--text2); cursor: pointer; transition: all 0.2s; white-space: nowrap; user-select: none; }
.seg-picker input:checked + label { background: var(--card); color: var(--text); box-shadow: 0 1px 4px rgba(0,0,0,0.08); }
[data-theme="dark"] .seg-picker input:checked + label { box-shadow: 0 1px 4px rgba(0,0,0,0.25); }
.seg-picker label:hover { color: var(--text); }

/* === Form Group === */
.form-group { padding: 20px; background: var(--hover-bg); border-radius: 12px; margin-bottom: 16px; }
.form-group-title { font-size: 13px; font-weight: 600; color: var(--text2); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 14px; }
.form-row-label { font-size: 14px; font-weight: 500; color: var(--text); min-width: 140px; }
/* Hint under a field: aligned with the control on desktop, flush left once rows stack. */
.form-hint { color: var(--text2); font-size: 12px; margin: 4px 0 0 152px; }
/* Checkbox rows (export rebuild, "show system keyspaces") */
.rebuild-chk { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font-size: 14px; }
.rebuild-chk input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; margin: 0; }

/* Alert indicator on the Monitoring nav button */
nav button { position: relative; }
nav button.has-alert::after {
    content: '';
    position: absolute;
    top: 5px; right: 6px;
    width: 7px; height: 7px;
    border-radius: 50%;
    background: var(--danger);
    box-shadow: 0 0 0 2px var(--card);
    animation: livePulse 1.8s infinite;
}


/* === Responsive ==========================================================
   The phone layout is a rewrite, not a squeezed desktop: one sticky bar,
   swipeable tab strips, data tables as key/value cards, 44px touch targets
   and 16px form text (anything smaller makes iOS zoom the page on focus).
   ======================================================================= */

/* --- Tablets / small laptops --- */
@media (max-width: 1024px) {
    .container { padding: 20px; }
    .ct-tiles { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; padding: 12px; }
    .probe-kvs { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
}

/* --- Phones --- */
@media (max-width: 768px) {
    /* Desktop's tight tracking is what makes small text muddy on a phone: reset it and give
       the copy a roomier line box instead. */
    body {
        overflow-x: hidden;
        letter-spacing: 0;
        line-height: 1.45;
        font-size: 16px;
    }
    h1, h2, h3, .card h2 { letter-spacing: -0.01em; line-height: 1.25; }

    /* ---- Top bar: title left, controls right, one row, under the notch ---- */
    #app > header {
        height: calc(54px + var(--safe-t));
        padding-top: var(--safe-t);
    }
    .header-inner { padding: 0 12px 0 calc(14px + var(--safe-l)); }
    #app > header h1 {
        font-size: 17px;
        font-weight: 700;
        max-width: 46%;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    #global-controls {
        position: fixed;
        top: var(--safe-t);
        right: calc(10px + var(--safe-r));
        height: 54px;
        gap: 7px;
        z-index: 1100;
    }
    .lang-switch { padding: 2px; }
    .lang-btn { padding: 6px 9px; font-size: 11.5px; }
    .theme-toggle { width: 32px; height: 32px; border-radius: 10px; }
    #logout-btn { padding: 7px 11px; font-size: 12.5px; border-radius: 9px; }
    /* min-height:0 keeps the header picker out of the 46px form-control sizing below. */
    select.font-select {
        height: 32px; min-height: 0; max-width: 92px; font-size: 11.5px;
        padding: 0 22px 0 9px; background-size: 12px 12px;
    }

    /* ---- Page tabs: full-bleed snap scroller pinned under the header ---- */
    .container {
        padding: 0 calc(14px + var(--safe-l)) calc(84px + var(--safe-b)) calc(14px + var(--safe-r));
    }
    nav {
        display: flex;
        gap: 7px;
        background: var(--bg);
        border-bottom: 1px solid var(--border);
        border-radius: 0;
        padding: 9px calc(14px + var(--safe-r)) 9px calc(14px + var(--safe-l));
        margin: 0 calc(-14px - var(--safe-r)) 16px calc(-14px - var(--safe-l));
        position: sticky;
        top: calc(54px + var(--safe-t));
        z-index: 90;
        overflow-x: auto;
        scroll-snap-type: x proximity;
        overscroll-behavior-x: contain;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    nav::-webkit-scrollbar, .subtabs::-webkit-scrollbar { display: none; }
    nav button {
        flex: 0 0 auto;
        scroll-snap-align: center;
        padding: 9px 15px;
        font-size: 14.5px;
        font-weight: 600;
        border-radius: 11px;
        background: var(--hover-bg);
    }
    nav button.active {
        background: var(--accent);
        color: var(--on-accent);
        box-shadow: 0 2px 10px color-mix(in srgb, var(--accent) 35%, transparent);
    }
    nav button:active { transform: scale(0.96); }
    nav button.has-alert::after { top: 4px; right: 4px; box-shadow: 0 0 0 2px var(--bg); }

    /* Sub-tabs (Monitoring, chart windows) get the same swipeable treatment. */
    .subtabs {
        display: flex;
        max-width: 100%;
        overflow-x: auto;
        flex-wrap: nowrap;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
        scroll-snap-type: x proximity;
        margin-bottom: 16px;
    }
    .subtab {
        flex: 0 0 auto;
        scroll-snap-align: center;
        padding: 9px 15px;
        font-size: 13.5px;
        min-height: 38px;
    }
    .subtab.active { background: var(--card); color: var(--text); }

    /* ---- Cards & type scale ---- */
    .card { padding: 16px 15px; border-radius: 16px; margin-bottom: 14px; }
    .card h2 { font-size: 18px; line-height: 1.25; margin-bottom: 14px; }
    .card > p { font-size: 14px; line-height: 1.45; }

    /* ---- Controls: 44px targets, 16px text (no iOS zoom-on-focus) ---- */
    .btn {
        min-height: 44px;
        padding: 11px 18px;
        font-size: 15px;
        font-weight: 600;
        border-radius: 12px;
    }
    .btn-sm { min-height: 36px; padding: 7px 13px; font-size: 13px; border-radius: 10px; }
    select, .cs-trigger {
        min-height: 46px;
        font-size: 16px;
        border-radius: 12px;
        min-width: 0;
        width: 100%;
    }
    select { padding: 12px 40px 12px 14px; background-position: right 14px center; background-size: 17px 17px; }
    input[type="email"], input[type="text"], input[type="number"], input[type="password"], textarea {
        font-size: 16px;
        min-height: 46px;
        border-radius: 12px;
        width: 100%;
    }
    .probe-input { font-size: 15px; min-height: 168px; line-height: 1.55; }
    .toggle-track { width: 50px; height: 28px; }
    .toggle-track::after { width: 24px; height: 24px; }
    .toggle input:checked + .toggle-track::after { transform: translateX(22px); }
    .rebuild-chk input[type="checkbox"] { width: 22px; height: 22px; }

    /* ---- Forms stack: label above a full-width control ---- */
    .form-row {
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
        margin-bottom: 14px;
    }
    .form-row > .btn, #uploadBtn { width: 100%; }
    .form-row-label {
        min-width: 0;
        font-size: 12px;
        font-weight: 700;
        color: var(--text2);
        text-transform: uppercase;
        letter-spacing: 0.04em;
    }
    .form-row-label:empty { display: none; }
    .form-hint { margin-left: 0; font-size: 12.5px; }
    .form-group { padding: 14px; border-radius: 14px; }
    .form-group-title { font-size: 12px; margin-bottom: 12px; }
    .seg-picker { display: flex; flex-wrap: wrap; width: 100%; }
    .seg-picker label { flex: 1 1 auto; text-align: center; padding: 10px 12px; font-size: 13.5px; }
    .drop-zone { padding: 32px 16px; border-radius: 14px; }
    .drop-zone-text { font-size: 15px; }

    /* ---- Data tables become key/value cards ----
       Label sits left, value right (::before takes the free space), and anything
       wide — a progress bar, a row of buttons — wraps onto its own full line. */
    .table-wrap { overflow-x: visible; padding: 0; }
    /* Container cards sit inside a group card — keep them off its border. */
    .ct-table-wrap { overflow-x: visible; padding: 10px 10px 0; }
    table.dt, table.dt tbody { display: block; width: 100%; }
    table.dt thead { display: none; }
    table.dt tr {
        display: block;
        border: 1px solid var(--border);
        border-radius: 14px;
        background: var(--card);
        box-shadow: var(--shadow);
        margin-bottom: 12px;
        padding: 2px 0;
        overflow: hidden;
    }
    table.dt td {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: flex-end;
        gap: 6px 12px;
        min-height: 44px;
        padding: 9px 14px;
        border-bottom: 1px solid var(--border);
        text-align: right;
        font-size: 15px;
        overflow-wrap: anywhere;
    }
    table.dt td::before {
        content: attr(data-label);
        margin-right: auto;
        flex: 0 1 auto;
        text-align: left;
        color: var(--text2);
        font-weight: 700;
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: 0.04em;
        line-height: 1.3;
    }
    table.dt td[data-label=""]::before, table.dt td:not([data-label])::before { content: ""; margin-right: auto; }
    table.dt tr td:last-child { border-bottom: none; }
    table.dt td:empty { display: none; }
    /* First cell is the card's headline, not a key/value pair. */
    table.dt tr td:first-child {
        display: block;
        text-align: left;
        font-size: 16px;
        font-weight: 700;
        padding: 12px 14px 11px;
        background: var(--surface);
    }
    table.dt tr td:first-child::before { display: none; }
    table.dt td code { font-size: 13px; }
    table.dt td .pbar, table.dt td .job-progress { flex: 1 0 100%; min-width: 0; margin-top: 2px; }
    table.dt td .pbar-meta, table.dt td .job-progress-info { font-size: 12.5px; }
    table.dt td .kind-chip { font-size: 15px; }
    table.dt td .btn { min-height: 40px; }
    table.dt td .act-detail { text-align: right; }
    /* Job group separators stay plain labels rather than cards. */
    table.dt tr.job-group-row, table.dt tr.job-group-toggle {
        border: none; box-shadow: none; background: transparent; margin: 14px 0 6px; padding: 0; overflow: visible;
    }
    table.dt tr.job-group-row td {
        display: block; border: none; background: transparent;
        padding: 2px 2px; min-height: 0; text-align: left; font-size: 12px;
    }
    table.dt tr.job-group-toggle td { min-height: 40px; padding: 10px 2px; }
    /* Today's runs: accent wash across the card, with the stripe on the card itself. */
    table.dt tr.job-row-today { box-shadow: inset 4px 0 0 var(--accent), var(--shadow); }
    table.dt tr.job-row-today td:first-child { background: var(--accent-light); box-shadow: none; }
    table.dt td.cell-actions { white-space: normal; gap: 8px; }

    /* ---- Stat tiles & container tiles ---- */
    .mon-stats { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
    .mon-stat { min-width: 0; padding: 12px 13px; border-radius: 14px; }
    .mon-stat-label { font-size: 11px; letter-spacing: 0.3px; }
    .mon-stat-value { font-size: 21px; margin-top: 3px; }
    .mon-stat-sub { font-size: 11.5px; }
    .mon-toolbar { gap: 8px; margin-bottom: 14px; }
    .mon-toolbar .mon-range-label { font-size: 12.5px; }
    .mon-toolbar .subtabs { margin: 0; min-width: 0; flex: 1 1 auto; }
    .mon-hint { display: none; } /* range brushing is a pointer gesture; hint is desktop-only */
    .mon-charts, .mon-charts.size-s, .mon-charts.size-m, .mon-charts.size-l {
        grid-template-columns: 1fr;
        gap: 12px;
    }
    .mon-chart-card { border-radius: 14px; padding: 12px 12px 8px; }
    .mon-chart-head { flex-direction: column; align-items: flex-start; gap: 6px; }
    .mon-chart-title { font-size: 15px; }
    .mon-leg { padding: 6px 9px; font-size: 12.5px; min-height: 34px; }
    .mon-tooltip { font-size: 12.5px; padding: 7px 10px; }
    .mon-stub { padding: 40px 16px; }

    .ct-head { padding: 13px 14px; gap: 11px; flex-wrap: wrap; }
    .ct-ic { width: 36px; height: 36px; border-radius: 10px; }
    .ct-ic svg { width: 19px; height: 19px; }
    .ct-title { font-size: 15px; }
    .ct-desc { font-size: 12px; }
    .ct-agg {
        flex-basis: 100%;
        gap: 6px 14px;
        font-size: 12.5px;
        margin-top: 4px;
        padding-top: 10px;
        border-top: 1px dashed var(--border);
    }
    .ct-tiles { grid-template-columns: 1fr; gap: 10px; padding: 12px; }
    .ct-tile { border-radius: 14px; padding: 13px 14px; }
    .ct-tile-name { font-size: 14.5px; }
    .ct-metric { font-size: 13.5px; margin-top: 8px; }

    /* ---- Check / Images pages ---- */
    .probe-kvs { grid-template-columns: 1fr; gap: 0; }
    .probe-kv { padding: 9px 0; font-size: 14px; }
    .probe-sub { font-size: 15px; }
    .probe-hint { font-size: 13px; }
    .img-block { margin-bottom: 22px; }
    .img-card { padding: 14px 15px; border-radius: 14px; }
    .img-title { font-size: 15.5px; }
    .img-body, .img-sides { grid-template-columns: 1fr; gap: 12px; }
    .img-side { padding: 14px 15px; border-radius: 14px; }
    .img-side .probe-kv { font-size: 13.5px; }
    .img-side .probe-kv b { font-size: 14px; }
    .img-actions .btn { width: 100%; }
    .img-ct { width: 100%; font-size: 13px; }

    /* ---- Queue payload viewer ---- */
    .q-msg-body { font-size: 12px; max-height: 240px; }
    .q-msg-head { flex-wrap: wrap; }

    /* ---- Login ---- */
    .login-card {
        width: calc(100% - 32px);
        max-width: 380px;
        padding: 34px 22px;
        border-radius: 20px;
    }
    .login-card h1 { font-size: 25px; }
    .login-card input[type="password"], .login-card button[type="submit"] { font-size: 16px; min-height: 50px; }

    /* ---- Toasts / overlays ---- */
    .global-notification {
        top: calc(10px + var(--safe-t));
        left: calc(12px + var(--safe-l));
        right: calc(12px + var(--safe-r));
        max-width: none;
        font-size: 14px;
        padding: 13px 16px;
    }
    .upload-overlay-content { min-width: 0; width: calc(100% - 40px); padding: 34px 22px; border-radius: 18px; }

    /* ---- Background tasks: FAB + full-screen sheet ---- */
    #task-toggle {
        right: calc(16px + var(--safe-r));
        bottom: calc(16px + var(--safe-b));
        width: 56px;
        height: 56px;
    }
    #task-scrim {
        display: block;
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.4);
        z-index: 2550;
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.3s ease;
    }
    #task-scrim.open { opacity: 1; pointer-events: auto; }
    #task-panel {
        width: 100%;
        max-width: none;
        height: 100dvh;
        border-left: none;
        padding-top: var(--safe-t);
        padding-bottom: var(--safe-b);
    }
    .task-panel-header { padding: 15px 16px; font-size: 17px; }
    .task-panel-close { font-size: 30px; padding: 0 8px; min-height: 44px; min-width: 44px; }
    #task-panel-body { padding: 12px calc(12px + var(--safe-r)) 20px calc(12px + var(--safe-l)); gap: 12px; }
    .task-card { padding: 13px 14px; border-radius: 14px; }
    .task-card-title { font-size: 14px; }
    .task-card .status { font-size: 11px; padding: 3px 9px; }
    .task-card-msg, .task-card-details { font-size: 12.5px; }
    .task-card-actions { gap: 16px; margin-top: 12px; }
    .task-card-actions a, .task-card-actions button { font-size: 13.5px; padding: 6px 0; }

    /* ---- Misc ---- */
    .status { font-size: 11.5px; padding: 4px 10px; }
    .msg { font-size: 14px; padding: 13px 15px; }
    .spinner { margin: 36px auto; }
}

/* --- Small phones (≤480px): the top bar sheds everything it can ----------------
   Four controls plus a title do not fit across a phone. The language pill collapses to a
   single button showing the language you switch *to*, the sign-out label becomes its icon,
   and the font picker (a preference, not a task) drops out entirely — which leaves the
   title enough room to stay readable. */
@media (max-width: 480px) {
    .lang-switch { background: none; padding: 0; }
    .lang-btn { padding: 7px 10px; border-radius: 9px; background: var(--hover-bg); font-size: 12px; }
    .lang-btn.active { display: none; }
    select.font-select { display: none; }
    #logout-btn { padding: 7px 9px; gap: 0; }
    #logout-btn .logout-icon { display: block; }
    #logout-label { display: none; }
    #app > header h1 { max-width: calc(100% - 150px); }
}

/* --- Narrow phones (≤360px): drop the optional chrome, single-column tiles --- */
@media (max-width: 360px) {
    select.font-select { display: none; }
    #app > header h1 { max-width: 40%; font-size: 16px; }
    .mon-stats { grid-template-columns: 1fr; }
    .container { padding-left: calc(11px + var(--safe-l)); padding-right: calc(11px + var(--safe-r)); }
    nav {
        padding-left: calc(11px + var(--safe-l));
        padding-right: calc(11px + var(--safe-r));
        margin-left: calc(-11px - var(--safe-l));
        margin-right: calc(-11px - var(--safe-r));
    }
    .card { padding: 14px 13px; }
    table.dt td { font-size: 14.5px; padding: 9px 12px; }
    table.dt tr td:first-child { font-size: 15.5px; padding: 11px 12px 10px; }
}

/* --- Touch devices: hover states never latch on --- */
@media (hover: none) {
    tbody tr:hover td, tbody tr.job-row-today:hover td { background: inherit; }
    tbody tr.job-group-toggle:hover td { background: var(--hover-bg); color: var(--text2); }
    .ct-tile:hover { transform: none; box-shadow: none; }
    .ct-head:hover, .mon-leg:hover { background: transparent; }
    .mon-stat:hover, .mon-chart-card:hover, .ct-group:hover, .task-card:hover {
        box-shadow: none; border-color: var(--border);
    }
    .btn-primary:hover { background: var(--accent); }
    .btn-secondary:hover { background: var(--hover-bg); }
    /* Task details expand on tap only (the card is already a toggle). */
    .task-card:hover .task-card-details { max-height: 0; }
    .task-card.expanded .task-card-details { max-height: 460px; }
    /* Pressed states carry the feedback hover used to. */
    .btn:active, nav button:active, .subtab:active { opacity: 0.85; }
    .ct-head:active, .task-card:active, .cs-option:active { background: var(--hover-bg); }
}

/* === Sub-tabs (per-product tabs on the upload and products pages) === */
.subtabs {
    display: inline-flex;
    background: var(--hover-bg);
    border-radius: 10px;
    padding: 3px;
    margin-bottom: 20px;
    gap: 2px;
    flex-wrap: wrap;
}
.subtab {
    padding: 7px 16px;
    border: none;
    background: transparent;
    border-radius: 8px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 500;
    color: var(--text2);
    font-family: inherit;
    transition: all 0.2s ease;
    white-space: nowrap;
}
.subtab:hover { color: var(--text); }
.subtab.active { background: var(--card); color: var(--text); box-shadow: 0 1px 4px rgba(0,0,0,0.08); }

/* =============================================================================
   bald-business additions.

   Everything above is the scylla-phone-checker sheet verbatim, so the two panels
   stay visually identical and a fix there ports across cleanly. Only the pieces
   this project needs and that one does not have live below.
   ============================================================================= */

/* Enrolment overlay reuses the login card, so it needs the same backdrop. */
#setup-overlay {
    position: fixed;
    inset: 0;
    background: radial-gradient(ellipse at 50% 0%, rgba(0, 113, 227, 0.06) 0%, var(--bg) 60%);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}
[data-theme="dark"] #setup-overlay {
    background: radial-gradient(ellipse at 50% 0%, rgba(10, 132, 255, 0.06) 0%, var(--bg) 60%);
}
#setup-overlay.hidden { display: none !important; }
#setup-error { color: var(--danger); font-size: 13px; margin-top: 14px; min-height: 20px; }
#setup-body input {
    width: 100%;
    padding: 14px 16px;
    margin-bottom: 10px;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    font-size: 16px;
    outline: none;
    font-family: inherit;
    background: var(--input-bg);
    color: var(--text);
}
#setup-body input:focus { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-light); }
.qr-box {
    background: #fff;
    padding: 12px;
    border-radius: 12px;
    display: inline-block;
    margin: 8px 0 14px;
}
.secret-line {
    font-family: var(--font-mono);
    font-size: 13px;
    word-break: break-all;
    color: var(--text2);
    margin-bottom: 14px;
}

/* Header identity chip. */
.header-inner { display: flex; align-items: center; gap: 12px; }
#whoami { font-size: 12px; }

/* Stat tiles on the dashboards. */
/* Дашборд — мозаика, а не стопка полос. Группы разной ширины затекают в
   промежутки (dense), поэтому маленькие блоки не оставляют пустых дорожек. */
.dash {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    grid-auto-flow: dense;
    gap: 16px;
    align-items: start;
}
.dash-group {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 18px 18px 20px;
}
/* Группа с четырьмя+ плашками стоит того, чтобы занять две колонки. */
.dash-group-wide { grid-column: span 2; }

.dash-group-title {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text2);
    margin: 0 0 14px;
}

.dash-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
    gap: 10px;
}
.dash-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 13px;
    padding: 13px 14px;
    min-width: 0;
}
/* Главная метрика группы — крупнее, но своей ширины. На всю строку она стояла
   раньше, и под двузначным числом оставалось пустое поле в пять его ширин:
   выделяет метрику размер цифры, а не полоса воздуха справа от неё. */
.dash-tile-value {
    font-size: 27px;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.05;
    color: var(--text);
    overflow-wrap: anywhere;
}
.dash-tile-big .dash-tile-value { font-size: 38px; }
.dash-tile-label {
    font-size: 12px;
    color: var(--text2);
    line-height: 1.2;
}
/* Пояснение под подписью: «через сколько часов номер станет грязным» — это не
   вторая подпись, а сноска, и весом она отличаться обязана. */
.dash-tile-hint { font-size: 11px; color: var(--text2); opacity: 0.75; line-height: 1.25; }

/* Сводка по номерам — те же плитки, но не внутри группы дашборда: у неё своя
   карточка и свой отступ снизу до таблицы. */
.num-summary { margin-bottom: 14px; }

/* Тон красит само число и подсвечивает плашку — так живая цифра видна с
   одного взгляда, а нули остаются нейтральными. */
.dash-tile-accent { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
.dash-tile-accent  .dash-tile-value { color: var(--accent); }
.dash-tile-accent  { background: var(--accent-light); }
.dash-tile-ok      .dash-tile-value { color: var(--success); }
.dash-tile-ok      { border-color: color-mix(in srgb, var(--success) 34%, var(--border)); }
.dash-tile-warn    .dash-tile-value { color: var(--warning); }
.dash-tile-warn    { border-color: color-mix(in srgb, var(--warning) 40%, var(--border)); }

@media (max-width: 760px) {
    .dash { grid-template-columns: 1fr; }
    .dash-group-wide { grid-column: auto; }
    .dash-tile-big .dash-tile-value { font-size: 32px; }
}

/* Сумма не рвётся посреди числа: «1 240,50$» переносом читается как два числа.
   Плитке с ней разрешено занять две колонки — но только ей и только если
   иначе не влезает. */
.dash-tile-money { grid-column: span 2; }
.dash-tile-money .dash-tile-value { overflow-wrap: normal; white-space: nowrap; }

/* Access matrix on the Settings page: a wide grid that must scroll on its own
   rather than pushing the page sideways. */
.matrix-wrap { overflow-x: auto; }
.matrix { border-collapse: collapse; width: 100%; font-size: 13px; }
.matrix th, .matrix td {
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    text-align: left;
    white-space: nowrap;
}
.matrix th { color: var(--text2); font-weight: 600; }
.matrix td.center, .matrix th.center { text-align: center; }
.matrix input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--accent); cursor: pointer; }

/* Toolbar above tables: search box, filters, actions on one line. */
.toolbar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 16px; }
.toolbar input[type="text"], .toolbar input[type="search"], .toolbar select {
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}
.toolbar input:focus, .toolbar select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
.grow { flex: 1 1 220px; }

/* Masked secret cell: click to reveal, which costs an audited round-trip. */
.secret {
    font-family: var(--font-mono);
    cursor: pointer;
    color: var(--text2);
    user-select: text;
}
.secret:hover { color: var(--accent); }

.muted { color: var(--text2); }
.right { text-align: right; }
.mono { font-family: var(--font-mono); font-size: 12px; }
.pill {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    background: var(--accent-light);
    color: var(--accent);
}

/* Request payload rendered as label/value pairs. */
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; font-size: 13px; }
.kv dt { color: var(--text2); }
.kv dd { margin: 0; }

@media (max-width: 700px) {
    .header-inner { flex-wrap: wrap; }
}

/* The notification element is permanent in the markup, so it has to be hidden
   until notify() adds .show — the scylla sheet assumes the node is created and
   removed instead. */
#notification { display: none; }
#notification.show { display: flex; }

/* === Query editor (products) ============================================== */

.query-box { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.query-input {
    flex: 1 1 420px;
    padding: 12px 14px;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    background: var(--input-bg);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 14px;
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.query-input:focus { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-light); }
/* A bad query is flagged while it is typed, not after a round-trip. */
.query-input.invalid { border-color: var(--danger); box-shadow: 0 0 0 4px rgba(255, 59, 48, 0.14); }

.query-error { color: var(--danger); font-size: 13px; min-height: 18px; margin-top: 8px; }
.query-error code { font-family: var(--font-mono); }
/* Points at the offending character the way a compiler does. */
.query-caret {
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1;
    color: var(--danger);
    white-space: pre;
    overflow: hidden;
    margin-top: 2px;
}

.query-hint { margin-top: 10px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.hint-label { font-size: 12px; color: var(--text2); margin-right: 4px; }
.chip {
    padding: 5px 11px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 12px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.chip-input {
    padding: 4px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 12px;
}

/* === Bulk editor ========================================================== */

.bulk-bar {
    background: var(--accent-light);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
    border-radius: 12px;
    padding: 14px 16px;
    margin-bottom: 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.bulk-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.bulk-row select, .bulk-row input[type="text"], .bulk-row input[type="number"] {
    padding: 8px 11px;
    border: 1px solid var(--border-strong);
    border-radius: 9px;
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
}

/* === Sortable tables and paging ========================================== */

th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--text); }
th.sorted { color: var(--accent); }
td.tick, th.tick { width: 34px; text-align: center; }
td.tick input, th.tick input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
tr.row-selected { background: var(--accent-light); }

.pager {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}
.pager select {
    padding: 6px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 9px;
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    margin-left: auto;
}
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

/* === Callout: page-level explanation that must not read as table chrome === */

.callout {
    border-left: 3px solid var(--accent);
    background: var(--surface);
    border-radius: 0 10px 10px 0;
    padding: 12px 16px;
    margin-bottom: 18px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--text);
}
.callout b { font-weight: 600; }
/* Предупреждение — тот же блок, другой цвет полосы: «настройка не работает ни
   для кого» читается не как пояснение. */
.callout-warn { border-left-color: var(--warning); }
/* Предупреждение — тот же блок, другой цвет полосы: «настройка не работает ни
   для кого» читается не как пояснение. */
.callout-warn { border-left-color: var(--warning); }

/* === Access settings: one card per subject instead of a scrolling matrix === */

.access-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; }
.access-card {
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 16px 18px;
    background: var(--surface);
}
.access-card h3 {
    font-size: 15px;
    font-weight: 600;
    margin-bottom: 4px;
    display: flex;
    align-items: center;
    gap: 8px;
}
.access-card .subject-kind { font-size: 11px; color: var(--text2); font-weight: 500; }
.access-list { display: flex; flex-direction: column; gap: 2px; margin-top: 10px; }
.access-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px;
    border-radius: 8px;
    cursor: pointer;
    font-size: 13px;
}
.access-item:hover { background: var(--hover-bg); }
.access-item input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--accent); flex-shrink: 0; }
.access-item .sub { color: var(--text2); font-size: 11px; margin-left: auto; }
.access-group-title {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: var(--text2);
    margin: 14px 0 4px;
}
.kind-row {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 8px;
    border-radius: 8px;
    font-size: 13px;
}
.kind-row:hover { background: var(--hover-bg); }
.kind-row .kind-name { font-weight: 600; min-width: 62px; }
.kind-row label { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }

/* === Action button groups: stop them colliding, keep them tappable ======== */

.actions { display: flex; flex-wrap: wrap; gap: 6px; }
.actions .btn { white-space: nowrap; }

/* === Manual product entry: a real form, two columns on desktop =========== */

.entry-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 14px; }
.entry-field { display: flex; flex-direction: column; gap: 5px; }
.entry-field label { font-size: 12px; font-weight: 600; color: var(--text2); }
.entry-field .req { color: var(--danger); }
.entry-field input, .entry-field select {
    padding: 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}
.entry-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
.entry-field .field-hint { font-size: 11px; color: var(--text2); }

/* Password handed out once — must be readable, not a footnote. */
.credential {
    font-family: var(--font-mono);
    font-size: 16px;
    font-weight: 600;
    background: var(--surface);
    border: 1px dashed var(--border-strong);
    border-radius: 8px;
    padding: 6px 10px;
    display: inline-block;
    user-select: all;
}

@media (max-width: 700px) {
    .query-box { flex-direction: column; align-items: stretch; }
    .access-grid { grid-template-columns: 1fr; }
    .pager select { margin-left: 0; }
}

/* =============================================================================
   Desktop polish pass.
   Appended last on purpose: everything below deliberately wins over the rules
   inherited from scylla-phone-checker, which were tuned for a narrower panel
   with smaller type.
   ============================================================================= */

/* --- width -----------------------------------------------------------------
   1200px was leaving a third of a desktop screen empty while tables scrolled
   sideways inside it. */
.container { max-width: min(1780px, 95vw); padding: 26px 30px 60px; }
#app > header .header-inner { max-width: min(1780px, 95vw); }

/* --- type ------------------------------------------------------------------
   One step up across the board. The old 11–12px secondary text was unreadable
   next to 14px body text and read as noise rather than information. */
td { font-size: 14.5px; padding: 13px 16px; }
th { font-size: 12.5px; padding: 11px 16px; }
.muted { font-size: 14px; }
.form-hint, .field-hint { font-size: 13.5px; color: var(--text2); margin-left: 0; }
.callout { font-size: 14.5px; padding: 14px 18px; }
.card h2 { font-size: 21px; margin-bottom: 18px; }
.card { padding: 26px 28px; }
.pill { font-size: 12.5px; padding: 4px 11px; }
.status { font-size: 12.5px; }

/* --- buttons ---------------------------------------------------------------
   A bare .btn had no background at all, so every secondary button was invisible
   against the card in both themes. It gets a real surface and border now; the
   coloured variants are re-declared below because they are defined earlier in
   the sheet and would otherwise lose to this rule. */
.btn {
    padding: 10px 18px;
    font-size: 14px;
    font-weight: 500;
    border: 1px solid var(--border-strong);
    background: var(--card);
    color: var(--text);
    border-radius: 10px;
}
.btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.btn:disabled, .btn[disabled] {
    opacity: 0.45;
    cursor: not-allowed;
    background: var(--card);
    color: var(--text2);
    border-color: var(--border);
}
.btn:disabled:hover { border-color: var(--border); color: var(--text2); background: var(--card); }
.btn-sm { padding: 8px 14px; font-size: 13.5px; border-radius: 9px; }

.btn-primary { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--on-accent); }
.btn-danger { background: var(--danger); color: var(--on-danger); border-color: var(--danger); }
.btn-danger:hover { opacity: 0.9; color: var(--on-danger); border-color: var(--danger); background: var(--danger); }
.btn-success { background: var(--success); color: var(--on-success); border-color: var(--success); }
.btn-success:hover { opacity: 0.9; color: #fff; background: var(--success); border-color: var(--success); }

/* Groups of row actions: never collide, never wrap mid-button. */
.actions { display: flex; flex-wrap: wrap; gap: 8px; }
.actions .btn { white-space: nowrap; }
td.actions, td .actions { white-space: normal; }

/* --- custom checkbox -------------------------------------------------------
   Native checkboxes render differently in every browser and ignore the theme. */
.chk { display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: 14px; line-height: 1.35; }
.chk input { position: absolute; opacity: 0; width: 0; height: 0; }
.chk-box {
    width: 20px;
    height: 20px;
    flex-shrink: 0;
    border: 1.5px solid var(--border-strong);
    border-radius: 6px;
    background: var(--input-bg);
    display: inline-grid;
    place-items: center;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.chk-box svg { opacity: 0; transform: scale(0.5); transition: all 0.15s cubic-bezier(0.16, 1, 0.3, 1); }
.chk input:checked + .chk-box { background: var(--accent); border-color: var(--accent); }
.chk input:checked + .chk-box { color: var(--on-accent); }
.chk input:checked + .chk-box svg { opacity: 1; transform: scale(1); }
.chk input:focus-visible + .chk-box { box-shadow: 0 0 0 4px var(--accent-light); }
.chk:hover .chk-box { border-color: var(--accent); }

/* --- switch ---------------------------------------------------------------- */
.switch { display: inline-flex; align-items: center; gap: 12px; cursor: pointer; font-size: 14.5px; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
    width: 46px;
    height: 26px;
    border-radius: 13px;
    background: var(--border-strong);
    position: relative;
    flex-shrink: 0;
    transition: background 0.2s ease;
}
.switch-track::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
    transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track::after { transform: translateX(20px); }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 4px var(--accent-light); }
.switch-text { display: flex; flex-direction: column; gap: 2px; }
.switch-text b { font-weight: 600; }
.switch-text span { font-size: 13px; color: var(--text2); }

/* --- query bar -------------------------------------------------------------
   One bordered field that holds the input, the typed value control and the
   actions, so the whole search reads as a single control rather than a row of
   loose widgets. */
.qbar {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 7px 8px 7px 18px;
    border: 1.5px solid var(--border-strong);
    border-radius: 16px;
    background: var(--input-bg);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.qbar:focus-within { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-light); }
.qbar.invalid { border-color: var(--danger); }
.qbar.invalid:focus-within { box-shadow: 0 0 0 4px rgba(255, 59, 48, 0.15); }
.qbar-icon { color: var(--text2); flex-shrink: 0; display: flex; }
.qbar input.qtext {
    flex: 1;
    min-width: 120px;
    border: none;
    outline: none;
    background: transparent;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 15px;
    padding: 12px 0;
}
.qbar input.qtext::placeholder { color: var(--text2); font-family: var(--font-sans); }
/* Slot for the value control that matches the column being typed — the date
   picker lives here, inside the bar, not under it. */
.qbar-slot { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.qbar-slot input[type="date"] {
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--card);
    color: var(--text);
    font-family: inherit;
    font-size: 13.5px;
    padding: 7px 10px;
}
.qbar-slot .seg { display: inline-flex; background: var(--hover-bg); border-radius: 10px; padding: 3px; gap: 2px; }
.qbar-slot .seg button {
    border: none;
    background: transparent;
    color: var(--text2);
    font-family: inherit;
    font-size: 13.5px;
    font-weight: 500;
    padding: 6px 14px;
    border-radius: 8px;
    cursor: pointer;
}
.qbar-slot .seg button:hover { background: var(--card); color: var(--text); }
.qbar-slot .type-note { font-size: 13px; color: var(--text2); white-space: nowrap; }
.qbar-actions { display: flex; gap: 8px; flex-shrink: 0; }

/* Error line: one row, no ascii art. */
.qerror {
    display: none;
    align-items: flex-start;
    gap: 10px;
    margin-top: 12px;
    padding: 12px 16px;
    border-radius: 12px;
    background: rgba(255, 59, 48, 0.09);
    border: 1px solid rgba(255, 59, 48, 0.25);
    font-size: 14px;
    color: var(--text);
}
.qerror.show { display: flex; }
.qerror .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--danger); margin-top: 6px; flex-shrink: 0; }
.qerror b { color: var(--danger); font-weight: 600; }
.qerror .example {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: 13px;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 7px;
    padding: 2px 8px;
    margin-top: 5px;
}

/* Suggestion strip under the bar. */
.qsuggest {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding: 12px 16px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--surface);
}
.qsuggest .hint-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text2);
    margin-right: 4px;
    white-space: nowrap;
}
.chip {
    padding: 7px 14px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--card);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 13.5px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.chip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.chip .chip-type { font-family: var(--font-sans); font-size: 11.5px; color: var(--text2); margin-left: 6px; }
.chip:hover .chip-type { color: var(--accent); }

/* --- bulk editor ----------------------------------------------------------- */
.bulk-bar {
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: 16px;
    padding: 18px 20px;
    margin-bottom: 20px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.bulk-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bulk-head .count {
    font-size: 15px;
    font-weight: 600;
}
.bulk-head .scope { font-size: 13.5px; color: var(--text2); }
.bulk-section {
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
    padding-top: 14px;
    border-top: 1px solid var(--border);
}
.bulk-section > .label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text2);
    min-width: 108px;
}
.bulk-section input[type="text"], .bulk-section input[type="number"] {
    padding: 9px 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
    min-width: 150px;
}
.bulk-section input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }

/* --- pager ------------------------------------------------------------------
   Was a row of borderless mini-buttons that disappeared into the card. */
.pager {
    display: flex;
    gap: 10px;
    align-items: center;
    flex-wrap: wrap;
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
    font-size: 14px;
}
.pager .btn { min-width: 44px; }
.pager .page-info { color: var(--text2); font-size: 14px; }
.pager .grow-gap { margin-left: auto; }

/* --- selection ticks -------------------------------------------------------- */
td.tick, th.tick { width: 42px; text-align: center; }
tr.row-selected td { background: var(--accent-light); }

/* --- payload cell on the requests page --------------------------------------
   A definition list squeezed into a table cell was unreadable; these are short
   key/value pairs and read better stacked with the key as a quiet prefix. */
.payload { display: flex; flex-direction: column; gap: 4px; min-width: 240px; max-width: 460px; }
.payload-row { display: flex; gap: 8px; font-size: 14px; line-height: 1.4; }
.payload-key { color: var(--text2); flex-shrink: 0; min-width: 74px; }
.payload-val { color: var(--text); word-break: break-word; }
.payload-empty { color: var(--text2); font-size: 14px; }

/* --- entry form ------------------------------------------------------------- */
.entry-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 18px; }
.entry-field label { font-size: 14px; font-weight: 600; color: var(--text); }
.entry-field input, .entry-field select { padding: 11px 14px; font-size: 14.5px; }

/* Handed-out credential: sits beside the message, copies on click. */
.credential {
    font-size: 17px;
    padding: 9px 14px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.credential:hover { border-color: var(--accent); color: var(--accent); }

/* --- access settings --------------------------------------------------------- */
.access-section-title {
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text2);
    margin: 26px 0 14px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
}
.access-section-title:first-of-type { margin-top: 8px; }
.access-card { padding: 20px 22px; }
.access-card h3 { font-size: 16.5px; }
.access-card .subject-kind { font-size: 12px; }
.access-item { font-size: 14px; padding: 9px 10px; }
.access-group-title { font-size: 12px; margin: 18px 0 6px; }
.kind-row { font-size: 14px; padding: 10px 8px; gap: 18px; }
.kind-row .kind-name { min-width: 72px; }

/* Compact custom select used inside a settings row. */
.cs.cs-inline { min-width: 152px; }
.cs.cs-inline .cs-trigger { min-height: 36px; padding: 6px 12px; font-size: 13.5px; border-radius: 10px; }

@media (max-width: 900px) {
    .container { max-width: 100%; padding: 20px 16px 48px; }
    .qbar { flex-wrap: wrap; }
    .qbar input.qtext { flex-basis: 100%; }
    .bulk-section > .label { min-width: 0; flex-basis: 100%; }
}

/* --- odds and ends from the polish pass ----------------------------------- */

/* Pushes an element to the right end of a flex row. */
.grow-gap { margin-left: auto; }

/* One-time password sits next to the button that produced it. */
.credential-slot { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.credential-slot .muted { font-size: 13.5px; }

.qsuggest .muted { font-size: 13.5px; }
.qsuggest code {
    font-family: var(--font-mono);
    font-size: 13px;
    background: var(--card);
    border: 1px solid var(--border);
    padding: 1px 6px;
    border-radius: 6px;
}

/* A settings row is label + control; the control keeps a sane minimum width. */
.access-item .cs.cs-inline { min-width: 148px; }
.access-item > span:first-child { flex: 1; }

/* Rows of buttons inside a table cell must not be squeezed by the column. */
td.cell-actions { white-space: nowrap; }
td.cell-actions .actions { flex-wrap: nowrap; }

/* Custom select inside a table row should not stretch the column. */
td .cs.cs-inline { min-width: 138px; }

/* --- last of the small type ------------------------------------------------
   Anything still under ~13px read as noise next to 14.5px body text. */
th { font-size: 13px; text-transform: none; letter-spacing: 0.2px; }
.pill { font-size: 13px; }
.status { font-size: 13px; }
.chip .chip-type { font-size: 12.5px; }
.access-card .subject-kind { font-size: 12.5px; }
.access-group-title { font-size: 12.5px; }
.qsuggest .hint-label { font-size: 13.5px; }
.entry-field .field-hint { font-size: 13.5px; }
.switch-text span { font-size: 13.5px; }
.bulk-head .scope, .bulk-section > .label { font-size: 13.5px; }
.subtab { font-size: 14px; padding: 8px 18px; }

/* A disabled button still has to read as a button, not as a blank patch. */
.btn:disabled, .btn[disabled] {
    opacity: 1;
    background: var(--surface);
    color: var(--text2);
    border-color: var(--border-strong);
    border-style: dashed;
}
.mono { font-size: 13.5px; }

/* =============================================================================
   Button colours — last word in the sheet, on purpose.

   `.btn` and `.btn-primary` have identical specificity, so whichever comes last
   wins. A plain `.btn { background: … }` appended after the variants silently
   stripped the fill from every primary and danger button while leaving their
   white text, which in the light theme is white on near-white. The variants are
   therefore re-declared immediately after the base rule, and this block stays at
   the bottom of the file.
   ============================================================================= */

.btn {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
}
.btn:hover {
    background: var(--accent-light);
    border-color: var(--accent);
    color: var(--accent);
}

.btn-primary {
    background: var(--accent);
    color: var(--on-accent);
    border-color: var(--accent);
}
.btn-primary:hover {
    background: var(--accent-hover);
    border-color: var(--accent-hover);
    color: #fff;
}

.btn-danger {
    background: var(--danger);
    color: #fff;
    border-color: var(--danger);
}
.btn-danger:hover {
    background: var(--danger);
    border-color: var(--danger);
    color: #fff;
    opacity: 0.9;
}

.btn-success {
    background: var(--success);
    color: #fff;
    border-color: var(--success);
}
.btn-success:hover {
    background: var(--success);
    border-color: var(--success);
    color: #fff;
    opacity: 0.9;
}

.btn-secondary {
    background: var(--hover-bg);
    color: var(--text);
    border-color: var(--border-strong);
}

/* Disabled comes after the variants too, so a disabled primary reads as
   disabled rather than as a dimmed blue button. */
.btn:disabled, .btn[disabled],
.btn-primary:disabled, .btn-danger:disabled, .btn-success:disabled {
    opacity: 1;
    background: var(--surface);
    color: var(--text2);
    border: 1px dashed var(--border-strong);
    cursor: not-allowed;
    transform: none;
}
.btn:disabled:hover, .btn[disabled]:hover {
    background: var(--surface);
    color: var(--text2);
    border-color: var(--border-strong);
}

/* --- follow-up fixes -------------------------------------------------------- */

/* The query field sits inside the bordered bar, so it must not draw its own
   border or focus ring — the global input[type=text]:focus rule was giving the
   search two nested highlights. */
.qbar input.qtext,
.qbar input.qtext:focus,
.qbar input.qtext:hover {
    border: none;
    outline: none;
    box-shadow: none;
    background: transparent;
    padding: 12px 4px 12px 6px;
    border-radius: 0;
}
.qbar { padding-left: 16px; gap: 12px; }
.qbar-icon { margin-right: 2px; }

/* Upload: batch on the left, single item on the right. */
.upload-split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.upload-split .card { margin-bottom: 0; }
@media (max-width: 1200px) { .upload-split { grid-template-columns: 1fr; } }

/* The row that carries the duplicate-handling choice and the action button. */
.action-row {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 20px;
    padding-top: 18px;
    border-top: 1px solid var(--border);
}
.mode-pick { display: flex; align-items: center; gap: 10px; }
.mode-pick .mode-label { font-size: 13.5px; color: var(--text2); white-space: nowrap; }

.file-summary {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin: 16px 0 12px;
    flex-wrap: wrap;
}
.file-summary .file-name { font-weight: 600; font-size: 15px; }

.import-tally { display: flex; gap: 18px; flex-wrap: wrap; align-items: baseline; margin: 16px 0 12px; }
.import-tally .tally { font-size: 14px; color: var(--text2); }
.import-tally .tally b { font-size: 20px; color: var(--text); margin-right: 4px; }
.import-tally .tally.bad b { color: var(--danger); }

/* Settings: columns instead of one long run, so the card is short without
   shrinking the type. */
.access-grid.wide { grid-template-columns: 1fr; }
.access-cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    gap: 4px 28px;
    align-items: start;
}
.access-cols.wide-cols { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.access-col { min-width: 0; }
.access-col .access-group-title { margin-top: 6px; }
.access-item { padding: 6px 8px; }
.kind-row { padding: 6px 8px; gap: 14px; }
.access-card { padding: 20px 24px; }
.access-card + .access-card { margin-top: 16px; }

/* A closed dropdown is still laid out, and an absolutely positioned box that
   hangs below its row counts toward the scrollable overflow of .table-wrap —
   which is what put a vertical scrollbar on a three-row table. Taking it out of
   layout entirely while closed is the fix; when open it is positioned fixed
   against the viewport by csToggle and no longer belongs to that container. */
.cs .cs-dropdown { display: none; }
.cs.open .cs-dropdown { display: block; }

/* =============================================================================
   Dialogs, language switch, and the light-theme contrast fix.
   ============================================================================= */

/* --- secondary text ----------------------------------------------------------
   The light value lives with the rest of the light palette at the top of the
   sheet now. It used to be patched here with a `:root:not([data-theme="dark"])`
   selector, which outranked every `[data-theme="…"]` block: the moment the theme
   chain grew past two entries, warm, cool, dusk and indigo all silently got the
   light theme's muted ink — 3.6:1 on a night surface. Each palette declares its
   own. */

/* Inactive nav and sub-tabs were the worst of it: grey on near-white. */
nav button, .subtab { color: #4a4a52; }
[data-theme="dark"] nav button, [data-theme="dark"] .subtab { color: var(--text2); }
nav button:hover, .subtab:hover { color: var(--text); }
nav button.active, .subtab.active { color: var(--text); font-weight: 600; }

/* --- language switch --------------------------------------------------------- */
.lang-switch { background: var(--card); border: 1px solid var(--border-strong); }
.lang-btn { color: var(--text2); padding: 6px 12px; font-size: 12.5px; }
.lang-btn:hover { color: var(--text); }
.lang-btn.active { background: var(--accent); color: var(--on-accent); }

/* --- dialogs ------------------------------------------------------------------
   The browser's alert/confirm/prompt are unstyled, ignore the theme and are
   partly disabled in modern browsers; these replace them. */
#modal-root { display: none; }
#modal-root.open { display: block; }

.modal-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.42);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    /* Выше слоя обучения (#tour-root, 4200): вопрос, заданный поверх тура,
       должен быть виден и нажимаем — иначе он висит под подсветкой, и ответить
       на него нечем. */
    z-index: 4300;
    animation: modalFade 0.16s ease;
}
[data-theme="dark"] .modal-scrim { background: rgba(0, 0, 0, 0.6); }

.modal {
    position: fixed;
    z-index: 4301;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(460px, calc(100vw - 32px));
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 18px;
    box-shadow: var(--shadow-lg);
    padding: 24px 26px 20px;
    animation: modalPop 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes modalFade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modalPop {
    from { opacity: 0; transform: translate(-50%, -48%) scale(0.96); }
    to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

.modal-head { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.modal-head h3 { font-size: 18px; font-weight: 600; letter-spacing: -0.01em; }
.modal-icon {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    display: grid;
    place-items: center;
    background: var(--accent-light);
    color: var(--accent);
    flex-shrink: 0;
}
.modal-icon.danger { background: rgba(255, 59, 48, 0.13); color: var(--danger); }

.modal-body { font-size: 15px; line-height: 1.5; color: var(--text); }
.modal-detail {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 10px;
    background: var(--surface);
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 13px;
    word-break: break-word;
}
.modal-input {
    width: 100%;
    margin-top: 16px;
    padding: 12px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 11px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 15px;
    outline: none;
}
.modal-input:focus { border-color: var(--accent); box-shadow: 0 0 0 4px var(--accent-light); }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* Окно с полями: строка причины и галочки под ней. Заведено под удаление
   заявки, где вопросов сразу пять и разносить их по пяти окнам нельзя —
   человек передумает в середине и оставит заявку наполовину снесённой. */
.modal-note {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 10px;
    background: color-mix(in srgb, var(--warning) 18%, var(--card));
    border: 1px solid color-mix(in srgb, var(--warning) 40%, var(--border));
    font-size: 13px;
    line-height: 1.45;
}
.modal-form { display: flex; flex-direction: column; gap: 12px; margin-top: 16px; text-align: left; }
.modal-field { display: flex; flex-direction: column; gap: 6px; }
.modal-field > span { font-size: 12px; color: var(--text2); }
/* Отступ поля живёт на .modal-input ради одиночного prompt; внутри формы его
   задаёт сама форма, иначе строка отъезжает от своей подписи. */
.modal-form .modal-input { margin-top: 0; }
/* Список словаря: моноширинным и выделяемым — его копируют целиком и вставляют
   с чужого стенда, а не читают как прозу. */
.modal-area {
    resize: vertical;
    min-height: 120px;
    font: 12.5px/1.6 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
/* Рамка вокруг обычной .chk: галочек в окне несколько, и каждая — отдельное
   решение про деньги. Без рамки они читались одним абзацем. */
.modal-check {
    display: flex;
    align-items: flex-start;
    padding: 10px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}
.modal-check:hover { border-color: var(--border-strong); }
.modal-check.danger { border-color: var(--danger); }
/* Выбор одного из двух: те же строки, но коробка круглая — «или/или» читается
   формой раньше подписи. */
.modal-radios { display: flex; flex-direction: column; gap: 8px; }
/* Три коротких положения в строку: в окне разбора такая группа стоит у каждого,
   кто держал заявку, и колонкой они втроём выталкивают кнопки окна за экран. */
.modal-radios.row { flex-direction: row; flex-wrap: wrap; }
.modal-radios.row .modal-radio { flex: 1 1 0; min-width: 96px; }
.modal-radio .chk-box { border-radius: 50%; }
/* Заголовок над радио-группой: групп в окне правки несколько (вид, доступная
   почта, выданная почта…), и без подписи они сливаются в одну кашу. */
.modal-group-label { display: block; font-size: 12px; color: var(--text2); margin-top: 4px; }
.modal-check-hint { display: block; margin-top: 3px; font-size: 12px; color: var(--text2); font-style: normal; }

/* --- мессенджер: плашка в углу -------------------------------------------------
   Висит поверх любого экрана, но под модалками (4000): диалог подтверждения
   перекрывает переписку, а не наоборот — он спрашивает про необратимое.

   Кнопка, список и окна стоят по координатам, которые считает layoutInbox, а не
   потоком. Поток двигал бы кнопку размером того, что рядом открылось: список
   раскрывался — и кнопка уезжала из-под пальца. Место кнопки выбирает человек,
   и открытие списка или окна не имеет права ни сдвинуть её, ни лечь поверх.

   Пузыри и подписи взяты у ленты заявки (.feed-row/.bubble): разговор с
   человеком один, и выглядеть он обязан одинаково с обеих сторон. */
#inbox-root {
    position: fixed;
    inset: 0;
    z-index: 3600;
    /* Ловим клики только по самим окнам: прозрачное полотно во весь экран
       перехватывало бы нажатия по странице под ним. */
    pointer-events: none;
}
#inbox-root .inbox-dock,
#inbox-root .inbox-list,
#inbox-root .inbox-reqs,
#inbox-root .inbox-win { pointer-events: auto; }
#inbox-root:empty { display: none; }

/* Обёртка окон в раскладке не участвует — она только для медиазапроса, который
   прячет лишние окна на узком экране. */
.inbox-windows { display: contents; }

.inbox-dock, .inbox-list, .inbox-reqs, .inbox-win { position: fixed; left: 0; top: 0; }

/* Заявки клиента поверх разговора. Держится курсором: ушёл с неё — и её нет,
   поэтому ни крестика, ни подложки во весь экран у неё нет. */
.inbox-reqs {
    width: 460px;
    max-width: calc(100vw - 16px);
    max-height: min(520px, calc(100vh - 24px));
    display: flex;
    flex-direction: column;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-lg);
    overflow: auto;
    padding: 14px 16px 16px;
    animation: inboxPop 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}
.inbox-reqs-head { display: flex; align-items: baseline; gap: 8px; }
.inbox-reqs-title { font-size: 13px; font-weight: 650; }
/* Имя клиента рядом с заголовком: слой открывают из разговора, и «чьи это
   заявки» должно читаться, не переводя взгляд на окно под ним. */
.inbox-reqs-name {
    font-size: 12px;
    color: var(--text2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.inbox-win-head, .inbox-list-head, .inbox-fab, .inbox-grip { touch-action: none; }
.inbox-win-head, .inbox-list-head { cursor: grab; }
.is-dragging, .is-dragging .inbox-win-head, .is-dragging .inbox-list-head {
    cursor: grabbing;
}
/* Пока тащат — без анимации появления и без плавных переходов: они дёргают
   картинку вслед за курсором. */
.is-dragging { animation: none; transition: none; user-select: none; }

.inbox-dock { display: flex; }

/* --- круглая кнопка ---
   Заметно крупнее прочих кнопок и залита акцентом: плашка одна на всю панель, и
   искать её глазами среди белых карточек человек не должен. */
.inbox-fab {
    position: relative;
    width: 64px;
    height: 64px;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--accent) 55%, #000);
    background: var(--accent);
    color: #fff;
    box-shadow: var(--shadow-lg), 0 6px 18px color-mix(in srgb, var(--accent) 42%, transparent);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.16s ease, background 0.16s ease, color 0.16s ease;
}
.inbox-fab:hover { transform: translateY(-2px); background: var(--accent-hover); }
/* Раскрытая — наоборот, светлая: список рядом и так кричит акцентом. */
.inbox-fab.is-open {
    background: var(--card);
    border-color: var(--border-strong);
    color: var(--text);
    box-shadow: var(--shadow-lg);
}

.inbox-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    border-radius: 11px;
    background: var(--danger);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    line-height: 22px;
    text-align: center;
    box-shadow: 0 0 0 2px var(--bg);
}

/* Пришло сообщение: кнопка бьётся, краснеет и расходится кольцом. Тихий пульс
   в углу экрана не замечали — а пропущенное сообщение клиента стоит дороже
   секунды мельтешения. Четыре такта и тишина: пульс, который не кончается,
   читается как поломка. */
.inbox-fab.inbox-pulse { animation: inboxPulse 0.85s ease-out 4; }
.inbox-fab.inbox-pulse .inbox-badge { animation: inboxBadgePop 0.85s ease-out 4; }

@keyframes inboxPulse {
    0% {
        transform: scale(1);
        background: var(--danger);
        box-shadow: var(--shadow-lg), 0 0 0 0 color-mix(in srgb, var(--danger) 70%, transparent);
    }
    28% {
        transform: scale(1.16) rotate(-7deg);
        background: var(--danger);
        box-shadow: var(--shadow-lg), 0 0 0 10px color-mix(in srgb, var(--danger) 28%, transparent);
    }
    58% {
        transform: scale(1.04) rotate(5deg);
        background: var(--danger);
        box-shadow: var(--shadow-lg), 0 0 0 20px color-mix(in srgb, var(--danger) 10%, transparent);
    }
    100% {
        transform: scale(1);
        background: var(--accent);
        box-shadow: var(--shadow-lg), 0 0 0 30px transparent;
    }
}

@keyframes inboxBadgePop {
    0%, 100% { transform: scale(1); }
    30% { transform: scale(1.45); }
}

/* Кому мельтешение противопоказано — тому мигание цветом без прыжков. */
@media (prefers-reduced-motion: reduce) {
    .inbox-fab.inbox-pulse { animation: inboxPulseSoft 0.9s ease-in-out 4; }
    .inbox-fab.inbox-pulse .inbox-badge { animation: none; }
    @keyframes inboxPulseSoft {
        0%, 100% { background: var(--accent); }
        50% { background: var(--danger); }
    }
}

/* --- список разговоров --- */
.inbox-list {
    width: 320px;
    max-height: min(460px, calc(100vh - 140px));
    display: flex;
    flex-direction: column;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    animation: inboxPop 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Своя, а не modalPop: тот написан для модалки по центру и держит в кадрах
   translate(-50%, -50%), которым уносит плашку из её угла. */
@keyframes inboxPop {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
}

.inbox-list-head {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 12px 12px 10px 16px;
    border-bottom: 1px solid var(--border);
}
.inbox-list-title { font-size: 15px; font-weight: 650; }

/* Свой распорщик, а не .grow: у того flex-basis 220px, и в шапке окна шириной
   240 он ужимает имя собеседника до многоточия. */
.inbox-spacer { flex: 1 1 auto; }

.inbox-icon-btn {
    background: none;
    border: none;
    color: var(--text2);
    cursor: pointer;
    padding: 4px 6px;
    border-radius: 8px;
    line-height: 1;
    display: inline-flex;
    align-items: center;
}
.inbox-icon-btn:hover { background: var(--hover-bg); color: var(--text); }
.inbox-icon-btn.is-on { color: var(--accent); }

.inbox-tabs { display: flex; gap: 4px; padding: 8px 10px 0; }
.inbox-tab {
    flex: 1 1 0;
    padding: 7px 8px;
    border: none;
    border-radius: 10px;
    background: none;
    color: var(--text2);
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
}
.inbox-tab:hover { background: var(--hover-bg); }
.inbox-tab.is-active { background: var(--surface); color: var(--text); }

.inbox-search { padding: 8px 10px; }
.inbox-search .input { width: 100%; font-size: 13.5px; padding: 8px 11px; }

.inbox-rows { flex: 1 1 auto; overflow-y: auto; padding: 0 6px 8px; }
.inbox-empty { padding: 18px 12px; font-size: 13px; text-align: center; }

.inbox-row {
    display: block;
    width: 100%;
    text-align: left;
    padding: 9px 10px;
    border: none;
    border-radius: 10px;
    background: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
}
.inbox-row:hover { background: var(--hover-bg); }
.inbox-row-top { display: flex; align-items: baseline; gap: 8px; }
.inbox-row-name {
    font-size: 13.5px;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.inbox-row-when { margin-left: auto; font-size: 11.5px; color: var(--text2); flex: none; }
.inbox-row-bottom { display: flex; align-items: center; gap: 8px; margin-top: 2px; }
.inbox-row-text {
    flex: 1 1 auto;
    font-size: 12.5px;
    color: var(--text2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Непрочитанное узнаётся жирным именем, а не цветной строкой: цветных строк в
   списке из тридцати разговоров становится тридцать. */
.inbox-row.is-unseen .inbox-row-name { font-weight: 750; }
.inbox-row.is-unseen .inbox-row-text { color: var(--text); }

.inbox-more { width: calc(100% - 20px); margin: 6px 10px 2px; }

/* --- окно разговора --- */
.inbox-win {
    width: 380px;
    height: min(520px, calc(100vh - 120px));
    min-width: 380px;
    display: flex;
    flex-direction: column;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 16px;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
    animation: inboxPop 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Свёрнутая вкладка — не бледная полоска: её ищут глазами среди чужих карточек,
   а с сообщением от этого собеседника она обязана ещё и позвать. */
.inbox-win.is-min {
    height: auto;
    width: 264px;
    min-width: 0;
    background: var(--surface);
    border-color: var(--border-strong);
}
.inbox-win.is-min:hover { border-color: var(--accent); }
.inbox-win.is-min .inbox-win-head {
    cursor: pointer;
    border-bottom: none;
    padding: 11px 10px 11px 14px;
}
.inbox-win.is-min .inbox-win-name { font-weight: 700; }
.inbox-win.is-min.is-alert {
    border-color: var(--danger);
    box-shadow: var(--shadow-lg), 0 0 0 2px color-mix(in srgb, var(--danger) 35%, transparent);
    animation: inboxTabAlert 1.1s ease-in-out 3;
}
@keyframes inboxTabAlert {
    0%, 100% { background: var(--surface); }
    50% { background: color-mix(in srgb, var(--danger) 16%, var(--surface)); }
}

/* Счётчик на самой вкладке: цифра на круглой кнопке общая и не говорит, кто из
   свёрнутых написал. */
.inbox-win-badge {
    min-width: 20px;
    height: 20px;
    padding: 0 6px;
    border-radius: 10px;
    background: var(--danger);
    color: #fff;
    font-size: 11.5px;
    font-weight: 700;
    line-height: 20px;
    text-align: center;
    flex: none;
}

.inbox-win-head {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 10px 10px 10px 14px;
    border-bottom: 1px solid var(--border);
    flex: none;
}
.inbox-win-name {
    flex: 0 1 auto;
    min-width: 0;
    font-size: 14px;
    font-weight: 650;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.inbox-win-tag {
    font-size: 11px;
    color: var(--text2);
    background: var(--surface);
    border-radius: 6px;
    padding: 2px 6px;
    flex: none;
}

/* Отступ с чужой стороны — не украшение: пузырь, прижатый к стенке окна, читается
   как обрезанный, и по одному взгляду не видно, чей он. Селектор с окном, а не
   голый .inbox-log: правила .chat лежат ниже по файлу и при равном весе бьют. */
.inbox-win .inbox-log {
    flex: 1 1 auto;
    margin: 8px 0 0;
    padding: 0 12px;
    max-height: none;
    gap: 8px;
}
.inbox-log .feed-in { padding-right: 20px; }
.inbox-log .feed-out { padding-left: 20px; }
.inbox-log .bubble { max-width: 84%; }

.inbox-win .rq-answer { padding: 8px 10px 10px; margin: 0; flex: none; }
.inbox-locked { font-size: 12.5px; margin: 6px 2px 0; }

/* Поле ввода: строка с круглым значком отправки справа, как в любом мессенджере.
   Кнопка со словом «Отправить» и поле в три строки занимали треть окна, в котором
   и так помещается пять реплик. */
.rq-input.composer {
    display: flex;
    align-items: flex-end;
    gap: 6px;
    margin-top: 8px;
    padding: 5px 5px 5px 12px;
    border-radius: 18px;
}
.rq-input.composer textarea {
    flex: 1 1 auto;
    padding: 6px 0;
    min-height: 20px;
    max-height: 108px;
    overflow-y: auto;
    font-size: 14px;
}
.composer-send {
    flex: none;
    width: 32px;
    height: 32px;
    border: none;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: background 0.14s ease, transform 0.14s ease;
}
/* Скрепка — рядом с полем, а не в шапке окна: снимок отправляют тем же жестом,
   что и слова, и искать его в другом месте человек не станет. */
.composer-clip {
    flex: none;
    width: 32px;
    height: 32px;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: transparent;
    color: var(--text2);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: color 0.14s ease, border-color 0.14s ease;
}
.composer-clip:hover { color: var(--accent); border-color: var(--accent); }
.composer-send:hover:not(:disabled) { background: var(--accent-hover); transform: scale(1.07); }
.composer-send:disabled { background: var(--border-strong); color: var(--card); cursor: default; }
.composer-send svg { margin-left: -1px; }

/* Уголок растяжения. Свой, а не resize: both, — у браузерного чужая лапка, чужой
   курсор и свой размер прямо в style, который переписывает раскладка. */
.inbox-grip {
    position: absolute;
    right: 1px;
    bottom: 1px;
    width: 18px;
    height: 18px;
    cursor: nwse-resize;
}
.inbox-grip::after {
    content: '';
    position: absolute;
    right: 4px;
    bottom: 4px;
    width: 8px;
    height: 8px;
    border-right: 2px solid var(--border-strong);
    border-bottom: 2px solid var(--border-strong);
    border-bottom-right-radius: 3px;
}
.inbox-win:hover .inbox-grip::after { border-color: var(--accent); }
.inbox-win.is-min .inbox-grip { display: none; }

/* Полосы прокрутки — свои: браузерная серая плита поперёк переписки выбивается
   из всего остального в панели. */
.inbox-log, .inbox-rows, .composer textarea, .chat {
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
.inbox-log::-webkit-scrollbar,
.inbox-rows::-webkit-scrollbar,
.composer textarea::-webkit-scrollbar, .chat::-webkit-scrollbar { width: 10px; }
.inbox-log::-webkit-scrollbar-track,
.inbox-rows::-webkit-scrollbar-track,
.composer textarea::-webkit-scrollbar-track, .chat::-webkit-scrollbar-track { background: transparent; }
.inbox-log::-webkit-scrollbar-thumb,
.inbox-rows::-webkit-scrollbar-thumb,
.composer textarea::-webkit-scrollbar-thumb, .chat::-webkit-scrollbar-thumb {
    background: var(--border-strong);
    border: 3px solid transparent;
    background-clip: content-box;
    border-radius: 8px;
}
.inbox-log::-webkit-scrollbar-thumb:hover,
.inbox-rows::-webkit-scrollbar-thumb:hover,
.composer textarea::-webkit-scrollbar-thumb:hover, .chat::-webkit-scrollbar-thumb:hover {
    background: var(--text2);
    background-clip: content-box;
}

/* Узкий экран: одно окно на всю ширину — два рядом здесь не помещаются, а ужатые
   до половины не читаются вовсе. */
@media (max-width: 900px) {
    .inbox-windows .inbox-win:not(:last-child) { display: none; }
}
@media (max-width: 700px) {
    .inbox-list, .inbox-win {
        width: calc(100vw - 20px);
        min-width: 0;
        max-width: calc(100vw - 20px);
    }
    .inbox-win:not(.is-min) { height: min(70vh, calc(100vh - 130px)); }
    .inbox-grip { display: none; }
}

/* Всплывашка, по которой можно нажать, — единственная кликабельная. */
.global-notification-link { cursor: pointer; }

/* --- settings: catalogue editors ---------------------------------------------- */
.grow-input { width: 100%; min-width: 140px; }
td .grow-input,
.action-row .grow-input {
    padding: 8px 11px;
    border: 1.5px solid var(--border-strong);
    border-radius: 9px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}
td .grow-input:focus, .action-row .grow-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
.action-row input[type="text"] {
    padding: 9px 12px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}

/* White on #ff3b30 is 3.5:1 — the fill is unmistakable but the label is thin at
   13.5px. A darker red for the filled variant only; the colour variable itself
   is left alone so borders, badges and text elsewhere keep the design system's
   shade. Dark theme has a lighter background behind it and does not need this. */
:root:not([data-theme="dark"]) .btn-danger {
    background: #d70015;
    border-color: #d70015;
}
:root:not([data-theme="dark"]) .btn-danger:hover {
    background: #b8000f;
    border-color: #b8000f;
}

/* =============================================================================
   Theme slider, tab caret, per-column rights, login spacing.
   ============================================================================= */

/* --- theme control -----------------------------------------------------------
   Collapsed it is the old icon button; hovering or focusing widens it into a
   slider. The width transition is on the wrapper so the slider itself does not
   have to animate. */
.theme-control {
    display: inline-flex;
    align-items: center;
    gap: 0;
    background: var(--hover-bg);
    border-radius: 9px;
    padding: 0;
    overflow: hidden;
    transition: gap 0.25s cubic-bezier(0.16, 1, 0.3, 1);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
}
.theme-slider {
    width: 0;
    opacity: 0;
    margin: 0;
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    cursor: ew-resize;
    transition: width 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}
.theme-control:hover .theme-slider,
.theme-control:focus-within .theme-slider {
    width: 116px;
    opacity: 1;
    margin: 0 12px 0 4px;
}
.theme-slider::-webkit-slider-runnable-track {
    height: 6px;
    border-radius: 3px;
    /* The track is the gradient it controls: light on the left, dark on the right. */
    background: linear-gradient(90deg, #f5f5f7 0%, #8e8e93 50%, #131316 100%);
}
.theme-slider::-moz-range-track {
    height: 6px;
    border-radius: 3px;
    background: linear-gradient(90deg, #f5f5f7 0%, #8e8e93 50%, #131316 100%);
}
.theme-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 15px;
    height: 15px;
    margin-top: -4.5px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--card);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    cursor: ew-resize;
}
.theme-slider::-moz-range-thumb {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    background: var(--accent);
    border: 2px solid var(--card);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
    cursor: ew-resize;
}

/* --- sliding tab caret --------------------------------------------------------
   One pill per strip that moves, instead of a background that appears on
   whichever button is active — a background cannot animate between elements. */
nav, .subtabs { position: relative; }
.tab-caret {
    position: absolute;
    top: 3px;
    left: 0;
    border-radius: 8px;
    background: var(--card);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1),
                width 0.28s cubic-bezier(0.16, 1, 0.3, 1),
                opacity 0.2s ease;
    pointer-events: none;
    z-index: 0;
}
[data-theme="dark"] .tab-caret { box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35); }
nav button, .subtab { position: relative; z-index: 1; }
/* The pill draws the active state now, so the button must not paint its own. */
nav button.active, .subtab.active { background: transparent; box-shadow: none; }

/* --- page transition ---------------------------------------------------------- */
#content { opacity: 0; transform: translateY(4px); }
#content.page-in {
    opacity: 1;
    transform: none;
    transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.16, 1, 0.3, 1);
}

/* --- a value this person may not see ------------------------------------------
   Deliberately not styled as clickable: unlike a masked secret, there is nothing
   behind it to reveal — the value never left the server. */
.hidden-value {
    font-family: var(--font-mono);
    color: var(--text2);
    opacity: 0.65;
    cursor: not-allowed;
    user-select: none;
}

/* --- per-column rights in settings --------------------------------------------- */
.rights-block {
    margin-top: 14px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
    overflow: hidden;
}
.rights-block > summary {
    cursor: pointer;
    padding: 11px 16px;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--text);
    user-select: none;
    list-style: none;
    display: flex;
    align-items: center;
    gap: 8px;
}
.rights-block > summary::-webkit-details-marker { display: none; }
.rights-block > summary::before {
    content: '';
    width: 0;
    height: 0;
    border-left: 5px solid var(--text2);
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    transition: transform 0.18s ease;
}
.rights-block[open] > summary::before { transform: rotate(90deg); }
.rights-block > summary:hover { background: var(--hover-bg); }
.rights-block .access-cols { padding: 4px 16px 14px; gap: 4px 24px; }
/* Подсказка про колонки файла — та же коробка, что и права: раскрывашка на
   странице должна быть одна на вид, а не две похожие. */
.upload-columns .table-wrap { padding: 0 16px; }
.upload-columns .muted { padding: 8px 16px 14px; font-size: 12.5px; }
/* Цены стоят сразу под раскрывашкой колонок, и без отступа их подписи липнут к
   её рамке. Тот же зазор, что и у самой раскрывашки сверху. */
.upload-prices { margin-top: 14px; }
.access-list.two-up { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 0 10px; }
.access-item.compact { padding: 5px 6px; font-size: 13.5px; }
.access-group-title { display: flex; align-items: center; gap: 10px; }
.link-btn {
    border: none;
    background: none;
    color: var(--accent);
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    padding: 0;
    text-transform: none;
    letter-spacing: 0;
}
.link-btn:hover { text-decoration: underline; }

/* --- login: the 2FA field was flush against the password field ------------------ */
.login-card input[type="password"] { margin-bottom: 10px; }
#login-code { margin-top: 4px; }
.login-card form { display: flex; flex-direction: column; }

/* =============================================================================
   Finance: KPI tiles, charts, ledger.

   Series colours follow the data's job, not taste. Both modes are selected
   steps of the same three hues, validated against the surface they sit on:
   worst adjacent CVD ΔE 21.6 light / 19.2 dark. Profit additionally uses a
   different mark — a line, not a bar — so identity never rests on hue alone.
   ============================================================================= */

.chart, .bar-list, .kpi-grid {
    /* Slot 1 aqua = profit, slot 2 blue = income, slot 3 red = expense. */
    --series-profit: #1baf7a;
    --series-income: #2a78d6;
    --series-expense: #e34948;
}
[data-theme="dark"] .chart,
[data-theme="dark"] .bar-list,
[data-theme="dark"] .kpi-grid {
    --series-profit: #199e70;
    --series-income: #3987e5;
    --series-expense: #e66767;
}

/* --- period picker ------------------------------------------------------------ */
.period-bar { display: flex; flex-direction: column; gap: 12px; margin-bottom: 22px; }
.preset-row, .range-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.range-field { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--text2); }
.range-field input {
    padding: 8px 11px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}
.range-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }

/* --- KPI tiles -----------------------------------------------------------------
   A headline number is not a chart: no axis, no mark, just the figure and what
   it is. The tone strip is a second cue beside the colour. */
.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.kpi {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 18px 20px 16px 22px;
    overflow: hidden;
}
.kpi::before {
    content: '';
    position: absolute;
    left: 0; top: 0; bottom: 0;
    width: 4px;
    background: var(--border-strong);
}
.kpi-up::before   { background: var(--series-income); }
.kpi-down::before { background: var(--series-expense); }
.kpi-good::before { background: var(--series-profit); }
.kpi-bad::before  { background: var(--series-expense); }
.kpi-label { font-size: 13px; color: var(--text2); margin-bottom: 6px; }
.kpi-value { font-size: 27px; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.kpi-sub { font-size: 12.5px; color: var(--text2); margin-top: 6px; }

/* --- chart marks ---------------------------------------------------------------
   Grid and axes recede; the data does not. */
.chart { display: block; overflow: visible; }
.chart-grid { stroke: var(--border); stroke-width: 1; }
.chart-axis { stroke: var(--border-strong); stroke-width: 1; }
.chart-tick { fill: var(--text2); font-size: 12px; font-family: var(--font-sans); }
.chart-bar { stroke: none; }
.chart-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.chart-dot { stroke: var(--card); stroke-width: 2; }
.chart-hit { fill: transparent; cursor: crosshair; }
.chart-empty { padding: 34px 0; text-align: center; color: var(--text2); font-size: 14px; }

.chart-bar.series-income  { fill: var(--series-income); }
.chart-bar.series-expense { fill: var(--series-expense); }
.chart-line.series-profit { stroke: var(--series-profit); }
.chart-dot.series-profit-dot { fill: var(--series-profit); }

.chart-legend { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 14px; padding-left: 62px; }
.legend-item { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--text2); }
.legend-item .dot, .tip-row .dot {
    width: 10px; height: 10px; border-radius: 50%; display: inline-block; flex-shrink: 0;
}
.legend-item .dash { width: 16px; height: 3px; border-radius: 2px; display: inline-block; }
.dot.series-income, .dash.series-income   { background: var(--series-income); }
.dot.series-expense, .dash.series-expense { background: var(--series-expense); }
.dot.series-profit, .dash.series-profit   { background: var(--series-profit); }

/* --- tooltip -------------------------------------------------------------------- */
.chart-tip {
    position: fixed;
    z-index: 5000;
    display: none;
    flex-direction: column;
    gap: 3px;
    padding: 10px 13px;
    border-radius: 11px;
    background: var(--card);
    border: 1px solid var(--border-strong);
    box-shadow: var(--shadow-lg);
    font-size: 13px;
    color: var(--text);
    pointer-events: none;
    max-width: 280px;
}
.chart-tip.show { display: flex; }
.chart-tip b { font-weight: 600; }
.tip-row { display: flex; align-items: center; gap: 7px; }
.tip-row b { margin-left: auto; font-variant-numeric: tabular-nums; }
.tip-sub { font-size: 12px; color: var(--text2); padding-left: 17px; }

/* --- ranked bars ---------------------------------------------------------------- */
.bar-list { display: flex; flex-direction: column; gap: 4px; }
.bar-row {
    display: grid;
    grid-template-columns: minmax(110px, 200px) 1fr max-content;
    gap: 14px;
    align-items: center;
    padding: 7px 8px;
    border-radius: 9px;
    cursor: default;
}
.bar-row:hover { background: var(--hover-bg); }
.bar-label { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-track { height: 12px; background: var(--hover-bg); border-radius: 6px; overflow: hidden; }
.bar-fill { display: block; height: 100%; border-radius: 6px; min-width: 3px; }
.bar-fill.series-income  { background: var(--series-income); }
.bar-fill.series-expense { background: var(--series-expense); }
.bar-fill.series-profit  { background: var(--series-profit); }
.bar-value { font-size: 14px; font-variant-numeric: tabular-nums; font-weight: 600; }

/* --- ledger ---------------------------------------------------------------------- */
.finance-split { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; align-items: start; }
.finance-split .card { margin-bottom: 0; }
@media (max-width: 1200px) { .finance-split { grid-template-columns: 1fr; } }

.positive { color: var(--series-profit); font-weight: 600; }
.negative { color: var(--series-expense); font-weight: 600; }
.pill-income  { background: color-mix(in srgb, var(--series-income) 16%, transparent); color: var(--series-income); }
.pill-expense { background: color-mix(in srgb, var(--series-expense) 16%, transparent); color: var(--series-expense); }

/* --- theme slider, second pass ---------------------------------------------------
   The first version was a bare range input; this one reads as a lamp dimmer. */
.theme-control {
    border: 1px solid var(--border-strong);
    background: var(--card);
    padding: 2px;
}
.theme-control:hover, .theme-control:focus-within { border-color: var(--accent); }
.theme-slider { height: 22px; }
.theme-control:hover .theme-slider,
.theme-control:focus-within .theme-slider { width: 132px; margin: 0 10px 0 2px; }
.theme-slider::-webkit-slider-runnable-track {
    height: 8px;
    border-radius: 4px;
    background: linear-gradient(90deg, #ffffff 0%, #c8c8cc 38%, #6c6c72 62%, #131316 100%);
    border: 1px solid var(--border);
}
.theme-slider::-moz-range-track {
    height: 8px;
    border-radius: 4px;
    background: linear-gradient(90deg, #ffffff 0%, #c8c8cc 38%, #6c6c72 62%, #131316 100%);
    border: 1px solid var(--border);
}
.theme-slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    width: 16px; height: 16px;
    margin-top: -5px;
    border-radius: 50%;
    background: var(--card);
    border: 2px solid var(--accent);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.28);
}
.theme-slider::-moz-range-thumb {
    width: 16px; height: 16px;
    border-radius: 50%;
    background: var(--card);
    border: 2px solid var(--accent);
    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.28);
}

/* =============================================================================
   Multi-select, and the request-kind editor.
   ============================================================================= */

/* Ровно три в ряд: auto-fit растягивал два списка на всю карточку, и раздел из
   двух настроек выглядел важнее раздела из шести. */
.ms-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin: 6px 0 18px; }
@media (max-width: 1180px) { .ms-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 720px) { .ms-grid { grid-template-columns: 1fr; } }
.ms { position: relative; }
.ms-trigger {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 11px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 12px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.ms-trigger:hover { border-color: var(--text2); }
.ms.open .ms-trigger { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
.ms-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ms-count {
    font-size: 12.5px;
    color: var(--text2);
    background: var(--hover-bg);
    padding: 3px 9px;
    border-radius: 999px;
    white-space: nowrap;
}
.ms.open .cs-arrow { transform: rotate(180deg); }

.ms-panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    z-index: 1200;
    display: none;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
.ms.open .ms-panel { display: block; }
/* Список у нижнего края окна раскрывается вверх — см. msPlace(). */
.ms.up .ms-panel { top: auto; bottom: calc(100% + 6px); }
.ms-search { padding: 8px 8px 4px; }
.ms-search input {
    width: 100%;
    padding: 9px 12px;
    border: 1.5px solid var(--border);
    border-radius: 10px;
    background: var(--bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13.5px;
    outline: none;
}
.ms-search input:focus { border-color: var(--accent); }
.ms-list { max-height: 268px; overflow-y: auto; padding: 4px; }
.ms-list::-webkit-scrollbar { width: 7px; }
.ms-list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 4px; }
.ms-row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 9px 11px;
    border-radius: 9px;
    cursor: pointer;
    font-size: 13.5px;
    user-select: none;
}
.ms-row:hover { background: var(--hover-bg); }
.ms-label { flex: 1; min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.35; }
.ms-box {
    width: 19px; height: 19px;
    flex-shrink: 0;
    border: 1.5px solid var(--border-strong);
    border-radius: 6px;
    background: var(--input-bg);
    display: inline-grid;
    place-items: center;
    transition: background 0.12s ease, border-color 0.12s ease;
}
.ms-box.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
/* Three states need three labels, not three shades of one. */
.ms-state {
    font-size: 12px;
    font-weight: 600;
    padding: 3px 10px;
    border-radius: 999px;
    white-space: nowrap;
}
.ms-inherit { background: var(--hover-bg); color: var(--text2); }
.ms-true { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success); }
.ms-false { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
.ms-actions {
    display: flex;
    gap: 14px;
    padding: 10px 14px;
    border-top: 1px solid var(--border);
    background: var(--surface);
}
.ms-empty { padding: 18px; text-align: center; color: var(--text2); font-size: 13.5px; }

.kind-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 4px; }

/* --- request kinds -------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.field > span { font-size: 13px; font-weight: 600; color: var(--text2); }
.field .req { color: var(--danger); }
.field input {
    padding: 10px 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
    width: 100%;
}
.field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
.field-narrow { max-width: 160px; }
.field-check { justify-content: flex-end; padding-bottom: 10px; }

.kind-new {
    border: 1px dashed var(--border-strong);
    border-radius: 14px;
    padding: 18px 20px;
    margin-bottom: 18px;
    background: var(--surface);
}
.kind-new-head { font-size: 14px; font-weight: 600; margin-bottom: 12px; }
.kind-new-form { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.kind-new-form .field { flex: 1 1 180px; }

.kind-list { display: flex; flex-direction: column; gap: 12px; }
.kind-card {
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card);
    overflow: hidden;
}
.kind-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 18px;
    cursor: pointer;
    user-select: none;
}
.kind-head:hover { background: var(--hover-bg); }
.kind-caret {
    width: 0; height: 0; flex-shrink: 0;
    border-left: 6px solid var(--text2);
    border-top: 5px solid transparent;
    border-bottom: 5px solid transparent;
    transition: transform 0.18s ease;
}
.kind-card.open .kind-caret { transform: rotate(90deg); }
.kind-title { font-size: 15.5px; font-weight: 600; }
.kind-code {
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--text2);
    background: var(--hover-bg);
    padding: 2px 9px;
    border-radius: 6px;
}
.kind-meta { font-size: 13px; color: var(--text2); margin-left: auto; }
.kind-state { font-size: 12px; font-weight: 600; padding: 3px 10px; border-radius: 999px; }
.kind-state.on { background: color-mix(in srgb, var(--success) 18%, transparent); color: var(--success); }
.kind-state.off { background: var(--hover-bg); color: var(--text2); }

.kind-body { display: none; padding: 4px 18px 18px; border-top: 1px solid var(--border); }
.kind-card.open .kind-body { display: block; }
.kind-form { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; padding: 16px 0; }
.kind-form .field { flex: 1 1 200px; }
/* Реквизиты пишут в несколько строк — им нужна вся ширина, а не колонка
   рядом с «сколько секунд». */
.kind-form .field-wide { flex: 1 1 100%; }
/* Рамка и поля — те же, что у соседних полей: базовые правила ловят input по
   типу, а textarea мимо них проходит. */
.kind-form .field-wide textarea {
    resize: vertical;
    min-height: 68px;
    padding: 10px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 13px;
    outline: none;
}
.kind-form .field-wide textarea:focus { border-color: var(--accent); }
.kind-form-actions { display: flex; gap: 8px; }

.kind-fields { border-top: 1px solid var(--border); padding-top: 14px; }
.kind-fields-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 10px;
    font-size: 13px;
    font-weight: 600;
    color: var(--text2);
}
.kind-fields-head .muted { font-weight: 400; font-size: 12.5px; }
.kind-nofields { padding: 10px 0 14px; }

.field-row { border: 1px solid var(--border); border-radius: 11px; margin-bottom: 8px; background: var(--surface); }
.field-row > summary {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    cursor: pointer;
    list-style: none;
    font-size: 13.5px;
}
.field-row > summary::-webkit-details-marker { display: none; }
.field-row > summary:hover { background: var(--hover-bg); border-radius: 11px; }
.field-key { font-family: var(--font-mono); font-size: 13px; min-width: 96px; }
.field-prompt { flex: 1; color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field-flag { font-size: 12px; padding: 2px 9px; border-radius: 999px; background: var(--hover-bg); color: var(--text2); }
.field-flag.on { background: var(--accent-light); color: var(--accent); }
.field-edit {
    display: flex;
    gap: 14px;
    align-items: flex-end;
    flex-wrap: wrap;
    padding: 6px 14px 16px;
    border-top: 1px solid var(--border);
}
.field-edit .field { flex: 1 1 220px; }

.field-new {
    display: flex;
    gap: 12px;
    align-items: flex-end;
    flex-wrap: wrap;
    padding: 14px;
    border: 1px dashed var(--border-strong);
    border-radius: 11px;
    margin-top: 4px;
}
.field-new .field { flex: 1 1 180px; }

/* =============================================================================
   Status colours follow the theme.

   These were authored as fixed translucent tints over an assumed white or
   near-black background, so at any slider position in between they composited
   into mud and looked stuck. Each one is now mixed against the current card
   colour from the current status variable, which the slider is already moving.
   ============================================================================= */

.status-CREATED, .status-CANCELLED {
    background: color-mix(in srgb, var(--text2) 14%, var(--card));
    color: var(--text2);
}
.status-RUNNING {
    background: color-mix(in srgb, var(--warning) 18%, var(--card));
    color: color-mix(in srgb, var(--warning) 78%, var(--text));
}
.status-COMPLETED {
    background: color-mix(in srgb, var(--success) 18%, var(--card));
    color: color-mix(in srgb, var(--success) 78%, var(--text));
}
.status-FAILED {
    background: color-mix(in srgb, var(--danger) 16%, var(--card));
    color: color-mix(in srgb, var(--danger) 80%, var(--text));
}
/* Лимит на аккаунте: не работа и не провал — заявка стоит. Свой цвет затем,
   чтобы «стоит сутки» не читали как «в работе»; мешается с темой, как соседи. */
.status-HOLD {
    background: color-mix(in srgb, var(--accent) 16%, var(--card));
    color: color-mix(in srgb, var(--accent) 80%, var(--text));
}

.msg-ok {
    background: color-mix(in srgb, var(--success) 14%, var(--card));
    color: color-mix(in srgb, var(--success) 78%, var(--text));
}
.msg-err {
    background: color-mix(in srgb, var(--danger) 14%, var(--card));
    color: color-mix(in srgb, var(--danger) 80%, var(--text));
}
.msg-warn {
    background: color-mix(in srgb, var(--warning) 15%, var(--card));
    color: color-mix(in srgb, var(--warning) 78%, var(--text));
}

.pill { background: var(--accent-light); color: var(--accent); }
.btn-secondary:hover { background: var(--hover-bg); }
.theme-toggle:hover { color: var(--text); background: var(--hover-bg); }

/* The query bar's error ring was a fixed red alpha over an assumed surface. */
.qbar.invalid:focus-within { box-shadow: 0 0 0 4px color-mix(in srgb, var(--danger) 22%, transparent); }
.qerror {
    background: color-mix(in srgb, var(--danger) 10%, var(--card));
    border-color: color-mix(in srgb, var(--danger) 28%, var(--card));
}
.modal-icon.danger { background: color-mix(in srgb, var(--danger) 14%, var(--card)); }

/* Scrollbars and the tab pill were painted with fixed blacks too. */
.cs-list::-webkit-scrollbar-thumb, .ms-list::-webkit-scrollbar-thumb { background: var(--border-strong); }
[data-theme="dark"] .cs-list::-webkit-scrollbar-thumb { background: var(--border-strong); }
.tab-caret { box-shadow: 0 1px 4px color-mix(in srgb, var(--text) 12%, transparent); }
[data-theme="dark"] .tab-caret { box-shadow: 0 1px 4px color-mix(in srgb, #000 40%, transparent); }
nav button.active, .subtab.active { box-shadow: none; }

/* Inputs and controls must all read against the derived field fill, not against
   an assumed white. */
input[type="email"], input[type="text"], input[type="number"], input[type="password"],
input[type="date"], input[type="search"], textarea {
    background: var(--input-bg);
    color: var(--text);
    border-color: var(--border-strong);
}
.chk-box, .ms-box { background: var(--input-bg); border-color: var(--border-strong); }
.switch-track { background: var(--border-strong); }

/* A region being replaced stays on screen, slightly recessed, instead of being
   blanked to a spinner — the blank was the flash. */
.is-swapping {
    opacity: 0.45;
    pointer-events: none;
    transition: opacity 0.12s ease;
}
#settings-body, #fin-body { opacity: 1; }
#settings-body.page-in, #fin-body.page-in {
    animation: swapIn 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
@keyframes swapIn {
    from { opacity: 0.35; transform: translateY(3px); }
    to { opacity: 1; transform: none; }
}

/* =============================================================================
   Ledger tab: one row of controls, sized to what each field holds.
   ============================================================================= */

/* Two options do not need a full-width control. */
.seg-picker.seg-compact label { padding: 7px 14px; font-size: 13.5px; }
.seg-picker.seg-compact { align-self: flex-start; }

.entry-row {
    display: grid;
    grid-template-columns: max-content minmax(180px, 1fr) 130px 150px minmax(150px, 1fr) minmax(200px, 2fr) max-content;
    gap: 14px;
    align-items: end;
}
.entry-cell { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.entry-caption { font-size: 13px; font-weight: 600; color: var(--text2); white-space: nowrap; }
.entry-caption .req { color: var(--danger); }
.entry-cell input {
    padding: 10px 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
    width: 100%;
}
.entry-cell input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
.entry-amount input { font-variant-numeric: tabular-nums; }
.entry-submit { align-self: end; white-space: nowrap; }
@media (max-width: 1400px) {
    .entry-row { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
    .entry-submit { grid-column: 1 / -1; justify-self: start; }
}

.import-row { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.import-preview { font-size: 14px; font-variant-numeric: tabular-nums; }

.tag-info {
    font-size: 11.5px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--hover-bg);
    color: var(--text2);
    white-space: nowrap;
    margin-left: 6px;
}

/* The category menu must scroll rather than run past the card. */
.cs-list { max-height: 240px; overscroll-behavior: contain; }
.ms-list { overscroll-behavior: contain; }

/* =============================================================================
   Theme stops.

   A single light→dark ramp has to pass through mid-grey, where nothing can be
   both muted and readable — that is the flat, dead middle. So the slider walks a
   chain of designed themes instead: light, warm paper, cool paper, dusk, indigo,
   night. Each stop is a real palette with its own ink and accent, so every
   position on the slider looks deliberate, and the crossing from light to dark
   is squeezed into a short stretch between "cool" and "dusk".

   Only the anchors are declared. Borders, hovers, field fills and the accent
   tint are derived from these by theme.js, so a new stop is nine values.
   ============================================================================= */

[data-theme="warm"] {
    --bg: #f6f1e8;
    --card: #fffcf6;
    --text: #2a241d;
    --text2: #6b6053;
    --accent: #9d5713;
    --accent-hover: #b3681f;
    --success: #2f8f52;
    --warning: #c98a00;
    --danger: #c93a2f;
}

[data-theme="cool"] {
    --bg: #eaeff6;
    --card: #ffffff;
    --text: #1b2430;
    --text2: #566373;
    --accent: #0b6bcb;
    --accent-hover: #1a7ae0;
    --success: #1f8a4c;
    --warning: #b8860b;
    --danger: #cc3a30;
}

[data-theme="dusk"] {
    --bg: #232936;
    --card: #2c3342;
    --text: #e7ecf5;
    --text2: #aebbcd;
    --accent: #6aa8ff;
    --accent-hover: #86baff;
    --success: #3fc47a;
    --warning: #ffb648;
    --danger: #ff6b5e;
}

[data-theme="indigo"] {
    --bg: #191a2b;
    --card: #212338;
    --text: #e8e6f5;
    --text2: #b2afcc;
    --accent: #8b7bf0;
    --accent-hover: #a294f5;
    --success: #45c98a;
    --warning: #ffb03a;
    --danger: #ff6a72;
}

/* The track shows the chain it controls, so the slider looks like what it does.
   The hard edge at 46% is the light/dark crossing: there is nothing readable to
   blend through there, so the themes change over rather than fade. */
/* Split in two on purpose: a selector list containing a pseudo-element the
   engine does not know is dropped whole, so a shared rule reached neither. */
.theme-slider::-webkit-slider-runnable-track {
    background: linear-gradient(90deg,
        #f5f5f7 0%, #f6f1e8 20%, #eaeff6 36%, #eaeff6 46%,
        #232936 46%, #232936 56%, #191a2b 78%, #131316 100%);
}
.theme-slider::-moz-range-track {
    background: linear-gradient(90deg,
        #f5f5f7 0%, #f6f1e8 20%, #eaeff6 36%, #eaeff6 46%,
        #232936 46%, #232936 56%, #191a2b 78%, #131316 100%);
}

/* Name of the stop the slider is standing on. */
.theme-name {
    font-size: 12px;
    color: var(--text2);
    white-space: nowrap;
    max-width: 0;
    opacity: 0;
    overflow: hidden;
    transition: max-width 0.28s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}
.theme-control:hover .theme-name,
.theme-control:focus-within .theme-name { max-width: 110px; opacity: 1; margin-right: 10px; }

/* === Бот: канал и юзеры =====================================================

   Экраны про бота — это карточки состояния, а не формы: главное на них не то,
   что можно ввести, а то, что уже настроено. Поэтому состояние идёт первым
   блоком, ввод — вторым, а таблица — последней. */

.state-line {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}
.state-line .state-name { font-size: 17px; font-weight: 600; }
.state-line .state-sub { color: var(--text2); font-size: 13px; }

/* Выключенное состояние: тот же значок, но без акцентного цвета — «не настроено»
   не должно выглядеть как достижение. */
.pill-off { background: var(--hover-bg); color: var(--text2); }

/* Блок «ключ — значение» между состоянием и формой. */
.kv-card {
    background: var(--hover-bg);
    border-radius: 12px;
    padding: 16px 18px;
    margin-bottom: 18px;
    font-size: 13.5px;
}
.kv-card .kv { gap: 8px 18px; }
.kv-card .kv dd { word-break: break-all; }

/* Ссылка-заявка длинная и её копируют, а не читают. */
.link-value {
    font-family: var(--font-mono);
    font-size: 12px;
    cursor: pointer;
    border-bottom: 1px dashed var(--border);
}
.link-value:hover { color: var(--accent); border-bottom-color: var(--accent); }

/* Счётчик ожидающих прямо на вкладке.
   Место под него занято всегда, и пустая очередь показывается нулём: пока значок
   пропадал на разобранной заявке, кнопка каждый раз меняла ширину, а соседние
   вкладки прыгали следом. */
.subtab .tab-count {
    display: inline-block;
    margin-left: 7px;
    padding: 1px 7px;
    min-width: 22px;
    text-align: center;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: var(--accent);
    color: var(--on-accent);
}

/* === Подсказка на наведение =================================================

   Своя вместо родной: та появляется через секунду, не переносит длинный текст и
   в тёмной теме остаётся светлым прямоугольником. Слой один на страницу, живёт
   в body и позиционируется по вьюпорту, поэтому не растягивает таблицу и не
   уезжает под её горизонтальную прокрутку. */
.hint {
    position: fixed;
    left: 0;
    top: 0;
    z-index: 3500;
    max-width: min(360px, calc(100vw - 24px));
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--card);
    color: var(--text);
    box-shadow: var(--shadow-lg);
    font-size: 12.5px;
    line-height: 1.45;
    /* Причина бана — фраза целиком, а не строка с многоточием. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    opacity: 0;
    visibility: hidden;
    transform: translateY(3px);
    transition: opacity 0.13s ease, transform 0.13s ease, visibility 0.13s;
    pointer-events: none;
}
.hint.open { opacity: 1; visibility: visible; transform: none; }
/* Подсказка с заголовком — не ярлык к значку, а объяснение действия: шире,
   крупнее и с шапкой, по которой видно, о какой кнопке речь. */
.hint-rich {
    max-width: min(420px, calc(100vw - 24px));
    padding: 14px 16px;
    /* Крупнее обычной подсказки: это не ярлык к значку, а условие, из-за
       которого человек стоит перед погашенной кнопкой и не знает, что делать. */
    font-size: 16px;
    line-height: 1.45;
}
.hint-title {
    display: block;
    margin-bottom: 6px;
    font-size: 14.5px;
    font-weight: 700;
    color: var(--text2);
}
/* Стрелка: две наложенные — нижняя рисует границу, верхняя закрывает её фоном. */
.hint::before,
.hint::after {
    content: '';
    position: absolute;
    left: var(--hint-arrow, 50%);
    margin-left: -6px;
    border: 6px solid transparent;
}
.hint::before { top: 100%; border-top-color: var(--border-strong); }
.hint::after { top: calc(100% - 1px); border-top-color: var(--card); }
.hint.hint-below::before { top: auto; bottom: 100%; border-top-color: transparent; border-bottom-color: var(--border-strong); }
.hint.hint-below::after { top: auto; bottom: calc(100% - 1px); border-top-color: transparent; border-bottom-color: var(--card); }

/* Носитель подсказки: точечное подчёркивание — единственное, что говорит «здесь
   есть что почитать». Подчёркивание, а не рамка снизу: носитель обычно значок со
   скруглением и заливкой, и линия через всю его ширину читается как поломка. */
.has-hint {
    cursor: help;
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
.has-hint:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Тот же курсор, но без подчёркивания: там, где носителей десятки подряд —
   названия строк бота, подписи языков, — пунктир превращает список в рябь, а
   сама подсказка нужна изредка. */
.has-hint-quiet { cursor: help; }

/* Подпись настройки, за которой стоит объяснение. Спрашивает о себе значок, а
   не подчёркивание: настроек в карточке три десятка, и пунктир под каждой
   превращает форму в рябь. Значок приглушён и подсвечивается вместе с подписью
   — до наведения он не должен спорить с самой подписью за внимание. */
/* Значок течёт вместе с текстом, а не стоит вторым элементом строки: подпись в
   карточке бывает в две строки, и во flex-контейнере он уезжал к правому краю
   первой, отрываясь от слова, к которому относится. */
.field-name { cursor: help; }
.tip-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    margin-left: 4px;
    vertical-align: middle;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    color: var(--text2);
    font-size: 10px;
    font-style: normal;
    font-weight: 700;
    line-height: 1;
}
.field-name:hover .tip-mark,
.field-name:focus-visible .tip-mark {
    border-color: var(--accent);
    color: var(--accent);
}
.field-name:focus-visible { outline: none; }

/* Шапка над таблицей: чей это канал и сколько ждут. */
.table-head-line {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 14px;
    font-size: 13.5px;
    color: var(--text2);
}
.table-head-line b { color: var(--text); font-weight: 600; }

/* Обратный отсчёт брони: пока он идёт, товар удержан и никому больше не виден. */
.ttl { font-family: var(--font-mono); font-size: 12.5px; font-weight: 600; }
.ttl-soon { color: var(--danger); }
.ttl-out { color: var(--text2); }

/* Опасный пункт в меню действий: цвет появляется только на наведении, иначе
   красная строка в списке читается как «сломано», а не «осторожно». */
.cs-option.cs-danger { color: var(--danger); }
.cs-option.cs-danger:hover { background: rgba(255, 59, 48, 0.12); }

/* Ячейка «ник + состояние»: без явного зазора инпут и значок под ним слипаются
   в одно пятно. */
.stacked-cell { display: flex; flex-direction: column; gap: 8px; align-items: flex-start; }
.stacked-cell input { width: 100%; min-width: 150px; }

/* Состояние строки: несколько значков в столбик вместо трёх отдельных колонок,
   каждая из которых несла одно слово. */
.badge-stack { display: flex; flex-wrap: wrap; gap: 6px; }

/* Настройки одной вкладки, но разные по смыслу: между ними нужна граница, а не
   пустая строка. */
.settings-block + .settings-block { border-top: 1px solid var(--border); margin-top: 32px; padding-top: 26px; }
.block-actions { margin-top: 16px; }

/* Пустая очередь — тусклый ноль: это объём, а не повод что-то сделать. Селектор
   повторён с .subtab, иначе акцентная заливка счётчика перебивает его по весу. */
.tab-count-soft,
.subtab .tab-count-soft { background: var(--hover-bg); color: var(--text2); }
.subtab.active .tab-count-soft { background: var(--accent-light); color: var(--accent); }

/* Неразобранная попытка передать контакт — не объём, а долг: она лежит, пока по
   ней не решили. Поэтому красным, а не акцентным: акцентных цифр на полосе
   несколько, и среди них эта потерялась бы. */
.tab-count-alert,
.subtab .tab-count-alert { background: var(--danger); color: #fff; }
.subtab.active .tab-count-alert { background: var(--danger); color: #fff; }

/* Сводка по группам: плитка — это и цифра, и фильтр. */
.group-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 14px; }
.group-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 8px;
    padding: 7px 12px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
    transition: all 0.15s ease;
}
.group-chip:hover { border-color: var(--accent); color: var(--accent); }
.group-chip.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.group-chip-count { font-weight: 700; font-size: 12px; opacity: 0.75; }

/* Журнал покупок: строка разворачивается в состав заказа. */
.jr-row { cursor: pointer; }
.jr-items { display: none; }
.jr-items.open { display: table-row; }
.jr-items td { background: var(--hover-bg); padding-top: 10px; padding-bottom: 12px; }
.item-chip {
    display: inline-flex;
    align-items: baseline;
    gap: 7px;
    margin: 3px 6px 3px 0;
    padding: 5px 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--card);
    font-size: 12.5px;
    font-family: var(--font-mono);
}
.item-chip-id { color: var(--accent); font-weight: 700; }

/* Заголовок блока настроек: раздел, а не подпись к полю. */
.block-title { font-size: 17px; font-weight: 650; margin: 0 0 4px; }
.block-sub { color: var(--text2); font-size: 13px; margin-bottom: 16px; }

/* Форма записи: подписи над полями в одну линию, кнопка — своей строкой. Кнопка
   в общем ряду с полями вставала то по низу подписи, то по низу поля, и ряд
   выглядел разъехавшимся. */
.entry-head { display: flex; align-items: center; gap: 12px; margin-bottom: 20px; }
.entry-head h2 { margin: 0; }
.entry-head .btn { margin-left: auto; }
.entry-actions { display: flex; gap: 8px; margin-top: 18px; }
.entry-row { align-items: end; }
.entry-cell .cs, .entry-cell .cs-trigger { width: 100%; }
/* Одинаковая высота у поля, списка и переключателя — иначе ряд «прыгает». */
.entry-cell input, .entry-cell .cs-trigger { min-height: 38px; }
.entry-cell .seg-picker { min-height: 38px; align-items: center; }

/* Узкие колонки: роль и телеграм не должны занимать половину таблицы. */
.col-narrow { width: 1%; }
.col-narrow .cs-trigger { min-width: 120px; }
.col-telegram { width: 240px; }
.col-access { width: 1%; }
/* Телеграм — одна строка, как остальные ячейки: цветная точка статуса + поле с
   кнопкой (или ник с «Отвязать»). Прежняя раскладка вешала статус второй
   строкой и раздувала весь ряд таблицы. Текст статуса ушёл в title точки. */
.tg-cell { display: flex; align-items: center; gap: 7px; }
.tg-cell .wa-pick { flex: 1 1 auto; width: auto; min-width: 0; }
.tg-nick { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;
    white-space: nowrap; font-size: 13px; }
.tg-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; }
.tg-dot-ok { background: #34c759; }
.tg-dot-wait { background: #ff9f0a; }
.tg-dot-off { background: var(--border-strong); }
.tg-link-row { display: flex; gap: 6px; align-items: center; width: 100%; }
.col-telegram .tg-link-row input {
    flex: 1 1 auto; width: auto; min-width: 0;
    padding: 5px 10px; font-size: 13px; border-radius: 8px;
}
.tg-link-row .btn { flex: 0 0 auto; padding: 5px 10px; font-size: 12px; }

/* Полоса вкладок: разделы слева, область видимости справа. */
.tab-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.tab-row .subtabs { margin-bottom: 20px; }
.subtabs-right { margin-left: auto; }

/* Значение перечислимой колонки в подсказке — такая же кнопка, как операнд. */
.chip-value { font-family: var(--font-mono); font-size: 11.5px; }

/* === Тексты бота ===========================================================

   Раздел сворачивается, но остаётся на экране строкой — видно, что внутри ещё
   есть. Запись идёт кнопкой: сохранение по уходу из поля выглядело так, будто
   панель ничего не сделала, а перевод правят по три поля подряд.

   Заливок «на глаз» здесь нет. Тёмные темы отличаются не только яркостью фона,
   и любой захардкоженный серый в них выглядит грязным пятном: заливка берётся
   из --hover-bg текущей темы, разделение — граница и отступ. */

.tx-search { min-width: 220px; }

.tx-section {
    border: 1px solid var(--border);
    border-radius: 14px;
    margin-bottom: 12px;
    background: var(--card);
    /* Подсветка шапки не должна вылезать за скругление карточки. */
    overflow: hidden;
}
/* Разделы, у которых внутри форма, а не список строк: своя рамка снимает
   отступы, которые есть у строк текстов. */
.tx-section-pad { padding: 0 16px 16px; }

/* Заголовок над списком текстов: три разных раздела подряд без него читаются
   как один длинный экран. */
.tx-heading {
    font-size: 15px;
    font-weight: 650;
    margin: 22px 0 10px;
}

/* Шапка — настоящая кнопка: её и жмут, и доходят до неё табом. */
.tx-section-head {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 13px 16px;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
    user-select: none;
    transition: background-color 0.15s ease;
}
.tx-section-head:hover { background: var(--hover-bg); }
.tx-section-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tx-section-title { font-size: 14.5px; font-weight: 650; color: var(--text); letter-spacing: -0.01em; }
/* Счётчик строк стоит вплотную к названию: это его свойство, а не отдельная
   колонка у правого края. */
.tx-section-count {
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--hover-bg);
    color: var(--text2);
    font-size: 11.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.tx-section-head:hover .tx-section-count { background: var(--card); }
/* Несохранённое видно и на свёрнутом разделе — иначе о правке узнают по тому,
   что бот отвечает по-старому. */
.tx-section-dirty {
    margin-left: auto;
    color: var(--warning);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}
.tx-caret {
    width: 7px;
    height: 7px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    color: var(--text2);
    transform: rotate(45deg);
    transition: transform 0.2s ease, color 0.15s ease;
    flex-shrink: 0;
    /* Уголок центрируется по своей диагонали, а не по рамке. */
    margin: -3px 2px 0 0;
}
.tx-section-head:hover .tx-caret { color: var(--accent); }
.tx-section.collapsed .tx-caret { transform: rotate(-45deg); margin: 0 2px 0 0; }
/* Содержимое отделено от заголовка линией, а не заливкой. */
.tx-section-body {
    border-top: 1px solid var(--border);
    padding: 4px 18px 8px;
}
.tx-section.collapsed .tx-section-body { display: none; }

/* Строка — не карточка в карточке: рамка вокруг каждой из тридцати строк
   превращает раздел в клетчатое полотно. Разделяет их линия. */
.text-row { padding: 16px 0 18px; }
.text-row + .text-row { border-top: 1px solid var(--border); }

/* Шапка строки: название, состояние, кнопка. Ключ вроде msg.request_created
   отсюда убран — это адрес строки в коде, а не её имя; он остался подсказкой на
   названии. */
.text-row-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 8px;
    min-height: 30px;
}
.text-row-title {
    flex: 1;
    min-width: 0;
    font-size: 15px;
    font-weight: 600;
    color: var(--text);
    letter-spacing: -0.01em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Что в текст подставляет бот. Стоит над полями, а не сбоку от названия: это
   про то, как править текст, а не про то, что это за строка. */
.text-row-vars {
    margin-bottom: 8px;
    color: var(--text2);
    font-size: 12.5px;
    line-height: 1.4;
}
/* «Не сохранено» и «сохранено» стоят на одном месте и не двигают кнопку: это
   два значения одного поля, а не два поля. */
.text-row-state {
    position: relative;
    flex-shrink: 0;
    width: 104px;
    height: 16px;
}
.text-row-dirty,
.text-row-saved {
    position: absolute;
    right: 0;
    top: 0;
    font-size: 11.5px;
    font-weight: 600;
    line-height: 16px;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.2s ease;
}
.text-row-dirty { color: var(--warning); }
.text-row-saved { color: var(--success); }
.text-row.dirty .text-row-dirty { opacity: 1; }
.text-row.saved .text-row-saved { opacity: 1; }
.text-row.saved .text-row-dirty { opacity: 0; }
/* Кнопка гаснет, пока править нечего: так видно, что правка уже записана. */
.tx-save { flex-shrink: 0; }
.tx-save:disabled { opacity: 0.55; cursor: default; }

/* Русский — исходник, поэтому на всю ширину; переводы под ним рядом. */
.tx-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
.tx-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tx-field-wide { grid-column: 1 / -1; }
.tx-field label { font-size: 12px; font-weight: 600; color: var(--text2); }
/* Поле ровно под текст: высоту textarea считает JS по содержимому, поэтому
   здесь только то, от чего этот расчёт отталкивается — одна строка и никаких
   собственных отступов сверх padding. */
.tx-field input, .tx-field textarea {
    width: 100%;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--input-bg);
    color: var(--text);
    font: inherit;
    font-size: 14.5px;
    line-height: 1.5;
}
.tx-field textarea {
    display: block;
    /* Высоту ведёт JS по содержимому, поэтому уголок ресайза здесь — лишняя
       рябь в углу каждого из ста восьмидесяти полей. */
    resize: none;
    /* Ниже потолка полосы нет — высота и так по тексту; выше — появляется, иначе
       одно длинное сообщение выдавило бы с экрана весь остальной список. */
    overflow-y: auto;
    min-height: 0;
}
.tx-field input:focus, .tx-field textarea:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
    outline: none;
}
.tx-field input:disabled, .tx-field textarea:disabled { opacity: 0.6; cursor: not-allowed; }

@media (max-width: 700px) {
    .tx-fields { grid-template-columns: 1fr; }
    /* Место под «сохранено» на телефоне съедает название строки: оно важнее, а
       о несохранённом и так говорит горящая кнопка. */
    .text-row-state { display: none; }
}

/* === Шапка: плавающие контролы стоят в её строке ===========================
   Они fixed, потому что переживают экраны логина и привязки 2FA, — но по
   вертикали должны совпадать со строкой шапки, а не висеть над ней. */
#global-controls {
    top: 0;
    right: 0;
    height: 52px;
    padding-right: max(16px, calc((100vw - min(1780px, 95vw)) / 2 + 24px));
}
/* Заголовок и «кто я» не заезжают под контролы: место под них зарезервировано. */
.header-inner { padding-right: 330px; min-width: 0; }
.header-inner h1 { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
#whoami { white-space: nowrap; flex-shrink: 0; }

@media (max-width: 900px) {
    .header-inner { padding-right: 250px; }
}
@media (max-width: 700px) {
    /* На телефоне контролы живут своей строкой сверху — см. правило выше. */
    #global-controls { top: var(--safe-t); height: 54px; padding-right: calc(10px + var(--safe-r)); }
    .header-inner { padding-right: 12px; }
}

/* === Заявки «Рес» ==========================================================
   Карточка занимает страницу целиком, поэтому здесь нет ничего про наложения:
   только сетка «ключ — значение», галерея кадров и лента переписки. */

/* Строку таблицы открывают кликом по ней самой; кнопка «Открыть» остаётся для
   тех, кто ищет глазами, куда нажать. */
.row-clickable { cursor: pointer; }
.row-clickable:hover td { background: var(--hover-bg); }

.row-flags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }

/* Пометки — контуром, а не заливкой: заливка есть у статуса, и рядом с ним две
   одинаковые плашки читались как два статуса. */
.pill-soft { background: transparent; border: 1px solid var(--border-strong); color: var(--text2); }
/* Тревожная: клиент чего-то ждёт от нас — цвет тот же, что у предупреждений. */
.pill-warn { background: transparent; border: 1px solid var(--warning); color: var(--warning); }

/* --- карточка заявки -------------------------------------------------------
   Экран отвечает на один вопрос: что с этой заявкой делать сейчас. Порядок
   сверху вниз — кто это (шапка), чем ходить (панель действий), с чем работать
   (данные и лента), что прислал клиент (кадры).

   Карточка не лежит в общей .card: у неё свои панели, и рамка вокруг рамки —
   это третий уровень вложенности на один экран. */
.rq { display: flex; flex-direction: column; gap: 14px; }

/* Панель — единица этого экрана: заголовок, содержимое, своя рамка. */
.rq-panel {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card);
    padding: 16px 18px 18px;
    min-width: 0;
}
.rq-panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 12px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--border);
}
.rq-panel-head h3 { margin: 0; font-size: 15px; font-weight: 650; }
.rq-panel-note { font-size: 12.5px; color: var(--text2); }
.rq-panel-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.rq-empty { margin: 6px 0; font-size: 14px; }

/* Живой отсчёт: моноширинный, иначе цифры дёргают соседний текст каждую
   секунду. Последняя минута — цветом предупреждения: до неё срок читают, в
   неё по нему решают. */
.rq-left { font-variant-numeric: tabular-nums; font-family: var(--font-mono, monospace); }
.rq-left-soon { color: var(--warning); font-weight: 650; }

/* Шапка: слева чем заявка называется и в каком состоянии, справа деньги и у
   кого она на руках. Эти два вопроса задают порознь — первый читают, второй
   сверяют, — поэтому и стоят они по разным краям. */
.rq-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card);
    padding: 14px 18px;
}
.rq-head-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; min-width: 0; }
.rq-title { display: flex; align-items: baseline; gap: 8px; }
.rq-title h2 { margin: 0; font-size: 22px; letter-spacing: 0.02em; }
/* Тип заявки — плашка, а не приписка. Лок и рес это разная работа, и вопрос
   «что это» задают раньше, чем дочитывают номер: мелкий серый текст рядом с
   22-пиксельным номером на этот вопрос не отвечал вовсе. */
.rq-type {
    font-size: 13.5px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    padding: 4px 11px;
    border-radius: 999px;
    border: 1px solid var(--border-strong);
    color: var(--text2);
    white-space: nowrap;
}
.rq-type-lock {
    border-color: color-mix(in srgb, var(--warning) 60%, var(--border));
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    color: var(--warning);
}
.rq-type-res {
    border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    color: var(--accent);
}
.rq-marks { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.rq-head-right { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
/* Цена — единственное число на экране, которое называют вслух. Крупно и один
   раз: в списке фактов она терялась между почтой и номером. */
.rq-price { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; }
.rq-price-value { font-size: 26px; font-weight: 650; letter-spacing: -0.01em; }
.rq-price-name { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text2); }
.rq-head-facts { display: flex; gap: 18px; margin: 0; flex-wrap: wrap; }
.rq-head-facts dt { font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text2); }
.rq-head-facts dd { margin: 2px 0 0; font-size: 13.5px; }

/* Панель действий: слева «что дальше», посередине инструменты, справа отказ.
   Место кнопки означает, что она делает, — это читается раньше подписи, поэтому
   у зон есть подписи и разделители, а порядок не меняется никогда. */
.rq-actions {
    /* Три равные колонки. Раньше ширину задавало содержимое, и зоны
       разъезжались на каждой заявке: где кнопок меньше — заголовок висел над
       пустотой, где больше — они переносились в столбик. Равные колонки держат
       границы зон на одном месте от заявки к заявке. */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    align-items: start;
    gap: 0;
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    padding: 12px 0;
}
.rq-zone { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding: 0 16px; }
/* Заголовок зоны отбит от кнопок линейкой во всю ширину колонки: без неё три
   подписи читались как ещё один ряд мелких кнопок. */
.rq-zone-name {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text2);
    padding-bottom: 7px;
    border-bottom: 1px solid var(--border);
}
/* Кнопки в ряд во всех зонах. Вертикальный столбик в одной из трёх читался как
   другой вид кнопок, хотя это те же кнопки. */
.rq-zone-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rq-zone-tools { border-left: 1px solid var(--border); border-right: 1px solid var(--border); }
/* Отказ больше не прижимается вправо: край зоны и так виден по линейке, а
   выравнивание по правому краю ставило подпись зоны не туда, где стоят две
   соседних. */

/* Шаг, который сейчас нельзя: не спрятан и не просто погашен, а с подписью
   почему. Спрятанную кнопку ищут, погашенную без объяснения — ждут неизвестно
   чего; подпись превращает и то, и другое в понятное «сделай сначала вот это». */
/* Не `disabled`, а вид выключенной: настоящая disabled-кнопка не получает ни
   наведения, ни нажатия, и объяснить, чего не хватает, уже не может. */
.rq-step-off { opacity: 1; color: var(--text2); border-style: dashed; cursor: help; }
.rq-step-off:hover { border-color: var(--border-strong); color: var(--text); }

.rq-waiting { display: inline-flex; align-items: center; gap: 8px; font-size: 13.5px; color: var(--text2); }
.rq-waiting::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--warning);
}
@media (max-width: 1200px) {
    .rq-actions { grid-template-columns: minmax(0, 1fr); gap: 14px; }
    .rq-zone-tools {
        border-left: none;
        border-right: none;
    }
}

/* Ряд чипсов исхода: он живёт под панелью действий и только минуту после
   нажатия «Прозвон». Своей строкой, а не внутри зоны: зоны — про то, что можно
   сделать вообще, а это вопрос про уже сделанное, и место в колонке
   инструментов он занимал бы постоянно. */
.rq-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 10px 14px;
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
    border-radius: var(--radius);
    background: var(--accent-light);
}
.rq-chips-name { font-size: 13px; color: var(--text2); margin-right: 2px; }
.rq-chips .chip-mute { color: var(--text2); }

/* Кнопка, которую пора нажать. Мигает медленно и только рамкой: кнопка,
   прыгающая или меняющая цвет заливки, читается как ошибка, а не как подсказка,
   и через минуту от неё прячут глаза. */
.btn-nudge {
    border-color: color-mix(in srgb, var(--warning) 60%, var(--border));
    animation: nudge 2.4s ease-in-out infinite;
}
@keyframes nudge {
    0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--warning) 45%, transparent); }
    50%      { box-shadow: 0 0 0 5px color-mix(in srgb, var(--warning) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) {
    /* Мигание — подсказка, а не смысл: без него остаётся рамка, и она говорит
       то же самое. */
    .btn-nudge { animation: none; }
}

/* Этапы работы: строки «когда — что — кто». Своим списком, а не таблицей:
   подпись сотрудника бывает длинной, и колонки разъезжались бы от заявки к
   заявке. */
.rq-steplog { margin: 0; padding: 2px 0; list-style: none; }
.rq-steplog-row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
    padding: 5px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--border);
}
.rq-steplog-row:last-child { border-bottom: none; }
.rq-steplog-at { color: var(--text2); font-family: var(--font-mono); font-size: 12px; }
.rq-steplog-what { color: var(--text); }
.rq-steplog-who { margin-left: auto; color: var(--text2); font-size: 12px; }

/* Данные и переписка стоят рядом: сотрудник читает письмо и пишет клиенту в
   один заход, а не листает страницу между ними. Кадры — во всю ширину под
   ними: их смотрят, когда уже прочитали и то, и другое. */
.rq-body {
    display: grid;
    /* Лента шириной с чат, а не с половину монитора: длина реплики в ней — две
       строки, и растянутая на 900px панель это 900px, из которых читают 300.
       Свободное место забирают данные: там строки длинные — почты, номера,
       заметки — и им ширина идёт впрок. Потолок всё же подняли: в письме с
       кодом строка длиннее реплики, и в 29rem оно ломалось посреди кода. */
    grid-template-columns: minmax(0, 1fr) minmax(360px, 34rem);
    gap: 14px;
    /* Колонки равной высоты: лента тянется под данные слева, и под ней не
       остаётся пустого поля в половину экрана. Прокручивается она внутри себя. */
    align-items: stretch;
}
.rq-col { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.rq-shots { grid-column: 1 / -1; }
@media (max-width: 1100px) {
    .rq-body { grid-template-columns: minmax(0, 1fr); }
}

/* Факты строками «подпись — значение», а не карточками в сетке: их читают
   сверху вниз, и колонка выравнивает значения по одному краю — глаз находит
   почту, не перечитывая подписи. */
.rq-facts { margin: 0; display: flex; flex-direction: column; }
.rq-fact {
    display: grid;
    grid-template-columns: 8.5rem minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    padding: 7px 0;
    border-top: 1px solid var(--border);
}
.rq-fact:first-child { border-top: none; padding-top: 0; }
.rq-fact dt { font-size: 12.5px; color: var(--text2); }
.rq-fact dd { margin: 0; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
/* Пометка «нет доступа» рядом со значением — мельче обычной плашки: она про то
   же поле, а не про заявку целиком. */
.rq-fact .pill { font-size: 11px; padding: 1px 8px; }
/* Значение — то, ради чего сюда смотрят: крупнее подписи и выделяется мышью
   целиком, потому что его переписывают в чужую форму. */
.rq-value { font-size: 14.5px; overflow-wrap: anywhere; user-select: all; }
/* Контакт — то, что переписывают руками в чужую форму, и опечатка в нём стоит
   заявки. Поэтому крупно и моноширинно: единица и буква l здесь должны
   отличаться. Нажатие копирует — кнопка рядом с каждым контактом весила
   столько же, сколько сам контакт. */
.rq-fact-big .rq-value {
    font-size: 16.5px;
    font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    letter-spacing: -0.01em;
    cursor: copy;
}
.rq-fact-big .rq-value:hover { color: var(--accent); }
/* --- что выдали ------------------------------------------------------------
   Выданные почта и номер — не такие же строки фактов, как остальные: их не
   читают, а с ними работают. Их переписывают в чужую форму, на них ждут код, по
   ним идут разбираться, когда код не пришёл. Строкой «подпись — значение» это
   выражалось хуже всего: контакт стоял тем же кеглем, что дата создания, а
   пришедшее на него висело отдельной строкой ниже — связать их можно было
   только по порядку. */
/* По верху, а не в растяжку: у почты и номера приходит разное, и карточка с
   короткой SMS не должна тянуться до высоты карточки с письмом. */
.rq-issued {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px;
    align-items: start;
}
@media (max-width: 1400px) {
    .rq-issued { grid-template-columns: minmax(0, 1fr); }
}
.rq-issued-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
    padding: 9px 11px 10px;
    border: 1px solid var(--border);
    border-radius: 11px;
    background: color-mix(in srgb, var(--text) 2.5%, transparent);
}
/* Полоса слева отвечает на «это про почту или про номер» до чтения подписи:
   карточки стоят рядом и различаются только текстом внутри. */
.rq-issued-mail { border-left: 3px solid color-mix(in srgb, var(--accent) 55%, transparent); }
.rq-issued-sms { border-left: 3px solid color-mix(in srgb, var(--warning) 55%, transparent); }
.rq-issued-head { display: flex; align-items: center; gap: 8px; }
.rq-issued-label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text2);
}
/* Дверь в историю — рядом с контактом, а не в подвале карточки: её открывают
   из того же вопроса, из которого смотрят на сам контакт. */
.rq-issued-hist {
    margin-left: auto;
    flex: none;
    padding: 1px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text2);
    font: inherit;
    font-size: 11.5px;
    cursor: pointer;
}
.rq-issued-hist:hover { border-color: var(--accent); color: var(--accent); }
/* Сам контакт — крупно и моноширинно: его переписывают руками, и единица с
   буквой l здесь обязаны отличаться. Нажатие копирует. */
.rq-issued-value {
    align-self: flex-start;
    max-width: 100%;
    padding: 0;
    border: none;
    background: none;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 16px;
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.3;
    text-align: left;
    overflow-wrap: anywhere;
    cursor: copy;
}
.rq-issued-value:hover { color: var(--accent); }
.rq-issued-none { margin: 0; font-size: 12.5px; color: var(--text2); }
/* Последнее пришедшее внутри карточки — без своей рамки и заливки: рамка в
   рамке на два поля читается как ещё одна панель, а это всё та же выдача.
   Отбивает его волосяная линия — ровно столько, сколько нужно, чтобы отделить
   сообщение от контакта, на который оно пришло. */
.rq-issued-card .rq-msg {
    padding: 7px 0 0;
    border: none;
    border-top: 1px solid var(--border);
    border-radius: 0;
    background: none;
}
/* Шапка сообщения в карточке — в одну строку: длинный адрес отправителя
   уносил время на второй этаж, и два поля выдачи переставали читаться парой.
   Адрес обрезается многоточием — его тут узнают, а не переписывают. */
.rq-issued-card .rq-msg-head { margin-bottom: 3px; gap: 6px; flex-wrap: nowrap; }
.rq-issued-card .rq-msg-from {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rq-issued-card .rq-msg-subject { font-size: 12.5px; }
.rq-issued-card .rq-msg .bubble-body { font-size: 13px; }
.rq-issued-card .rq-msg-code { font-size: 14.5px; padding: 2px 9px; margin: 0 0 4px; }
.rq-issued-card .rq-msg-at { font-size: 11px; }

/* --- сообщение карточкой ---------------------------------------------------
   Одна разметка на два места: последнее пришедшее в карточке заявки и вся
   история в окне. Раньше письмо в панели данных было голым текстом с датой под
   ним — тема терялась в теле, код искали глазами, а «SMS это или письмо»
   читалось только по подписи слева, которой в истории нет вовсе. */
.rq-msg {
    display: block;
    min-width: 0;
    padding: 8px 10px 9px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card);
}
.rq-msg-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; margin-bottom: 4px; }
.rq-msg-head .bubble-tag { margin-left: 0; }
.rq-msg-from {
    font-size: 12.5px;
    font-weight: 650;
    color: color-mix(in srgb, var(--text) 78%, var(--text2));
    overflow-wrap: anywhere;
}
/* Время уходит вправо и не спорит с отправителем за место. */
.rq-msg-at {
    margin-left: auto;
    flex: none;
    font-size: 11.5px;
    color: var(--text2);
    font-variant-numeric: tabular-nums;
}
/* Почему строки нет в ленте — сказано на ней самой. Молчание здесь читалось бы
   как «лента врёт», а причин ровно две и обе назывались. */
.rq-msg-mark {
    padding: 1px 7px;
    border-radius: 999px;
    border: 1px dashed var(--border-strong);
    font-size: 11px;
    color: var(--text2);
    white-space: nowrap;
}
.rq-msg-mark-off {
    border-style: solid;
    border-color: color-mix(in srgb, var(--danger) 40%, transparent);
    color: color-mix(in srgb, var(--danger) 78%, var(--text));
}
.rq-msg-subject { font-size: 13.5px; font-weight: 650; margin-bottom: 3px; overflow-wrap: anywhere; }
.rq-msg .bubble-body { font-size: 14px; }
/* Код — то, ради чего сообщение открывают: отдельной кнопкой и до текста, а не
   где-то в его середине. */
.rq-msg-code {
    display: inline-block;
    margin: 1px 0 5px;
    padding: 3px 11px;
    border-radius: 8px;
    border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 16px;
    font-weight: 700;
    letter-spacing: 0.08em;
    cursor: pointer;
}
.rq-msg-code:hover { border-style: solid; border-color: var(--accent); }
.rq-fact-big dt { font-size: 12px; }
/* «Доступна» рядом с «нет доступа» — один и тот же вопрос, и ответ на него
   должен читаться одинаково быстро в обе стороны. */
.pill-ok {
    background: transparent;
    border: 1px solid color-mix(in srgb, var(--series-profit, #3aa76d) 60%, var(--border));
    color: var(--series-profit, #3aa76d);
}
.rq-fact a { color: var(--accent); text-decoration: none; }
.rq-fact a:hover { text-decoration: underline; }
/* Архив (потерянные контакты) приглушён: он нужен для сверки, а не для работы. */
.rq-fact-dim .rq-value { font-size: 13px; color: var(--text2); user-select: text; }
.rq-copy {
    flex: none;
    padding: 1px 7px;
    border: 1px solid var(--border);
    border-radius: 7px;
    background: var(--surface);
    color: var(--text2);
    font-size: 12.5px;
    line-height: 1.4;
    cursor: pointer;
}
.rq-copy:hover { border-color: var(--accent); color: var(--accent); }

/* Подзаголовок внутри панели: «что выдали» и «заявок по аккаунту» — это не
   отдельные панели, а продолжение того же чтения про клиента. */
.rq-subhead {
    margin: 16px 0 8px;
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text2);
}

/* Счётчик по аккаунту — плитками: вопрос к нему один, «много или нет», и
   крупное число отвечает на него быстрее строки «2 (лок 1, рес 1)». */
.rq-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
.rq-stat {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    min-width: 0;
}
.rq-stat-value { font-size: 19px; font-weight: 650; line-height: 1.1; }
.rq-stat-name { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text2); }
.rq-stat-note { font-size: 11.5px; color: var(--text2); overflow-wrap: anywhere; }

/* Ник клиента: нажатие ведёт в разговор внутри панели, стрелка рядом — в личку
   телеграма. Ник выглядит ссылкой, потому что ссылкой и был, и менять его вид
   заодно с адресом значило бы, что человек его не узнает. */
.tg-handle { display: inline-flex; align-items: baseline; gap: 4px; }
.tg-open {
    border: none;
    background: none;
    padding: 0;
    color: var(--accent);
    font: inherit;
    cursor: pointer;
}
.tg-open:hover { text-decoration: underline; }
/* Стрелка тусклее ника: выход наружу — редкий ход, а места занимает столько же. */
.tg-away {
    color: var(--text2);
    font-size: 11px;
    line-height: 1;
    text-decoration: none;
}
.tg-away:hover { color: var(--accent); }

/* Заявки карточками: блок «Заявки по аккаунту» и модалка мессенджера. Три в
   ряд — столько влезает и в панель карточки, и в модалку над разговором, и
   девять на странице читаются одним взглядом, без прокрутки. */
.rq-cards {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 8px;
    margin-top: 10px;
}
.rq-mini {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 4px;
    padding: 9px 11px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    color: var(--text);
    font: inherit;
    text-align: left;
    min-width: 0;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.rq-mini:hover { border-color: var(--accent); background: var(--accent-light); }
/* Номер и статус в строку, пока влезают: в узкой колонке модалки статус уходит
   под номер, а не выдавливает сетку. */
.rq-mini-head {
    display: flex;
    align-items: center;
    gap: 4px 8px;
    justify-content: space-between;
    flex-wrap: wrap;
}
.rq-mini-number { font-size: 13px; font-weight: 650; }
.rq-mini-when { font-size: 11.5px; color: var(--text2); }
/* Имя работяги обрезаем строкой: карточек три в ряд, и длинный ник ломал бы
   сетку целиком ради одной ячейки. */
.rq-mini-who {
    font-size: 11.5px;
    color: var(--text2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rq-cards-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 10px;
    font-size: 12.5px;
}
.rq-cards-note { margin-top: 10px; font-size: 12.5px; }

/* Сворачиваемый блок внутри панели. Свёрнутая строка отвечает на свой вопрос
   целиком — «семь заявок», «в работе 18 минут», — поэтому раскрывают её редко,
   и закрытое состояние здесь основное. */
.rq-fold { border-top: 1px solid var(--border); margin-top: 12px; }
.rq-fold-head {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 10px 0 2px;
    background: none;
    border: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.rq-fold-title { font-size: 12.5px; font-weight: 650; }
.rq-fold-note { font-size: 12.5px; color: var(--text2); margin-left: auto; }
.rq-fold-caret {
    width: 0;
    height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    opacity: 0.55;
    transition: transform 0.12s ease;
}
.rq-fold.open > .rq-fold-head .rq-fold-caret,
.rq-group:not(.closed) .rq-fold-caret { transform: rotate(90deg); }
.rq-fold-body { display: none; padding: 10px 0 4px; }
.rq-fold.open > .rq-fold-body { display: block; }

/* Барабан отметок: точка шага и линия к следующему рисуются на самом элементе.
   Отдельные узлы ради двух кружков — это разметка, которую потом нечем
   объяснить. */
.rq-steps { margin: 0; padding: 2px 0 2px 4px; list-style: none; }
.rq-step {
    position: relative;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 5px 0 5px 18px;
    font-size: 13px;
}
.rq-step::before {
    content: '';
    position: absolute;
    left: 2px;
    top: 9px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    border: 1.5px solid var(--border-strong);
    background: var(--card);
}
.rq-step:not(:last-child)::after {
    content: '';
    position: absolute;
    left: 5.25px;
    top: 17px;
    bottom: -2px;
    width: 1.5px;
    background: var(--border);
}
.rq-step-past::before { background: var(--accent); border-color: var(--accent); }
.rq-step-next { color: var(--text2); }
.rq-step-at { color: var(--text2); font-size: 12.5px; white-space: nowrap; }

/* Лента занимает высоту панели целиком, а поле ответа прижато к её низу:
   отвечают, дочитав до конца, и искать поле, уехавшее вместе с прокруткой, —
   лишний шаг на каждом ответе. */
.rq-feed { display: flex; flex-direction: column; }
/* Прокрутка внутри ленты, а не у страницы: событий по заявке бывает полсотни,
   и панель в полтора экрана уводила бы всё остальное вниз.

   Потолка в пикселях здесь нет намеренно. Он был, и от него лента занимала
   420px внутри панели, растянутой соседней колонкой на 800: прокручиваемый
   кусок выходил вдвое ниже рамки, в которой стоял, и низ панели читался как
   обрезанный. Высоту задаёт колонка, лента её заполняет. */
.rq-feed .chat { flex: 1 1 auto; min-height: 240px; margin: 0; overflow-y: auto; }

/* Чем сотрудник отвечает клиенту: кнопками всегда, полем — по праву. */
.rq-answer { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border); }
.rq-answer-name {
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text2);
    margin-bottom: 8px;
}
.rq-replies { display: flex; flex-wrap: wrap; gap: 8px; }
.rq-composer { margin-top: 10px; }

/* Своё поле ввода вместо браузерного. Дефолтная textarea приезжала с чужой
   рамкой, чужим фокусом и полосой изменения размера в углу — единственный
   элемент на экране не из этого набора. */
.rq-input {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    padding: 8px 10px 8px;
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.rq-input:focus-within {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.rq-input textarea {
    width: 100%;
    border: none;
    background: none;
    outline: none;
    resize: none;
    padding: 2px 2px 6px;
    font: inherit;
    font-size: 14px;
    line-height: 1.45;
    color: var(--text);
    min-height: 24px;
}
.rq-input-foot { display: flex; align-items: center; gap: 8px; }
.rq-input-count { font-size: 11.5px; color: var(--text2); }
.rq-input-count.over { color: var(--danger); }

/* Цвет заметки — три кружка. Вариантов ровно три, и выпадающий список из трёх
   пунктов это два нажатия там, где хватает одного. */
.rq-colors { display: flex; gap: 6px; }
.rq-color {
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 1.5px solid var(--border-strong);
    background: var(--card);
    cursor: pointer;
    padding: 0;
}
.rq-color-red { background: color-mix(in srgb, var(--danger) 70%, transparent); border-color: var(--danger); }
.rq-color-yellow { background: color-mix(in srgb, var(--warning) 70%, transparent); border-color: var(--warning); }
.rq-color.on { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }

/* Заметки: цвет — полосой слева, а не подписью «красная». Полоса видна краем
   глаза при прокрутке, подпись — нет. */
.rq-notes {
    display: flex;
    flex-direction: column;
    gap: 10px;
    /* Список не тянется вниз без конца: три заметки видно целиком, дальше своя
       прокрутка. Без потолка панель заметок на живом аккаунте уезжала на
       два экрана и уводила за собой всё, что под ней. */
    max-height: 260px;
    overflow-y: auto;
    padding-right: 4px;
}
.rq-note {
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: 10px;
    background: var(--surface);
}
.rq-note-red { border-left-color: var(--danger); }
.rq-note-yellow { border-left-color: var(--warning); }
.rq-note-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12.5px; }
.rq-note p { margin: 4px 0 0; font-size: 14px; line-height: 1.45; white-space: pre-wrap; }
/* Правка — иконка в углу заметки, а не кнопка в полтекста. Раньше «Изменить»
   весила больше самой заметки и утаскивала её к полю ввода внизу. */
.rq-note-edit {
    margin-left: auto;
    width: 24px;
    height: 24px;
    border-radius: 7px;
    border: 1px solid transparent;
    background: none;
    color: var(--text2);
    cursor: pointer;
    line-height: 1;
}
.rq-note-edit:hover { border-color: var(--accent); color: var(--accent); }
.rq-note-editing { border-color: var(--accent); }
.rq-note-left { font-size: 11.5px; color: var(--text2); white-space: nowrap; }
.rq-note-form { margin-top: 12px; }
.rq-notes-panel .rq-fold { border-top: none; margin-top: 0; }

/* Слот с непринятой пересъёмкой отбит рамкой: пометки в заголовке мало, когда
   слотов пятнадцать. */
.shot-slot-wait { border-color: var(--warning); }

/* Строка проверки IMAP в форме мыльницы и её результат. */
.mail-probe-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.mail-probe {
    margin-top: 10px;
    padding: 10px 12px;
    border-radius: 9px;
    border: 1px solid var(--border);
    background: var(--surface);
    display: flex;
    flex-direction: column;
    gap: 5px;
}
.mail-probe.ok  { border-color: color-mix(in srgb, var(--series-profit) 45%, var(--border)); }
.mail-probe.bad { border-color: color-mix(in srgb, var(--series-expense) 45%, var(--border)); }
.mail-probe-detail { font-size: 12.5px; color: var(--text2); word-break: break-word; }
/* Овал типа почты несёт подсказку на наведение — курсор об этом и говорит. */
.pill.has-hint { cursor: help; }

/* Шапка блока доступа в форме мыльницы: домен, во что превратится строка и
   куда мы за письмами пойдём. Стоит над полями, потому что отвечает на вопрос
   «а что вообще заполнять». */
.mail-provider { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; }

/* Приписка к значению факта: откуда почта, сверена ли доплата. Значение здесь
   главное, приписка — уточнение, поэтому она приглушена и не спорит с ним
   размером. */
.res-source-badge {
    font-size: 11.5px;
    padding: 1px 8px;
    border-radius: 999px;
    background: var(--surface);
    color: var(--text2);
    white-space: nowrap;
}
/* Ждём денег — это не ошибка, а состояние: жёлтый, а не красный. */
.res-source-badge.warn { background: var(--accent-light); color: var(--warning); }

/* --- галерея кадров --------------------------------------------------------
   Кадр и его прошлые версии стоят в одном ряду: пересъёмку смотрят сравнением,
   поэтому «было» не прячется под вкладку, а лежит рядом и приглушено.

   У слота своя рамка: без неё заголовки выстраивались в одну строку через всю
   карточку, и было не видно, где кончается один кадр и начинается следующий. */
.shot-grid { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-start; }
.shot-slot {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 10px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
}
.shot-slot-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; font-size: 12.5px; color: var(--text2); }
/* Отметка слота галочкой в его же заголовке: пересъёмку просят пачкой, и
   набирать номера через запятую в диалоге — это переписывать глазами то, что
   уже видно на экране. */
.shot-pick { display: flex; align-items: center; gap: 6px; cursor: pointer; }
.shot-pick input { accent-color: var(--accent); }
.shot-slot-on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

/* Размер плиток. Пятнадцать кадров смотрят по-разному: сначала «где какой»
   мелкой сеткой, потом конкретный — крупно. Выбор помнится между заявками. */
.shot-sizes { display: inline-flex; gap: 2px; }
.shot-size {
    width: 26px;
    height: 24px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text2);
    cursor: pointer;
    font-size: 11px;
    line-height: 1;
}
.shot-size:first-child { border-radius: 7px 0 0 7px; }
.shot-size:last-child { border-radius: 0 7px 7px 0; }
.shot-size.on { border-color: var(--accent); color: var(--accent); }
.shot-grid-s .shot { width: 92px; }
.shot-grid-s .shot img { height: 118px; }
.shot-grid-l .shot { width: 210px; }
.shot-grid-l .shot img { height: 270px; }

/* Полоска действий над выбранными: появляется только с выбором — пустая, она
   предлагала бы переснять «ничего». */
.shot-bar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--border);
}
.shot-bar-count { font-size: 13px; color: var(--text2); }
.shot-row { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 4px; }
.shot {
    position: relative;
    display: block;
    padding: 0;
    cursor: zoom-in;
    flex: 0 0 auto;
    width: 140px;
    border: 1px solid var(--border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--surface);
}
.shot img { display: block; width: 100%; height: 180px; object-fit: cover; }
.shot-old { opacity: 0.55; }
.shot-old:hover { opacity: 1; }
.shot-tag {
    position: absolute;
    left: 6px;
    top: 6px;
    padding: 2px 7px;
    border-radius: 999px;
    font-size: 10.5px;
    background: var(--surface);
    color: var(--text2);
}

/* --- лента событий ---------------------------------------------------------
   Пузыри, а не строки «кто · когда · что»: сторона сообщения читается раньше
   любой подписи, а «бот 10.08.2026, 15:39» занимало два этажа и было крупнее
   самого события. Чужое слева, наше справа, время мелко в углу, дата —
   разделителем между днями. Отметки бота в стороны не раскладываются: они идут
   посередине тонкой строкой, как служебная запись между репликами. */
/* Высота ленты — от окна, а не константой: на большом экране 460 px оставляли
   половину карточки пустой, а ленту всё равно прокручивали. */
.chat { display: flex; flex-direction: column; gap: 6px; max-height: clamp(380px, 58vh, 900px); overflow-y: auto; margin: 14px 0; padding-right: 4px; }

.feed-row { display: flex; }
.feed-in { justify-content: flex-start; }
.feed-out { justify-content: flex-end; }

.bubble {
    position: relative;
    max-width: 78%;
    min-width: 96px;
    padding: 7px 11px 4px;
    border-radius: 14px;
    background: var(--surface);
    border: 1px solid var(--border);
}
.feed-in .bubble { border-bottom-left-radius: 5px; }
/* Наше — цветом, а не подписью: это единственное, что нужно различать взглядом. */
.feed-out .bubble {
    border-bottom-right-radius: 5px;
    background: var(--accent-light);
    border-color: color-mix(in srgb, var(--accent) 22%, transparent);
}
/* Пришедшее на выданные номер и мыло — не разговор с клиентом, и рамка об этом
   говорит: «код 123456» от бота и от сервиса по автору не отличить. */
.feed-svc .bubble { border-left: 3px solid var(--border-strong); }

.bubble-head { display: flex; gap: 7px; align-items: baseline; margin-bottom: 3px; }
/* Кто написал — подпись, а не шёпот: серым по серому её переставали замечать, и
   в ленте, где втроём отвечают одному, «кто это сказал» терялось. */
.bubble-from {
    font-size: 12.5px;
    font-weight: 700;
    color: color-mix(in srgb, var(--text) 78%, var(--text2));
    word-break: break-word;
}
.feed-out .bubble-from { color: color-mix(in srgb, var(--accent) 75%, var(--text)); }
.bubble-subject { font-size: 14px; font-weight: 600; margin-bottom: 3px; word-break: break-word; }
/* Откуда сообщение: «по заявке RES-42» в общем чате, вид доставки в ленте
   заявки. Метка, а не текст — иначе она читается как часть сообщения. */
.bubble-tag {
    margin-left: auto;
    flex-shrink: 0;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--surface-strong, var(--surface));
    border: 1px solid var(--border);
    font-size: 11px;
    font-weight: 650;
    color: var(--text2);
    white-space: nowrap;
}
/* SMS и письмо различаются цветом метки: их читают по-разному — в одном код, в
   другом письмо целиком, — и одинаковая серая метка на обоих это скрывала. */
.bubble-tag-sms {
    background: color-mix(in srgb, var(--warning) 16%, var(--card));
    border-color: color-mix(in srgb, var(--warning) 40%, transparent);
    color: color-mix(in srgb, var(--warning) 72%, var(--text));
}
.bubble-tag-mail {
    background: color-mix(in srgb, var(--accent) 14%, var(--card));
    border-color: color-mix(in srgb, var(--accent) 38%, transparent);
    color: color-mix(in srgb, var(--accent) 78%, var(--text));
}
/* Обращение в поддержку. Цветом, а не только словом: в ленте из полусотни строк
   серая метка теряется, а разбирают их раньше остальных. */
.bubble-support {
    margin-left: 0;
    background: color-mix(in srgb, var(--danger) 14%, var(--card));
    border-color: color-mix(in srgb, var(--danger) 38%, transparent);
    color: color-mix(in srgb, var(--danger) 78%, var(--text));
}
/* Снимки в переписке. Ряд миниатюр, каждая открывается в соседней вкладке
   целиком: разбирать скриншот в пузыре шириной в палец невозможно. */
.bubble-shots {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 6px;
}
.bubble-shot {
    display: block;
    padding: 0;
    background: none;
    line-height: 0;
    border-radius: 10px;
    overflow: hidden;
    border: 1px solid var(--border);
    cursor: zoom-in;
}
.bubble-shot:hover { border-color: var(--accent); }
.bubble-shot img {
    display: block;
    max-width: 220px;
    max-height: 220px;
    object-fit: cover;
}
/* Текст сообщения — крупнее подписи и времени вокруг него: читают именно его. */
.bubble-body { font-size: 15.5px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }
/* Время в углу, а не строкой: подпись под сообщением стоила бы целого этажа.
   Дату несёт разделитель дня, здесь остаются часы и минуты. */
.bubble-time {
    display: block;
    text-align: right;
    font-size: 11.5px;
    color: var(--text2);
    margin-top: 2px;
    font-variant-numeric: tabular-nums;
}

/* Код из SMS — то, ради чего строку открывают. Отдельной кнопкой: выделять его
   мышью из середины сообщения приходилось на каждой заявке. */
.bubble-code {
    display: inline-block;
    margin-top: 5px;
    padding: 3px 10px;
    border-radius: 8px;
    border: 1px dashed color-mix(in srgb, var(--accent) 45%, transparent);
    background: color-mix(in srgb, var(--accent) 10%, transparent);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: 0.06em;
    cursor: pointer;
}
.bubble-code:hover { border-style: solid; border-color: var(--accent); }

/* Письмо приходит целиком, вместе с подвалом, и одно такое вытесняет из окна
   весь разговор. Три строки — ровно та часть, где стоит код. */
.bubble-body.clipped {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
/* Раскрытая простыня остаётся в своих берегах: письмо на десять экранов иначе
   уносит вверх и разговор до него, и поле ответа под ним. */
.bubble-long {
    max-height: 42vh;
    overflow-y: auto;
    padding: 6px 8px;
    border-radius: 8px;
    border: 1px solid var(--border);
    background: color-mix(in srgb, var(--text) 3%, transparent);
    overscroll-behavior: contain;
}
.bubble-more {
    margin-top: 3px;
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}
.bubble-more:hover { text-decoration: underline; }

/* Отправленное, но ещё не подтверждённое: строка стоит в ленте сразу, приглушённо.
   Не дошло — рамка краснеет и рядом появляется «ещё раз». */
.feed-pending .bubble { opacity: 0.62; }
.feed-pending.is-failed .bubble {
    opacity: 1;
    border-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 10%, var(--card));
}
.feed-pending.is-failed .bubble-time { color: var(--danger); }
.bubble-retry {
    margin-left: 8px;
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font: inherit;
    font-size: 11.5px;
    cursor: pointer;
    text-decoration: underline;
}

/* Служебная отметка: капсула со значком посередине ленты. Серая строчка в общем
   потоке терялась между пузырями ровно там, где по ней восстанавливают порядок
   событий, — а событие «лок снят» весит не меньше реплики. */
.feed-note-row { display: flex; justify-content: center; }
.feed-note {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: 90%;
    padding: 4px 11px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    font-size: 12.5px;
    color: var(--text2);
    text-align: left;
}
.feed-note-icon { flex: none; color: var(--text2); }
.feed-note-body { color: color-mix(in srgb, var(--text) 70%, var(--text2)); }
.feed-note-time {
    font-size: 11px;
    opacity: .8;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* Денежная отметка: та же капсула, но своим цветом и с меткой — её видит не
   каждый, и по строке должно быть понятно, почему у соседа её нет. */
.feed-note.feed-money {
    border-color: color-mix(in srgb, var(--success) 42%, transparent);
    background: color-mix(in srgb, var(--success) 12%, var(--card));
}
.feed-money .feed-note-icon,
.feed-money .feed-note-body { color: color-mix(in srgb, var(--success) 75%, var(--text)); }
.feed-note-tag {
    font-size: 10.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: 1px 6px;
    border-radius: 6px;
    background: color-mix(in srgb, var(--success) 22%, var(--card));
    color: color-mix(in srgb, var(--success) 80%, var(--text));
    white-space: nowrap;
}

/* Разделитель дня: дата стоит здесь и поэтому не повторяется в каждом пузыре.
   Липнет к верху ленты — пролистав полсотни событий, «какой это день» спрашивают
   чаще, чем что-либо ещё. */
.feed-day {
    display: flex;
    justify-content: center;
    margin: 6px 0 2px;
    position: sticky;
    top: 0;
    z-index: 2;
    pointer-events: none;
}
.feed-day span {
    font-size: 11.5px;
    font-weight: 650;
    color: var(--text2);
    /* Непрозрачная: она висит поверх пузырей, и просвечивающий сквозь неё текст
       читается как опечатка. */
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    padding: 2px 12px;
    box-shadow: var(--shadow);
}

.chat-tag {
    font-size: 11px;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--hover-bg);
    color: var(--text2);
}

/* --- просмотрщик кадров ----------------------------------------------------
   Своё окно поверх страницы: скриншот здесь читают и сравнивают, а вкладка
   браузера теряет и соседние кадры, и саму заявку. */
/* Во весь экран и без рамки: кадр читают, а окно вокруг него — панель
   инструментов, а не карточка. Светлый кант вокруг картинки только мешал. */
#shot-viewer { position: fixed; inset: 0; z-index: 1200; background: rgba(0, 0, 0, 0.92); }
.shot-view {
    position: relative;
    margin: 0;
    display: flex;
    flex-direction: column;
    width: 100vw;
    height: 100vh;
    overflow: hidden;
}
/* Подпись, а не панель инструментов. Кнопки «Закрыть», «Масштаб», «По размеру»
   и «Скачать» отвечали на вопросы, которых никто не задавал, и закрывали собой
   сам кадр. Закрывают нажатием мимо кадра и по Esc — как в телеграме. */
.shot-view-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 16px;
    background: transparent;
    color: rgba(255, 255, 255, 0.72);
    font-size: 12.5px;
    flex-wrap: wrap;
    pointer-events: none;
}
.shot-view-head .muted { color: rgba(255, 255, 255, 0.5); }
/* Кадр двигает трансформация, поэтому окно ничего не прокручивает: полосы
   прокрутки мышью не тянутся. */
.shot-view-body { flex: 1; min-height: 0; position: relative; overflow: hidden; cursor: grab; touch-action: none; }
.shot-view-body.grabbing { cursor: grabbing; }
.shot-view-body img {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
    max-width: none;
    max-height: none;
    user-select: none;
    -webkit-user-drag: none;
}
/* Полоска миниатюр: пятнадцать кадров стрелками листать долго. */
.shot-strip { display: flex; gap: 6px; padding: 8px 10px; border-top: 1px solid var(--border); overflow-x: auto; background: var(--card); }
.shot-thumb { flex: 0 0 auto; width: 66px; height: 48px; padding: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--surface); cursor: pointer; }
.shot-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot-thumb:hover { border-color: var(--border-strong); }
.shot-thumb.on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.shot-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 42px;
    height: 42px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
}
.shot-nav.prev { left: 12px; }
.shot-nav.next { right: 12px; }
.shot-nav:hover { background: var(--hover-bg); }

/* --- история выданного контакта --------------------------------------------
   Своё окно поверх карточки, а не раскрытие внутри неё: история — это сотня
   писем чужого ящика, и развёрнутая в панели данных она выдавливает вниз и
   ленту, и счётчик заявок. Окно закрывают нажатием мимо и по Esc, как
   просмотрщик кадров. */
#hist-viewer {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: flex;
    padding: 3vh 3vw;
    background: rgba(0, 0, 0, 0.55);
}
.hist-view {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 860px;
    margin: auto;
    max-height: 94vh;
    min-height: 0;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card);
    overflow: hidden;
}
.hist-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
}
.hist-title { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hist-what {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text2);
}
/* Контакт в шапке — подтверждение, что показывают именно его: окно живёт
   дольше отрисовки карточки, из которой его открыли. */
.hist-contact {
    font-family: var(--font-mono);
    font-size: 16px;
    font-weight: 650;
    overflow-wrap: anywhere;
}
/* «Обновляется само» стоит в шапке не для красоты: без него окно, открытое в
   ожидании кода, закрывают и открывают заново, чтобы «проверить». */
.hist-live {
    margin-left: auto;
    flex: none;
    padding: 2px 9px;
    border-radius: 999px;
    border: 1px solid color-mix(in srgb, var(--series-profit, #3aa76d) 45%, var(--border));
    color: var(--series-profit, #3aa76d);
    font-size: 11.5px;
    white-space: nowrap;
    cursor: default;
}
.hist-close {
    flex: none;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--surface);
    color: var(--text2);
    font-size: 19px;
    line-height: 1;
    cursor: pointer;
}
.hist-close:hover { border-color: var(--border-strong); color: var(--text); }

.hist-tools {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--border);
    flex-wrap: wrap;
}
.hist-tabs { display: flex; gap: 4px; padding: 3px; border-radius: 999px; background: var(--surface); }
.hist-tab {
    padding: 4px 12px;
    border: none;
    border-radius: 999px;
    background: none;
    color: var(--text2);
    font: inherit;
    font-size: 13px;
    cursor: pointer;
}
.hist-tab:hover { color: var(--text); }
.hist-tab.on { background: var(--card); color: var(--text); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08); }
.hist-search {
    flex: 1 1 200px;
    min-width: 140px;
    padding: 7px 11px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    font-size: 13.5px;
}
.hist-count { flex: none; font-size: 12px; color: var(--text2); font-variant-numeric: tabular-nums; }

.hist-list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 14px 16px;
}
.hist-empty { margin: 0; padding: 26px 8px; text-align: center; font-size: 13.5px; color: var(--text2); }
.hist-empty .muted { display: block; margin-top: 6px; font-size: 12px; }
.hist-row { min-width: 0; }
/* Пришедшее при открытом окне: подсветка держится до закрытия, а не гаснет по
   таймеру. Окно открывают ровно затем, чтобы дождаться этой строки, и увидеть
   её должен тот, кто отвернулся на минуту. */
.hist-row.is-fresh .rq-msg {
    border-color: color-mix(in srgb, var(--accent) 55%, transparent);
    background: color-mix(in srgb, var(--accent) 7%, var(--card));
}

/* Настройки «Рес» — свой блок над списком видов, с воздухом между ними: без
   отбивки карточка настроек читалась как часть формы «новый вид». */
.res-settings { margin: 18px 0 26px; }
.res-settings .kind-card { border-color: var(--border-strong); }

/* =============================================================================
   Доступы ролей: разделы в карточке и разделы внутри списка.

   Настроек стало столько, что одна полоса выпадающих списков читается как
   сплошной текст. Разделение делается воздухом и одной волосяной линией, а не
   рамками и заливками: рамка внутри карточки, которая сама в рамке, — это уже
   третий уровень вложенности на один экран.
   ============================================================================= */

.access-card h3 + .access-section { margin-top: 14px; }
.access-section + .access-section { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--border); }
.access-section-head {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: var(--text2);
    margin-bottom: 8px;
}
.access-section-hint { font-size: 12.5px; color: var(--text2); margin: -4px 0 10px; line-height: 1.45; }
.access-section .ms-grid { margin: 0; }

/* Части одного раздела рядом. Линия между ними — та же волосяная, что отделяет
   разделы друг от друга, только вертикальная: «куда пускать», «что разрешено
   делать» и «типы заявок» отвечают на вопросы про одно место, и читать их надо
   вместе. Доли ширины задаёт разметка — по числу списков в части. */
.access-split { display: flex; gap: 24px; align-items: stretch; }
/* Списки стоят на одной линии, даже когда над одним из них есть подсказка, а над
   соседним нет: разнобой по высоте читается как «эти списки про разное». */
.access-split-part { min-width: 0; display: flex; flex-direction: column; }
.access-split-part .ms-grid { margin-top: auto; }
.access-split-part + .access-split-part {
    padding-left: 24px;
    border-left: 1px solid var(--border);
}

/* Списки прав внутри раздела стоят одной строкой, пока в неё влезают. Дальше
   переносятся: `nowrap` при полудюжине списков выпихивал их за край карточки —
   у роли списков трое, а у человека на вкладке исключений вдвое больше. */
.access-section .ms-grid { display: flex; flex-wrap: wrap; gap: 12px; }
/* Потолок ширины — чтобы одинокий список в разделе не растягивался во всю
   карточку: строка из одного элемента во всю ширину читается как заголовок. */
.access-section .ms-grid > * { flex: 1 1 240px; min-width: 0; max-width: 420px; }

@media (max-width: 900px) {
    .access-split { flex-direction: column; gap: 18px; }
    .access-split-part { width: 100%; }
    .access-split-part + .access-split-part {
        padding-left: 0;
        padding-top: 18px;
        border-left: none;
        border-top: 1px solid var(--border);
    }
    /* На узком экране ряд из шести списков — шесть полосок по полсантиметра. */
    .access-section .ms-grid { flex-wrap: wrap; }
    .access-section .ms-grid > * { flex: 1 1 220px; }
}

/* Раздел внутри списка. Подпись — не строка: по ней не нажимают, и она не должна
   выглядеть как то, что можно включить. Отсюда мелкий кегль, разрядка и линия
   сверху вместо фона. */
.ms-group {
    font-size: 10.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.7px;
    color: var(--text2);
    padding: 10px 11px 4px;
    pointer-events: none;
}
.ms-row + .ms-group { margin-top: 4px; border-top: 1px solid var(--border); padding-top: 10px; }

/* Пароль в карточке «Рес»: значение и есть кнопка. Точки под курсором должны
   выглядеть нажимаемыми, раскрытый пароль — уже нет, но копировать его можно. */
.pass-tap {
    border: 1px dashed var(--border-strong);
    background: none;
    color: var(--text);
    border-radius: 8px;
    padding: 3px 9px;
    cursor: pointer;
    font-size: 13.5px;
    letter-spacing: 0.5px;
}
.pass-tap:hover { border-color: var(--accent); color: var(--accent); }
.pass-tap.is-open { border-style: solid; letter-spacing: 0; }

/* Зарплатный лист: день с подытогом, под ним сделки.
   Числа выравниваются по правому краю и моноширинными цифрами — колонку сумм
   читают столбиком, а не по одной строке. */
.num { text-align: right; font-variant-numeric: tabular-nums; }

.pay-day { border: 1px solid var(--border); border-radius: 14px; margin-bottom: 10px; overflow: hidden; }
.pay-day-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 11px 16px;
    background: var(--hover-bg);
}
.pay-day-date { font-size: 14px; font-weight: 650; color: var(--text); letter-spacing: -0.01em; }
.pay-day-total { font-size: 14px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.pay-day-body { padding: 2px 16px 6px; }

/* Строка — не карточка в карточке: тридцать рамок превращают день в клетку. */
.pay-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 10px 0; }
.pay-row + .pay-row { border-top: 1px solid var(--border); }
.pay-row-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pay-deal { font-size: 13.5px; color: var(--text); }
/* Код сделки — не часть названия события: по нему ищут заявку глазами. */
.pay-deal-code { color: var(--text2); }
.pay-row-sub { font-size: 12px; color: var(--text2); }
.pay-amount { font-size: 13.5px; font-weight: 650; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Снятое начисление читается как снятое, а не как «заработал меньше». */
.pay-amount.is-negative { color: var(--danger); }


/* === Очередь деревом: кто ведёт — кто делает — что именно ================= */
/* Группировка по исполнителю раньше показывала плитки со счётчиками: одно
   число, из которого не видно ни заявок, ни чьи они. Теперь свёртки, и заявки
   лежат внутри той группы, к которой относятся. */
.tree { display: flex; flex-direction: column; gap: 10px; margin-top: 14px; }
.tree-node {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    overflow: hidden;
}
/* Вложенная группа — своя рамка внутри чужой: без неё два уровня свёрток
   читаются как один длинный список. */
.tree-node .tree-node { margin: 0 12px 10px; background: var(--card); }
.tree-head {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 11px 14px;
    border: none;
    background: none;
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.tree-head:hover { background: var(--hover-bg); }
.tree-caret {
    width: 0; height: 0; flex-shrink: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    color: var(--text2);
    transition: transform 0.12s ease;
}
.tree-node.open > .tree-head .tree-caret { transform: rotate(90deg); }
.tree-name { font-weight: 600; font-size: 15px; }
.tree-kind { font-size: 11.5px; color: var(--text2); text-transform: lowercase; }
.tree-stat { font-size: 12.5px; color: var(--text2); white-space: nowrap; }
.tree-body { display: none; padding: 0 12px 10px; }
.tree-node.open > .tree-body { display: block; }
.tree-node .tree-node > .tree-body { padding: 0 10px 8px; }

/* === Зарплата: период, список сотрудников, сводка =========================
   Свои элементы управления здесь не заводятся: период — обычный список панели
   плюс те же поля дат, что на «Финансах», переключатели — `.seg-picker` с
   «Загрузок». Два предыдущих захода (ряд из шести кнопок, потом свой поповер)
   были попыткой изобрести то, что в системе уже есть. */
.pay-head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.pay-period { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.pay-period-note { font-size: 13px; }

/* Шапка чужого листа: «Назад», чей это лист, период. Колонки-пикера слева
   больше нет — люди выбираются в обычной таблице, как во всех очередях.

   Три элемента в одном ряду с общим отступом читались как ряд из трёх кнопок,
   и имя терялось между «Назад» и списком периодов. Поэтому: управление по краям,
   имя своим блоком с подписью, под шапкой — линия, отделяющая её от плиток. */
.pay-head-sheet {
    gap: 14px;
    padding-bottom: 16px;
    border-bottom: 1px solid var(--border);
}
.pay-who { display: flex; flex-direction: column; min-width: 0; }
.pay-who-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text2);
}
.pay-sheet-title { font-size: 19px; font-weight: 650; margin: 0; letter-spacing: -0.01em; }
/* Период — к правому краю: слева отвечают «чей лист», справа «за какой срок». */
.pay-head-sheet .pay-period { margin-left: auto; }

/* Список сотрудников: доли колонок заданы, иначе имя забирает всю свободную
   ширину и между ним и ролью зияет пустая треть карточки. Кнопка прижата к
   правому краю — там же, где она во всех остальных таблицах панели. */
.pay-staff-search { max-width: 340px; }
.pay-staff-table th:nth-child(1) { width: 28%; }
.pay-staff-table th:nth-child(2) { width: 14%; }
.pay-staff-table th:nth-child(3) { width: 18%; }
.pay-staff-table th:nth-child(4) { width: 26%; }
.pay-staff-table td:last-child, .pay-staff-table th:last-child { text-align: right; }
.pay-staff-table td.cell-actions .actions { justify-content: flex-end; }

/* На узком доли колонок только мешают: имя ломается по слогам. Таблица едет
   вбок целиком, как остальные таблицы панели, а период под шапкой встаёт
   слева — прижимать его к правому краю на своей строке не за чем. */
@media (max-width: 900px) {
    /* Общее правило узкого экрана снимает прокрутку с `.table-wrap`; здесь она
       нужна обратно, иначе таблица шириной 620 просто вылезет за карточку. */
    .pay-staff-table .table-wrap { overflow-x: auto; }
    .pay-staff-table table { min-width: 620px; }
    .pay-staff-table th:nth-child(1),
    .pay-staff-table th:nth-child(2),
    .pay-staff-table th:nth-child(3),
    .pay-staff-table th:nth-child(4) { width: auto; }
    .pay-staff-search { max-width: none; }
    .pay-head-sheet .pay-period { margin-left: 0; }
}

/* Раскрытая строка сводки: сделки, из которых она сложилась. */
th.sortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.sortable:hover { color: var(--text); }
.row-caret {
    display: inline-block;
    width: 0; height: 0;
    margin-right: 8px;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent;
    border-bottom: 4px solid transparent;
    color: var(--text2);
    transition: transform 0.12s ease;
}
.row-caret.open { transform: rotate(90deg); }
tr.row-details td { background: var(--surface); padding: 10px 14px; }
tr.row-band td {
    background: var(--hover-bg);
    font-weight: 600;
    font-size: 13px;
    padding: 8px 14px;
}
tr.row-band td .muted { margin-left: 10px; font-weight: 400; }
.deal-list { display: flex; flex-direction: column; gap: 4px; }
.deal {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border);
    border-radius: 9px;
    background: var(--card);
    color: var(--text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.deal:hover { border-color: var(--accent); }
.deal-code { font-weight: 600; }
.deal-part, .deal-score, .deal-sum { font-size: 12.5px; color: var(--text2); white-space: nowrap; }
.deal-sum { font-weight: 600; color: var(--text); }
.deal-facts { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px 20px; }
.deal-fact dt { font-size: 12px; color: var(--text2); margin-bottom: 2px; }
.deal-fact dd { font-size: 14.5px; }

/* === Настройка комиссий ==================================================
   Была таблица «субъект × событие»: строка на каждого сотрудника и каждое
   событие, из которых заполнены единицы. Теперь две карточки ролей и список
   реальных исключений — экран перестал расти вместе со штатом. */
.comm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; }
.comm-card {
    border: 1px solid var(--border);
    border-radius: 14px;
    padding: 16px 18px;
    background: var(--surface);
}
.comm-card h3 { font-size: 15.5px; font-weight: 600; margin-bottom: 12px; }
.rate-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.rate-row { display: flex; align-items: center; gap: 10px; }
.rate-name { flex: 1; min-width: 0; font-size: 14px; }
.rate-input {
    width: 82px;
    padding: 7px 10px;
    border: 1.5px solid var(--border-strong);
    border-radius: 9px;
    background: var(--input-bg);
    color: var(--text);
    font: inherit;
    font-size: 14px;
    text-align: right;
    outline: none;
}
.rate-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }
/* Тип ставки — короткий выбор из трёх, а не поле во всю оставшуюся ширину:
   растянутый на полкарточки «%» читается как что-то важнее самой ставки. */
/* min-width у общего .cs — 200 px, и в узкой строке ставки он и распирал ряд. */
.rate-row .cs { flex: none; width: 124px; min-width: 0; }
.comm-add .cs { flex: none; min-width: 150px; }

.comm-personal { margin-top: 26px; }
.comm-personal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.comm-personal-head h3 { font-size: 15px; font-weight: 600; }
.comm-add {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 14px;
    margin-bottom: 12px;
    border: 1px dashed var(--border-strong);
    border-radius: 12px;
}
.comm-list { display: flex; flex-direction: column; gap: 6px; }
.comm-item {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}
.comm-item-who { font-weight: 600; }
.comm-item-event { color: var(--text2); font-size: 13.5px; }
.comm-item-rate { font-weight: 600; white-space: nowrap; }

/* === Кошелёк и пополнения ===
 *
 * Вкладки собраны из блоков, которые идут друг за другом сверху вниз: адреса,
 * форма заведения, журнал. Каждый блок либо есть, либо пуст, и без отбивки
 * снизу «адресов приёма нет» прилипало к кнопке под ним, а таблица адресов — к
 * форме. Отбивка на самих блоках, а не на .msg: пустой блок не должен оставлять
 * после себя дыру, отсюда :empty. */
#deposits-addresses,
#deposits-add,
#deposits-table,
#wallet-debtors,
#wallet-adjust {
    margin-bottom: 18px;
}

#deposits-addresses:empty,
#deposits-add:empty,
#wallet-debtors:empty,
#wallet-adjust:empty {
    margin-bottom: 0;
}

/* Плашка «адресов нет» — единственное на экране, что требует действия, поэтому
 * она отбита и сверху: рядом с фильтрами она читалась как их подпись. */
#deposits-addresses .msg {
    margin-top: 0;
}

/* === Смена сотрудника ======================================================
   Кнопка в шапке рядом с логином: смену закрывают на выходе из-за стола, и
   элемент, до которого надо дойти, вспоминается только когда заявки уже
   перестали приходить. */
.shift-toggle {
    flex-shrink: 0;
    padding: 4px 12px;
    border: 1.5px solid var(--border-strong);
    border-radius: 999px;
    background: transparent;
    color: var(--text2);
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
.shift-toggle:hover { border-color: var(--text2); color: var(--text); }
/* На смене — заливкой: это состояние, за которым следят краем глаза, и контур
   рядом с контуром «не на смене» пришлось бы читать. */
.shift-toggle.on { background: var(--success); border-color: var(--success); color: #fff; }
.shift-toggle.on:hover { filter: brightness(1.07); }

.shift-settings { margin-bottom: 18px; }
.shift-settings-row { display: flex; gap: 12px; align-items: flex-end; }
.shift-settings .form-hint { margin-left: 0; }

/* Исполнитель не на смене — серым, но в списке: назначить его можно, просто
   панель переспросит. */
.cs-option.cs-muted { color: var(--text2); }

/* === Настройки видом-группами ==============================================
   Восемнадцать полей «Реса» лежали одним flex-wrap: узкое поле под трёхзначное
   число растягивалось по ширине своей подписи, и ряд рвался в произвольных
   местах. Здесь они разложены по смыслу, а внутри группы — сеткой с общей
   колонкой, поэтому поля стоят ровно независимо от длины подписи. */
/* Стопка групп: широкие лежат прямо в ней и занимают строку целиком.
   Сеткой это было раньше и выравнивалось по строкам — группа из одного поля
   растягивалась по соседней из пяти, и между ними зияла дыра в полстроки. */
/* Группы настроек — сеткой, а не столбцом. Столбцом каждая тянулась во всю
   ширину карточки, и группа из двух полей занимала строку целиком: справа
   пустота, а всё остальное уехало вниз. Теперь короткие встают рядом, а те,
   которым нужна ширина (списки, таблицы, шаблоны), просят её сами. */
/*
 * Колоночный поток, а не сетка. Сетка равняет строку по самой высокой группе:
 * рядом с «Очередью» из десяти полей соседняя группа из двух оставляла столбец
 * пустоты в собственный рост. Здесь короткая группа не тянет за собой соседнюю
 * — следующая просто встаёт под неё, и колонки заполняются плотно.
 *
 * Широкие группы (словари, поля-списки, группы на десяток чисел) выходят из
 * потока на всю ширину: `column-span: all` разрывает колонки до себя и после.
 */
.set-groups {
    columns: 300px 3;
    column-gap: 12px;
}
.set-groups > .set-group {
    break-inside: avoid;
    margin: 0 0 12px;
}
.set-groups > .set-group-wide {
    column-span: all;
    /* Отступ сверху — свой: `margin-bottom` соседа по колонке до широкой группы
       не доходит, она разрывает поток колонок и встаёт вплотную к ним. Между
       двумя широкими подряд хватает нижнего отступа первой. */
    margin-top: 12px;
    /* Поля внутри широкой группы считают ширину от всей карточки: десять чисел
       встают двумя рядами вместо пяти. */
    break-inside: auto;
}
.set-groups > .set-group-wide:first-child,
.set-groups > .set-group-wide + .set-group-wide { margin-top: 0; }
/* Пара групп в одну строку: колоночный поток ставит их по высоте, а связанные
   по смыслу должны стоять рядом — сроки заявки и то, чем её закрывают. */
.set-row {
    display: grid;
    grid-template-columns: 2fr 1fr;
    align-items: start;
    gap: 12px;
    margin: 12px 0;
}
.set-groups > .set-row:first-child,
.set-groups > .set-group-wide + .set-row,
.set-groups > .set-row + .set-group-wide { margin-top: 0; }
@media (max-width: 900px) {
    .set-row { grid-template-columns: 1fr; }
}
.set-group {
    padding: 11px 13px;
    background: var(--hover-bg);
    border-radius: 10px;
}
.set-group-title {
    font-size: 11.5px;
    font-weight: 700;
    color: var(--text2);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 8px;
}
/* align-items: end — подпись в две строки не должна ронять своё поле ниже
   соседних: выравниваем по низу, и все рамки в ряду стоят на одной линии. */
.set-grid {
    display: grid;
    /* 165, а не 190: группа теперь живёт в колонке сетки, и её внутренние поля
       считают ширину от неё, а не от всей карточки. На 190 в колонку влезало
       одно поле, и группа из восьми вытягивалась в столбик на весь экран. */
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    align-items: end;
    gap: 8px 10px;
}
/* Шаблоны разбора — текст, а не число: им нужна ширина, а не место в общем ряду. */
.set-grid-wide { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
/* Поля-списки (причины отказа, словари) занимают всю строку сетки: рядом с
   полем под цифру они сжимались до колонки в 190px, и строка причины
   обрезалась на третьем слове. */
.set-grid > .reasons-field { grid-column: 1 / -1; }
.set-hint { margin: 0 0 8px; }

/* Присмотр за работой — свой карточкой и своим порядком.
   Это не набор величин, а один сценарий: тихо — спросили — не ответил —
   отобрали. Блоки идут по порядку сценария и пронумерованы, а под каждым стоит
   фраза, собранная из его же полей: проверить настройку, не воспроизводя её в
   уме, иначе нечем. */
.watch-blocks {
    display: grid;
    /* Шаги в ряд, слева направо, по порядку сценария: тихо — спросили — не
       ответил. Стопкой третий шаг с одним полем занимал строку в свой рост, а
       рядом с ним пустовало две трети ширины. Номера у заголовков держат
       порядок чтения и в ряду. */
    grid-template-columns: repeat(auto-fit, minmax(310px, 1fr));
    align-items: start;
    gap: 12px;
    margin-bottom: 14px;
}
.watch-block {
    padding: 11px 13px;
    background: var(--hover-bg);
    border-radius: 10px;
}
.watch-block-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 12px;
}
/* Номер шага кружком: он говорит, что блоки читаются подряд, а не выбираются. */
.watch-step {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    flex: none;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 12px;
    font-weight: 700;
}
.watch-block-title { font-size: 15px; font-weight: 600; color: var(--text); }
/* Поле блока — две строки одной высоты: подпись и рамка. Без этого поле с
   подписью в одну строку поднимало свою рамку выше соседних, и ряд настроек шёл
   лесенкой. Подпись мерится двумя строками — длиннее их здесь ничего нет. */
.watch-block .set-grid { align-items: start; }
.watch-block .field {
    display: grid;
    grid-template-rows: minmax(2.7em, auto) auto;
    align-content: start;
}
/* Итог блока — не подпись поля, а вывод из всех полей сразу, и стоит он под
   ними, отбитый линией: иначе читается как ещё одна подсказка к последнему. */
.watch-sum {
    margin: 12px 0 0;
    padding-top: 10px;
    border-top: 1px solid var(--border);
    font-size: 13.5px;
    line-height: 1.5;
    color: var(--text);
}
.watch-sum:empty { display: none; }
.watch-outcomes {
    padding: 16px 18px;
    background: var(--hover-bg);
    border-radius: 12px;
}

/* «Приём заявок» и «Пуши манагерам» — в одну строку. Первый занимал целую
   строку карточки под две галки, а строки второго растягивались на всю её
   ширину, и тумблер уезжал от своей подписи на пол-экрана. */
.set-pair {
    display: grid;
    grid-template-columns: minmax(240px, 1fr) minmax(400px, 1.7fr);
    gap: 14px;
    align-items: start;
}
@media (max-width: 900px) {
    .set-pair { grid-template-columns: 1fr; }
}

/* Кнопка «добавить» под списком — со своей отбивкой: вплотную к последней
   строке она читается как её продолжение, а не как действие над списком. */
.set-group > .kind-form-actions,
.watch-outcomes > .kind-form-actions,
.reasons-field .kind-form-actions { margin-top: 12px; }

/* Настройки-тумблеры (пуши манагерам, приём заявок) — список строк, а не сетка
   полей: у события есть подпись в шесть слов и состояние «да/нет», и ячейка в
   190px под такое не годится вовсе. */
.set-toggles {
    display: grid;
    gap: 2px;
    /* Потолок ширины: на всю карточку подпись остаётся у левого края, а тумблер
       уезжает к правому, и связать их глазом уже нельзя. */
    max-width: 520px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--card);
    overflow: hidden;
}
.set-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    padding: 7px 11px;
    font-size: 13px;
}
/* Тумблеры под сеткой полей (правило сдачи под сроками) — со своей отбивкой:
   вплотную к рамке последнего поля строка читается как его подпись. */
.set-grid + .set-toggles,
.set-toggles + .set-grid { margin-top: 10px; }
.set-toggle + .set-toggle { border-top: 1px solid var(--border); }
.set-toggle:hover { background: var(--hover-bg); }
/* Подпись строки не растягивается на всю ширину: тумблер отжат к правому краю
   самим space-between, а подписи нужна только её собственная ширина — иначе
   значок подсказки уезжает от последнего слова. */
.set-toggle .field-name { min-width: 0; }
/* Свой тумблер отбит от общих: он про одного человека, и стоять с ними в одном
   списке значит обещать, что он такой же. */
.set-toggles-own { margin-top: 10px; }
.notify-cols {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    align-items: start;
    gap: 12px 16px;
}
.notify-cols .set-reach { margin-top: 10px; }
/* «Сейчас эти пуши получают N чел.» — итог блока, а не подпись к последнему
   тумблеру: своя отбивка сверху отделяет его от строки, под которой он стоит. */
.set-reach { margin: 12px 0 0; }

/* Единица измерения справа внутри рамки. Подпись от неё освободилась, и все
   поля группы стали одной ширины. */
.field-box { position: relative; display: block; }
.field-box.has-unit input { padding-right: 44px; }
.field-unit {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 12px;
    font-style: normal;
    font-weight: 600;
    color: var(--text2);
    pointer-events: none;
}
.field-box select, .field-box .input {
    width: 100%;
    padding: 10px 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
}
/*
 * Кастомный список внутри поля формы. Рамка, радиус и отступы — как у input в
 * соседней ячейке: два разных прямоугольника в одной строке настроек читаются
 * как две разные формы. Собственный min-width списка здесь снимается — колонка
 * сетки бывает уже двухсот пикселей, и он распирал бы её.
 */
.field-box .cs { min-width: 0; }
.field-box .cs-trigger { padding: 10px 13px; border-radius: 10px; }

/*
 * Поля настроек ниже общих: их тут три десятка в столбик, и каждый лишний
 * пиксель отступа множится на тридцать. Правило живёт под `.set-grid`, чтобы
 * формы заявок и пополнений остались как были.
 */
.set-grid .field > span { font-size: 12.5px; }
.set-grid .field { gap: 3px; }
.set-grid .field input,
.set-grid .field-box select,
.set-grid .field-box .input { padding: 7px 10px; font-size: 13px; border-radius: 8px; }
.set-grid .field-box .cs-trigger { padding: 7px 10px; border-radius: 8px; }
.set-grid .field-box.has-unit input { padding-right: 40px; }
.set-grid .field-unit { right: 9px; font-size: 11px; }

.set-grid .field textarea.input {
    width: 100%;
    padding: 10px 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 13px;
    resize: vertical;
    outline: none;
}
.set-grid .field textarea.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }

@media (max-width: 700px) {
    .set-grid { grid-template-columns: 1fr 1fr; }
    .set-grid-wide { grid-template-columns: 1fr; }
    .shift-toggle { font-size: 11px; padding: 3px 9px; }
}

/* === Подсказка по нику в форме пополнения ==================================
   Раньше в поле вбивали числовой id: его знает бот, а сотрудник знает @ника.
   Список висит над панелью фильтров, поэтому позиционируется абсолютом от
   самого поля, а не встраивается в поток — иначе он раздвигал бы тулбар. */
.wa-pick { position: relative; display: flex; flex: 1 1 220px; min-width: 0; }
.wa-pick input { width: 100%; }
.wa-suggest {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 40;
    max-height: 280px;
    overflow-y: auto;
    background: var(--card);
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.14);
    padding: 4px;
}
[data-theme="dark"] .wa-suggest { box-shadow: 0 8px 28px rgba(0, 0, 0, 0.45); }
.wa-suggest-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text);
    font: inherit;
    font-size: 13.5px;
    text-align: left;
    cursor: pointer;
}
.wa-suggest-row:hover { background: var(--hover-bg); }
.wa-suggest-name { font-weight: 600; white-space: nowrap; }
/* Остаток — то, ради чего сюда смотрят вторым взглядом: сколько у него уже есть. */
.wa-suggest-balance { margin-left: auto; flex-shrink: 0; color: var(--text2); }
.wa-suggest-empty { padding: 10px; color: var(--text2); font-size: 13px; }


/* --- все заявки ------------------------------------------------------------
   Экран отвечает на «где вообще всё»: список в порядке «сколько это уже стоит»,
   и группировка, если спросили про кого-то конкретно. Плиток сводки над ним
   больше нет: те же четыре числа стоят на дашборде, а здесь они занимали первый
   экран и подменяли собой фильтры, которые тут же под ними. */

/* Группа — строка-разделитель внутри одной таблицы, а не своя таблица: колонки
   должны стоять по всей странице ровно, иначе соседние группы нечем сравнить. */
.rq-group-row td {
    background: var(--hover-bg);
    cursor: pointer;
    font-size: 13px;
    padding-top: 9px;
    padding-bottom: 9px;
}
.rq-group-row .rq-fold-caret { display: inline-block; margin-right: 8px; vertical-align: middle; }

/* --- готовые ответы клиенту в настройках -----------------------------------
   Строка списка целиком в один ряд: подпись, три перевода и выключатель. Ряды
   переставляют стрелками — порядок здесь и есть порядок кнопок у работяги. */
.reply-list { display: flex; flex-direction: column; gap: 8px; }
/* Подписи полей — только у первой строки: дальше колонки те же, и «Текст
   клиенту (ru)» над каждой из десяти строк — это десять повторов одного слова
   ценой в строку высоты каждый. */
.reply-list > .reply-row:not(:first-child) .field > span { display: none; }
.reply-row {
    display: flex;
    align-items: flex-end;
    gap: 10px;
    flex-wrap: wrap;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
}
.reply-off { opacity: 0.55; }

/* Причины полного отказа: тот же список, но строка в одну фразу — переносить
   её на вторую линию незачем, поэтому без flex-wrap и с растянутым полем. */
/* Причины — строки в пять слов: столбиком они занимали высоту в свой список,
   тогда как в ряд их встаёт три. */
.reason-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
    gap: 8px;
}
.reason-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 8px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}
.reason-row input { flex: 1 1 auto; min-width: 0; }
.reasons-field { grid-column: 1 / -1; }
.reply-order { display: flex; flex-direction: column; gap: 2px; }
.reply-move {
    width: 22px;
    height: 18px;
    border: 1px solid var(--border);
    border-radius: 5px;
    background: var(--card);
    color: var(--text2);
    cursor: pointer;
    font-size: 10px;
    line-height: 1;
}
.reply-move:disabled { opacity: 0.4; cursor: default; }
.reply-label { flex: 0 0 170px; }
.reply-text { flex: 1 1 220px; min-width: 160px; }
.reply-on { display: flex; align-items: center; gap: 6px; font-size: 12.5px; white-space: nowrap; }
.reply-on input { accent-color: var(--accent); }

/* --- шаблоны разбора входящих ----------------------------------------------
   Строка в одну линию: подписи полей ушли в подсказку внутри рамки. Их было по
   две над каждой строкой, и набор из четырёх шаблонов занимал пол-экрана одними
   заголовками — при том что читают в нём кусок сообщения, а не слово «Кусок
   сообщения». */
/* Два набора рядом, а не друг под другом: в каждом одна строка настроек, и
   стопкой они занимали высоту вчетверо больше своего содержимого. */
.tpl-sets {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 12px 16px;
}
.tpl-set-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 6px;
}
.tpl-set-name {
    font-size: 12px;
    font-weight: 700;
    color: var(--text2);
    text-transform: uppercase;
    letter-spacing: 0.5px;
}
.tpl-empty { font-size: 13px; margin: 0; }
.tpl-list { display: flex; flex-direction: column; gap: 6px; }
.tpl-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 8px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
}
.tpl-off { opacity: 0.55; }
.tpl-row .input { padding: 7px 10px; font-size: 13.5px; }
/* Строка набора — не форма, а строчка: подпись и начало шаблона, всё остальное
   открывается окном. Писать шаблон в поле высотой в строку было нечем — письмо
   в него не влезает, а проверить его до следующего клиента было негде. */
.tpl-open {
    flex: 1 1 auto;
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
    padding: 6px 8px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.tpl-open:hover { background: var(--hover-bg); }
.tpl-open-label {
    flex: 0 0 160px;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 13.5px;
    font-weight: 600;
}
.tpl-open-body {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--font-mono);
    font-size: 12.5px;
    color: var(--text2);
}
.tpl-on { flex: 0 0 auto; }

/* Окно шаблона: поля во всю ширину, шаблон и проверочное сообщение — моноширинно
   и в несколько строк. Ради этого окно и заводили. */
/* Окно под шаблон шире и с прокруткой: четыре поля и ответ проверки в 460 px
   для сообщения не помещаются, а на коротком экране окно уезжало за край. */
.modal:has(.tpl-edit) {
    width: min(680px, calc(100vw - 32px));
    max-height: calc(100vh - 48px);
    display: flex;
    flex-direction: column;
}
.modal:has(.tpl-edit) .modal-body { overflow-y: auto; }
.tpl-edit { display: flex; flex-direction: column; gap: 12px; text-align: left; }
.tpl-edit .field { display: flex; flex-direction: column; gap: 4px; }
.tpl-edit .field > span { font-size: 12px; color: var(--text2); }
/* Базовое правило поля (строка 830) на textarea не распространяется — только на
   input, — поэтому рамку, отступы и фокус повторяем здесь, иначе поле приезжает
   с браузерным дефолтом. */
.tpl-edit-area {
    width: 100%;
    min-height: 76px;
    resize: vertical;
    padding: 10px 14px;
    border: 1.5px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    outline: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.45;
}
.tpl-edit-area::placeholder { color: var(--text2); }
.tpl-edit-area:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-light);
}
/* Кнопку скобки кладём в угол самого поля: она про то, что в этом поле, и
   отдельной строкой под ним висела бы ни к чему не привязанной. */
.tpl-edit-area-wrap { position: relative; }
.tpl-edit-area-wrap .tpl-edit-area { padding-top: 34px; }
.tpl-holder-btn {
    position: absolute;
    top: 6px;
    right: 6px;
}
.tpl-hint { display: block; margin-top: 4px; font-size: 12px; line-height: 1.4; }
/* Проверочное поле отделено фоном: это не часть шаблона, а сообщение, на
   котором его меряют, и слитый с телом шаблона вид путал одно с другим. */
.tpl-edit-sample { background: var(--surface); }

/* «Что видит разбор» — под стрелкой: нужно оно, когда шаблон не подошёл к
   письму, а не каждый раз. Моноширинно и с переносами: там и теги, и кракозябры
   до чистки. */
.tpl-seen { margin-top: 8px; }
.tpl-seen > summary {
    cursor: pointer;
    font-size: 12px;
    color: var(--text2);
    list-style: none;
}
.tpl-seen > summary::-webkit-details-marker { display: none; }
.tpl-seen > summary::before { content: '▸ '; }
.tpl-seen[open] > summary::before { content: '▾ '; }
.tpl-seen-text {
    margin-top: 6px;
    max-height: 160px;
    overflow: auto;
    padding: 8px 10px;
    border-radius: 8px;
    background: var(--surface);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.4;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Ответ проверки. Рамка красится по исходу: «подошло» и «не подошло» читают
   боковым зрением, не вчитываясь в текст под заголовком. */
.tpl-check {
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 10px 12px;
    font-size: 13px;
}
.tpl-check p { margin: 4px 0 0; }
.tpl-check-on { border-color: var(--success); }
.tpl-check-off { border-color: var(--border); }
.tpl-check-head { font-weight: 700; }
.tpl-check-on .tpl-check-head { color: var(--success); }
.tpl-check-off .tpl-check-head { color: var(--text2); }
.tpl-check-values { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.tpl-check-value, .tpl-check-feed {
    display: flex;
    align-items: baseline;
    gap: 8px;
    min-width: 0;
}
.tpl-check-feed { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.tpl-check-name { flex: 0 0 auto; color: var(--text2); font-size: 12px; }
.tpl-check-got {
    flex: 1 1 auto;
    min-width: 0;
    font-family: var(--font-mono);
    overflow-wrap: anywhere;
}
/* Удаление красится только под курсором: три красных крестика в столбик — это
   первое, что видно в наборе, при том что жмут их раз в полгода. */
.tpl-drop { flex: 0 0 auto; color: var(--text2); }
.tpl-drop:hover { border-color: var(--danger); color: var(--danger); }

/* Пояснение к приёму — под стрелкой: его читают один раз, а мешает оно каждый
   раз. Заголовок остаётся видимым, чтобы было понятно, что там объясняют. */
.set-about { margin: 0 0 8px; }
.set-about > summary {
    cursor: pointer;
    font-size: 13px;
    color: var(--text2);
    list-style: none;
}
.set-about > summary::-webkit-details-marker { display: none; }
.set-about > summary::before {
    content: '▸';
    display: inline-block;
    margin-right: 6px;
    transition: transform 0.15s ease;
}
.set-about[open] > summary::before { transform: rotate(90deg); }
.set-about > summary:hover { color: var(--text); }
.set-about > p {
    margin: 8px 0 0;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text2);
    max-width: 90ch;
}

/* =============================================================================
   Рассылка
   ========================================================================== */

.bc-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 20px; }
.bc-head h2 { margin: 0; }
.bc-head-note { font-size: 13px; color: var(--text2); }

/* Редактор слева, предпросмотр справа: «как это увидит клиент» отвечает на
   вопрос, который задают, пока пишут, а не после. */
.bc-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 400px);
    gap: 28px;
    align-items: start;
}

@media (max-width: 1180px) {
    .bc-grid { grid-template-columns: minmax(0, 1fr); }
}

.bc-compose { min-width: 0; }

.bc-section {
    font-size: 12px;
    font-weight: 650;
    letter-spacing: 0.6px;
    text-transform: uppercase;
    color: var(--text2);
    margin-bottom: 12px;
}

.bc-section-gap { margin-top: 26px; }

/* Поля сегмента: одна сетка, одна высота. Раньше подпись, селект и число стояли
   на трёх разных уровнях — селект был «инлайновым» (36px), а поле суммы обычным
   (44px), и ряд читался как сломанный. */
.bc-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
    align-items: start;
}

.bc-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }

.bc-field-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--text2);
    line-height: 18px;
}

.bc-field .cs { min-width: 0; }
.bc-field-hint { font-size: 12px; color: var(--text2); }

.bc-amount {
    height: 44px;
    padding: 0 13px;
    border: 1.5px solid var(--border-strong);
    border-radius: 12px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
    outline: none;
    width: 100%;
}

.bc-amount:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }

/* Панель, поле и подвал — одна рамка: это один инструмент, а тремя коробками
   он читался как три. */
.bc-editor {
    border: 1.5px solid var(--border-strong);
    border-radius: 14px;
    background: var(--input-bg);
    overflow: hidden;
    transition: border-color 0.15s, box-shadow 0.15s;
}

.bc-editor:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); }

.bc-toolbar {
    display: flex;
    align-items: center;
    gap: 2px;
    padding: 8px 10px;
    border-bottom: 1px solid var(--border);
    background: var(--card);
}

.bc-tool {
    width: 34px;
    height: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid transparent;
    border-radius: 9px;
    background: transparent;
    color: var(--text2);
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}

.bc-tool svg { width: 18px; height: 18px; }
.bc-tool:hover { background: var(--accent-light); color: var(--accent); }
.bc-tool:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bc-tool:active { transform: translateY(0.5px); }
.bc-tool-last { margin-left: auto; }

.bc-tool-sep {
    width: 1px;
    height: 20px;
    margin: 0 6px;
    background: var(--border);
    flex: none;
}

/* Поле моноширинное: в нём набирают разметку, и звёздочка должна стоять ровно
   там, где её видно. Красивое — в предпросмотре справа. */
.bc-text {
    display: block;
    width: 100%;
    min-height: 150px;
    padding: 14px 16px;
    border: none;
    background: transparent;
    color: var(--text);
    font-family: var(--font-mono);
    font-size: 13px;
    line-height: 1.6;
    resize: none;
    outline: none;
}

.bc-text::placeholder { color: var(--text2); }

.bc-editor-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 14px;
    border-top: 1px solid var(--border);
    background: var(--card);
    font-size: 12px;
    color: var(--text2);
}

.bc-length.bc-over { color: var(--danger); font-weight: 600; }

/* Палитра значков: слоем у кнопки, поверх поля — раздвигать ради неё редактор
   значило бы дёргать текст под курсором. */
.bc-emoji {
    position: fixed;
    z-index: 90;
    width: 320px;
    max-height: 320px;
    display: flex;
    flex-direction: column;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

.bc-emoji-head { padding: 10px; border-bottom: 1px solid var(--border); }

.bc-emoji-search {
    width: 100%;
    height: 34px;
    padding: 0 12px;
    border: 1.5px solid var(--border-strong);
    border-radius: 9px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
    outline: none;
}

.bc-emoji-search:focus { border-color: var(--accent); }
.bc-emoji-grid { overflow-y: auto; padding: 8px 10px 12px; }

.bc-emoji-group {
    font-size: 11px;
    font-weight: 650;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--text2);
    margin: 10px 0 6px;
}

.bc-emoji-row { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }

.bc-emoji-item {
    height: 34px;
    border: none;
    border-radius: 8px;
    background: transparent;
    font-size: 19px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.1s;
}

.bc-emoji-item:hover { background: var(--accent-light); }
.bc-emoji-empty { padding: 16px 4px; color: var(--text2); font-size: 13px; text-align: center; }

.bc-cheatsheet {
    margin-top: 14px;
    padding: 14px 16px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 8px 20px;
}

.bc-cheat-row { display: flex; align-items: baseline; gap: 10px; font-size: 12.5px; }
.bc-cheat-row code {
    font-family: var(--font-mono);
    color: var(--accent);
    white-space: nowrap;
}
.bc-cheat-row span { color: var(--text2); }
.bc-cheatsheet-note { grid-column: 1 / -1; margin: 6px 0 0; font-size: 12px; color: var(--text2); }

.bc-actions { display: flex; align-items: center; gap: 12px; margin-top: 18px; flex-wrap: wrap; }
.bc-count, .bc-noright { font-size: 13px; color: var(--text2); }

/* Предпросмотр повторяет чат телеграма: лист бумаги на вопрос «как это придёт»
   не отвечает. */
.bc-side { min-width: 0; }

.bc-preview {
    padding: 18px 16px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--bg);
    min-height: 150px;
}

.bc-bubble {
    padding: 11px 14px;
    border-radius: 14px 14px 14px 5px;
    background: var(--card);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-sm, 0 1px 2px rgba(0, 0, 0, 0.06));
    font-size: 14px;
    line-height: 1.55;
    word-break: break-word;
}

.bc-bubble b { font-weight: 650; }
.bc-bubble a { color: var(--accent); }

.bc-bubble pre {
    margin: 8px 0;
    padding: 10px 12px;
    border-radius: 10px;
    background: var(--bg);
    border: 1px solid var(--border);
    font-family: var(--font-mono);
    font-size: 12px;
    overflow-x: auto;
    white-space: pre-wrap;
}

.bc-bubble code { font-family: var(--font-mono); font-size: 12.5px; }

.bc-bubble blockquote {
    margin: 8px 0;
    padding: 2px 0 2px 12px;
    border-left: 3px solid var(--accent);
    color: var(--text2);
}

/* Спойлер закрыт, пока на него не навели: предпросмотр должен показывать и то,
   чего клиент сразу не увидит. */
.bc-bubble .tg-spoiler {
    background: var(--text2);
    color: transparent;
    border-radius: 4px;
    cursor: pointer;
    transition: color 0.15s, background 0.15s;
}

.bc-bubble .tg-spoiler:hover { background: transparent; color: inherit; }

.bc-side-note { margin: 10px 0 0; font-size: 12px; color: var(--text2); line-height: 1.5; }
.bc-empty { color: var(--text2); font-size: 13px; margin: 0; }

/* История: колонки заданы долями, иначе текст рассылки съедал время и автора. */
.bc-table { table-layout: fixed; width: 100%; }
.bc-table td { vertical-align: top; }
.bc-col-when { width: 150px; white-space: nowrap; }
.bc-col-who { width: 130px; }
.bc-col-seg { width: 260px; }
.bc-col-progress { width: 150px; }
.bc-col-text { width: auto; }

.bc-chips { display: flex; flex-wrap: wrap; gap: 5px; }

.bc-chip {
    display: inline-flex;
    align-items: center;
    height: 22px;
    padding: 0 9px;
    border-radius: 11px;
    background: var(--bg);
    border: 1px solid var(--border);
    font-size: 12px;
    color: var(--text2);
    white-space: nowrap;
}

.bc-chip-who { background: var(--accent-light); border-color: transparent; color: var(--accent); }

.bc-progress { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.bc-failed { font-size: 12px; color: var(--danger); }
.bc-share { font-size: 12px; color: var(--text2); }

.bc-past {
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.bc-past.is-open { display: block; }

.bc-past-more {
    margin-top: 4px;
    padding: 0;
    border: none;
    background: none;
    color: var(--accent);
    font-size: 12px;
    cursor: pointer;
}

.bc-past-more:hover { text-decoration: underline; }

/* === Сотрудники: колонка «доступ» =========================================
   Вход и второй фактор съехались в одну выпадайку: её подпись — само
   состояние, поэтому и цвет у подписи тот же, что был у плашек, из которых
   ячейка собрана. Правило бьёт .cs-placeholder — подпись здесь не заглушка. */
/* Ширина под самую длинную подпись: перенос делал строку втрое выше. */
td .cs.cs-access { min-width: 202px; }
.cs.cs-access .cs-label { white-space: nowrap; }
.access-flat { font-size: 13px; white-space: nowrap; }

.cs-tone-ok   .cs-label, .access-flat.cs-tone-ok   { color: #1b8a33; }
.cs-tone-warn .cs-label, .access-flat.cs-tone-warn { color: #c67d00; }
.cs-tone-bad  .cs-label, .access-flat.cs-tone-bad  { color: #d32f2f; }
.cs-tone-off  .cs-label, .access-flat.cs-tone-off  { color: var(--text2); }

[data-theme="dark"] .cs-tone-ok   .cs-label,
[data-theme="dark"] .access-flat.cs-tone-ok   { color: #30d158; }
[data-theme="dark"] .cs-tone-warn .cs-label,
[data-theme="dark"] .access-flat.cs-tone-warn { color: #ffb340; }
[data-theme="dark"] .cs-tone-bad  .cs-label,
[data-theme="dark"] .access-flat.cs-tone-bad  { color: #ff6961; }

/* Меню действий с непрочитанным письмом видно со строки: число самого письма
   лежит внутри списка, и закрытое меню иначе ничем не отличается от соседних. */
.cs.cs-accent .cs-trigger { border-color: var(--accent); }
.cs.cs-accent .cs-label { color: var(--accent); }

/* === Личное дело ==========================================================
   Окно с таблицей, а не с фразой: в общие 460 px четыре колонки рассыпались
   по слогам, а список длиннее экрана уезжал за край вместе с кнопкой. */
.modal:has(.record-log) {
    width: min(720px, calc(100vw - 32px));
    max-height: calc(100vh - 48px);
    display: flex;
    flex-direction: column;
}
/* Вбок прокручивается таблица, а не тело окна: overflow-y:auto по спецификации
   делает overflow-x таким же, и лишний пиксель ширины вешал полосу под кнопки. */
.modal:has(.record-log) .modal-body { overflow-y: auto; overflow-x: hidden; min-height: 0; }

.record-log table { width: 100%; }
/* Дата, номер и род — по содержимому и в одну строку; вся свободная ширина
   достаётся причине, единственной колонке с живым текстом. */
.record-log th, .record-log td { white-space: nowrap; }
.record-log .rec-reason { white-space: normal; width: 100%; min-width: 200px; }
/* На узком экране колонки перестают держать строку: прокрутка вбок в окне,
   которое и так открыто поверх всего, хуже переноса. */
@media (max-width: 620px) {
    .record-log th, .record-log td { white-space: normal; padding: 8px 10px; }
    .record-log .rec-reason { min-width: 0; }
}
/* Шапка держится при прокрутке: без неё к середине списка непонятно, что есть что. */
.record-log thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--card);
}

/* --- обучение ------------------------------------------------------------------
   Затемнение — четыре прямоугольника вокруг выреза, а не одно полотно с дыркой:
   дырка, вырезанная маской, всё равно ловит нажатия, и подсвеченная кнопка
   переставала нажиматься ровно тогда, когда её просят нажать.

   Слой ниже окон (4000): «Выйти из обучения» спрашивают диалогом, и он обязан
   встать поверх подсветки, а не под ней. */
#tour-root { position: fixed; inset: 0; z-index: 4200; pointer-events: none; }

/* Затемнение кликов не ловит: во время обучения по элементам под ним можно
   нажимать — и подсвеченный шаг, и что угодно ещё. Тур это не сбрасывает: он
   держит свой шаг и перерисовывает рамку на любое изменение страницы. */
.tour-shade {
    position: fixed;
    background: rgba(0, 0, 0, 0.55);
    pointer-events: none;
    transition: all .18s ease;
}

/* Рамка вокруг выреза: сама ничего не ловит — под ней живая кнопка. */
.tour-ring {
    position: fixed;
    border: 2px solid var(--accent);
    border-radius: 12px;
    box-shadow: 0 0 0 3px var(--accent-light);
    pointer-events: none;
    transition: all .18s ease;
}

.tour-tip {
    position: fixed;
    width: min(440px, calc(100vw - 24px));
    padding: 18px 20px 14px;
    background: var(--card);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
}

/* Шаг без подсветки: рассказ, к которому нечего показать. */
.tour-tip-center {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.tour-tip-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.tour-tip-step { font-size: 12.5px; color: var(--text2); flex: 1; font-weight: 600; }

.tour-tip-x {
    border: 0;
    background: none;
    color: var(--text2);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    padding: 2px 4px;
}
.tour-tip-x:hover { color: var(--text); }

.tour-tip h3 { margin: 0 0 11px; font-size: 19px; color: var(--text); }

/* Текст крупнее и почти чёрный: серым в подсказке читают через строчку. Абзацы
   отбиты друг от друга — уточнение не должно слипаться с правилом. */
.tour-tip-body p {
    margin: 0 0 10px;
    font-size: 16px;
    line-height: 1.62;
    color: var(--text);
}
.tour-tip-body p:last-child { margin-bottom: 0; }

/* Выделения: акцентом — суть, предупреждением — то, на чём обжигаются. */
.tour-em { color: var(--accent); font-weight: 650; }
.tour-warn { color: var(--danger, #d1453b); font-weight: 650; }

/* Пункты: маркер акцентного цвета, чтобы список читался списком. Правила висят
   на самих ul/li внутри тела, а не на классе: тело приезжает из настроек уже
   как HTML с голыми <ul><li>, и точки браузера по умолчанию уезжали за левый
   край подсказки. */
.tour-tip-body ul {
    margin: 2px 0 10px;
    padding-left: 2px;
    list-style: none;
}
.tour-tip-body ul:last-child { margin-bottom: 0; }
.tour-tip-body li {
    position: relative;
    padding-left: 22px;
    margin-bottom: 7px;
    font-size: 16px;
    line-height: 1.55;
    color: var(--text);
}
.tour-tip-body li::before {
    content: '';
    position: absolute;
    left: 6px;
    top: 10px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
}
.tour-tip-body li:last-child { margin-bottom: 0; }

/* Развилки: ветку выбирают из подсказки, и кнопки идут столбцом — три штуки в
   строку не помещаются, а обрезанная подпись ветки не отличается от соседней. */
.tour-forks { display: flex; flex-direction: column; gap: 6px; margin-top: 12px; }
.tour-forks .btn { justify-content: flex-start; text-align: left; }

.tour-tip-foot { display: flex; align-items: center; gap: 8px; margin-top: 14px; }

/* --- помощь --------------------------------------------------------------------
   Инструкцию читают подряд и длинно, оттого две колонки: слева липкое
   оглавление, справа узкая полоса текста. Строка во всю ширину монитора
   теряется на переносе, а пятнадцать равных заголовков подряд читаются как
   список — блок даёт им границы и свой цвет.

   Цвет блока живёт в одной переменной --tone: её ставит класс tone-*, а всё
   остальное — полоска, точка, подсветка в оглавлении — считается от неё. Так
   новый тон добавляется одной строкой, а не десятью. */
.tone-blue   { --tone: var(--accent); }
.tone-violet { --tone: #7c5cff; }
.tone-green  { --tone: #23a55a; }
.tone-orange { --tone: #d98a00; }
.tone-teal   { --tone: #0d9488; }
.tone-red    { --tone: var(--danger); }
[data-theme="dark"] .tone-violet { --tone: #a78bfa; }
[data-theme="dark"] .tone-green  { --tone: #3fce7c; }
[data-theme="dark"] .tone-orange { --tone: #ffab2e; }
[data-theme="dark"] .tone-teal   { --tone: #2dd4bf; }

.help-page { display: flex; flex-direction: column; gap: 18px; }

/* Шапка: заливка уходит вправо-вверх, чтобы кнопка обучения не висела на пустом
   белом поле, а читалась как продолжение заголовка. */
.help-hero {
    display: flex;
    align-items: center;
    gap: 20px;
    background:
        radial-gradient(120% 180% at 100% 0%, var(--accent-light) 0%, transparent 60%),
        var(--card);
}
.help-hero-text h2 { margin: 0 0 6px; }
.help-hero-text p { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--text2); max-width: 62ch; }
.help-hero-btn { margin-left: auto; white-space: nowrap; }

.help-layout {
    display: grid;
    grid-template-columns: 236px minmax(0, 1fr);
    gap: 20px;
    align-items: start;
}

/* Оглавление липнет под шапку панели: её высота 52px плюс воздух. */
.help-side-in { position: sticky; top: 68px; display: flex; flex-direction: column; gap: 12px; }

.help-find {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13px;
}
.help-find:focus { outline: 2px solid var(--accent-light); outline-offset: -1px; border-color: var(--accent); }
.help-find::-webkit-search-cancel-button { filter: grayscale(1) opacity(0.5); }

/* Свёртка оглавления: на широком экране её нет — заголовок скрыт, а `open`
   ставит скрипт по ширине окна. */
.help-fold > summary {
    display: none;
    list-style: none;
    cursor: pointer;
    padding: 9px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 10px;
    background: var(--card);
    font-size: 13.5px;
    font-weight: 600;
}
.help-fold > summary::-webkit-details-marker { display: none; }
.help-fold > summary::after { content: '▾'; float: right; color: var(--text2); }
.help-fold[open] > summary::after { content: '▴'; }

/* Глобальный `nav {}` — про главное меню панели: заливка, радиус и отступ.
   Оглавление тоже <nav>, и без снятия этих правил под ним висела серая плашка. */
.help-nav {
    display: flex;
    flex-direction: column;
    gap: 14px;
    background: none;
    border-radius: 0;
    padding: 0;
    margin: 0;
}
.help-nav-group { display: flex; flex-direction: column; gap: 1px; padding-left: 10px; border-left: 2px solid var(--border); }
.help-nav-group.is-on { border-left-color: var(--tone); }
.help-nav-title {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--text2);
    padding: 0 8px 4px;
}
.help-nav-group.is-on .help-nav-title { color: var(--tone); }
.help-nav-link {
    font-size: 13px;
    line-height: 1.35;
    color: var(--text2);
    text-decoration: none;
    padding: 5px 8px;
    border-radius: 7px;
}
.help-nav-link:hover { color: var(--text); background: var(--hover-bg); }
/* Отметка «вы здесь»: без неё колонка слева — просто список ссылок, по которому
   непонятно, докуда дочитали. */
.help-nav-link.is-on {
    color: var(--tone);
    font-weight: 600;
    background: color-mix(in srgb, var(--tone) 12%, transparent);
}

.help-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }

.help-group {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 4px 22px 6px;
    /* Тон блока — полоской сверху, а не заливкой: заливка под длинным текстом
       мешает читать, а полоска видна и на прокрутке. */
    border-top: 3px solid var(--tone);
}
.help-group-head { display: flex; align-items: center; gap: 9px; padding: 16px 0 4px; }
.help-group-mark { width: 8px; height: 8px; border-radius: 50%; background: var(--tone); flex: none; }
.help-group-head h3 {
    margin: 0;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--tone);
}

.help-sec { padding: 14px 0 16px; border-bottom: 1px solid var(--border); scroll-margin-top: 76px; }
.help-sec:last-child { border-bottom: 0; }
.help-sec h4 { margin: 0 0 10px; font-size: 16px; font-weight: 650; letter-spacing: -0.015em; }

.help-rich { max-width: 72ch; }
.help-rich p { margin: 0 0 9px; font-size: 14px; line-height: 1.65; color: var(--text2); }
.help-rich p:last-child { margin-bottom: 0; }
.help-rich b, .help-rich strong { color: var(--text); font-weight: 650; }
/* Маркер списка — точка тона блока: браузерная сидит на другой высоте и в
   тёмной теме сереет вместе с текстом. */
.help-rich ul { margin: 6px 0 10px; padding-left: 2px; list-style: none; }
.help-rich li { position: relative; padding-left: 20px; margin-bottom: 6px; font-size: 14px; line-height: 1.6; color: var(--text2); }
.help-rich li::before {
    content: '';
    position: absolute;
    left: 5px;
    top: 9px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--tone);
}
.help-rich li:last-child { margin-bottom: 0; }

/* Выноски: в инструкции есть строки, которые надо не прочитать, а заметить.
   Цветом текста такое не передаётся — красный абзац читается хуже обычного. */
.help-rich .help-warn, .help-rich .help-tip {
    margin: 10px 0;
    padding: 11px 14px;
    border-radius: 10px;
    border-left: 3px solid;
    font-size: 13.5px;
    line-height: 1.6;
}
.help-rich .help-warn {
    border-left-color: var(--danger);
    background: color-mix(in srgb, var(--danger) 9%, transparent);
}
.help-rich .help-tip {
    border-left-color: var(--tone);
    background: color-mix(in srgb, var(--tone) 9%, transparent);
}
.help-rich .help-warn p, .help-rich .help-tip p { font-size: 13.5px; color: var(--text); }
.help-rich .help-warn p:last-child, .help-rich .help-tip p:last-child { margin-bottom: 0; }

/* Название кнопки панели внутри текста: в инструкции их много, и в кавычках они
   не отличаются от обычной цитаты. */
.help-rich .help-kbd {
    display: inline-block;
    padding: 1px 7px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    font-size: 12.5px;
    font-weight: 600;
    white-space: nowrap;
}

.help-none { padding: 24px 4px; font-size: 13.5px; color: var(--text2); }

/* Поиск прячет непопавшее атрибутом hidden, а он проигрывает display у флексов —
   без этой строки отфильтрованная ветка оглавления оставалась бы на месте. */
.help-nav-group[hidden], .help-nav-link[hidden], .help-group[hidden], .help-sec[hidden] {
    display: none;
}

@media (max-width: 900px) {
    .help-layout { grid-template-columns: minmax(0, 1fr); }
    /* Липкая колонка на узком экране съедала бы половину высоты, поэтому
       оглавление становится обычным блоком в начале страницы. */
    .help-side-in { position: static; }
    .help-fold > summary { display: block; }
    .help-nav { flex-direction: row; flex-wrap: wrap; gap: 8px 14px; padding-top: 10px; }
    .help-nav-group { flex: 1 1 200px; }
    .help-hero { flex-direction: column; align-items: flex-start; }
    .help-hero-btn { margin-left: 0; }
    .help-group { padding: 4px 16px 6px; }
}

/* --- настройки: тексты помощи --------------------------------------------------
   Правится не только текст, но и состав: блоки и разделы заводятся, удаляются и
   переставляются отсюда. Отсюда две вложенности — блок с рамкой своего тона и
   свёрнутые разделы внутри. */
.help-ed-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 12px 0 10px; }

/* Отчёт сухого прогона в окне загрузки: две колонки «что — сколько». */
.io-report .io-file { font-family: var(--font-mono); font-size: 12.5px; color: var(--text2); margin-bottom: 10px; }
.io-report dl { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; margin: 0; }
.io-report dt { font-size: 13px; color: var(--text2); }
.io-report dd { font-size: 13px; margin: 0; }
.io-report .io-warn { margin-top: 10px; font-size: 12.5px; color: var(--warning); }
.help-eds { display: flex; flex-direction: column; gap: 14px; }

.help-ed-group {
    border: 1px solid var(--border);
    border-top: 3px solid var(--tone);
    border-radius: 12px;
    background: var(--card);
}
.help-ed-ghead { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 11px 12px; }
.help-ed-gmark { width: 8px; height: 8px; border-radius: 50%; background: var(--tone); flex: none; }
.help-ed-gtitle {
    flex: 1 1 150px;
    min-width: 120px;
    max-width: 230px;
    padding: 6px 9px;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 13.5px;
}
.help-ed-code { font-size: 11.5px; color: var(--text2); font-family: var(--font-mono); }

.help-ed-tones { display: flex; gap: 4px; }
.help-ed-tone {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 1px solid var(--border-strong);
    background: var(--tone);
    cursor: pointer;
    padding: 0;
}
.help-ed-tone.is-on { box-shadow: 0 0 0 2px var(--card), 0 0 0 3.5px var(--tone); }

.help-ed-icon {
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text2);
    border-radius: 7px;
    padding: 3px 8px;
    font-size: 13px;
    line-height: 1.3;
    cursor: pointer;
}
.help-ed-icon:hover { color: var(--text); border-color: var(--border-strong); }
.help-ed-icon.is-danger:hover { color: var(--danger); border-color: var(--danger); }

.help-ed-secs { display: flex; flex-direction: column; gap: 6px; padding: 0 12px 12px; }
.help-ed-add { align-self: flex-start; }

.help-ed-sec { border: 1px solid var(--border); border-radius: 10px; background: var(--bg); }
.help-ed-sec > summary {
    list-style: none;
    cursor: pointer;
    padding: 9px 12px;
    display: flex;
    align-items: center;
    gap: 10px;
}
.help-ed-sec > summary::-webkit-details-marker { display: none; }
.help-ed-sec > summary::before { content: '▸'; color: var(--text2); transition: transform .15s ease; }
.help-ed-sec[open] > summary::before { transform: rotate(90deg); }
.help-ed-name { font-weight: 600; font-size: 13.5px; }

.help-ed-form { padding: 0 12px 12px; display: flex; flex-direction: column; gap: 14px; }
.help-ed-lang { display: flex; flex-direction: column; gap: 6px; }
.help-ed-tag { font-size: 11px; font-weight: 700; color: var(--text2); letter-spacing: .04em; }
.help-ed-title {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--input-bg);
    color: var(--text);
    font-family: inherit;
    font-size: 14px;
}
.help-ed-body {
    min-height: 90px;
    max-height: 300px;
    padding: 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 0 0 8px 8px;
    background: var(--input-bg);
    color: var(--text);
    font-size: 14px;
    line-height: 1.55;
    overflow-y: auto;
}
.help-ed-body:focus { outline: 2px solid var(--accent-light); outline-offset: -1px; }
.help-ed-body p { margin: 0 0 8px; }
.help-ed-body p:last-child { margin-bottom: 0; }
.help-ed-body ul { margin: 4px 0 8px; padding-left: 20px; }
.help-ed-body li { margin-bottom: 4px; }
/* Выноска в поле правки выглядит так же, как на странице: иначе её ставят
   вслепую и проверяют переключением на «Помощь». */
.help-ed-body .help-warn, .help-ed-body .help-tip {
    margin: 8px 0;
    padding: 8px 12px;
    border-radius: 8px;
    border-left: 3px solid var(--danger);
    background: color-mix(in srgb, var(--danger) 9%, transparent);
}
.help-ed-body .help-tip {
    border-left-color: var(--accent);
    background: var(--accent-light);
}
.help-ed-body .help-kbd {
    padding: 1px 7px;
    border: 1px solid var(--border-strong);
    border-radius: 6px;
    background: var(--surface);
    font-size: 12.5px;
    font-weight: 600;
}

.tour-fmt.is-warn { color: var(--danger); }
.tour-fmt.is-tip { color: var(--accent); }
.tour-fmt.is-kbd { font-weight: 600; }

.help-ed-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.help-ed-move { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text2); }
.help-ed-move select { padding: 5px 26px 5px 8px; font-size: 12.5px; }
.help-ed-saved { font-size: 13px; color: var(--accent); }

@media (max-width: 620px) {
    .help-ed-gtitle { flex-basis: 100%; max-width: none; }
}

/* --- настройки: тексты тура ----------------------------------------------------
   Редактор как «Тексты бота», но тело — WYSIWYG-поле с форматированием. Каждый
   шаг свёрнут: сорок шагов развёрнутыми — экран, по которому только скроллят. */
.tour-eds { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }

.tour-ed { border: 1px solid var(--border); border-radius: 12px; background: var(--card); }
.tour-ed > summary {
    list-style: none;
    cursor: pointer;
    padding: 12px 14px;
    display: flex;
    align-items: baseline;
    gap: 10px;
}
.tour-ed > summary::-webkit-details-marker { display: none; }
.tour-ed > summary::before {
    content: '▸';
    color: var(--text2);
    transition: transform .15s ease;
}
.tour-ed[open] > summary::before { transform: rotate(90deg); }
.tour-ed-name { font-weight: 600; font-size: 14px; }
.tour-ed-key { font-size: 12px; color: var(--text2); font-family: ui-monospace, monospace; }

.tour-ed-body-wrap { padding: 0 14px 14px; display: flex; flex-direction: column; gap: 14px; }
.tour-ed-lang { display: flex; flex-direction: column; gap: 6px; }
.tour-ed-tag { font-size: 11px; font-weight: 700; color: var(--text2); letter-spacing: .04em; }

.tour-ed-title {
    width: 100%;
    padding: 8px 10px;
    border: 1px solid var(--border-strong);
    border-radius: 8px;
    background: var(--input-bg);
    color: var(--text);
    font-size: 14px;
}

.tour-ed-toolbar {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
    padding: 4px;
    border: 1px solid var(--border);
    border-radius: 8px 8px 0 0;
    border-bottom: 0;
    background: var(--bg);
}
.tour-fmt {
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    border-radius: 6px;
    padding: 3px 8px;
    font-size: 13px;
    cursor: pointer;
    min-width: 28px;
}
.tour-fmt:hover { border-color: var(--border-strong); }
.tour-fmt.is-bold { font-weight: 800; }
.tour-fmt.is-big { font-size: 15px; }
.tour-fmt.is-small { font-size: 11px; }

/* Цветовые образцы: кружок цвета, которым красят выделение. */
.tour-sw {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    cursor: pointer;
    border: 1px solid rgba(0, 0, 0, 0.2);
    display: inline-block;
}

.tour-ed-body {
    min-height: 90px;
    padding: 10px 12px;
    border: 1px solid var(--border-strong);
    border-radius: 0 0 8px 8px;
    background: var(--input-bg);
    color: var(--text);
    font-size: 14px;
    line-height: 1.55;
    overflow-y: auto;
    max-height: 260px;
}
.tour-ed-body:focus { outline: 2px solid var(--accent-light); outline-offset: -1px; }
.tour-ed-body p { margin: 0 0 8px; }
.tour-ed-body p:last-child { margin-bottom: 0; }
.tour-ed-body ul { margin: 4px 0 8px; padding-left: 20px; }
.tour-ed-body li { margin-bottom: 4px; }

.tour-ed-foot { display: flex; align-items: center; gap: 10px; }
.tour-ed-saved { font-size: 13px; color: var(--accent); }

/* --- обучение: несколько окон разом ------------------------------------------
   Шаг неочевидного действия показывает все его последствия сразу: своя рамка и
   своя выноска на каждый изменившийся кусок, панель управления снизу. */
/* Куски тени вокруг дырок — без плавности (двигают на каждый скролл) и чуть
   поднятый над чистым чёрным: полупрозрачный чистый чёрный некоторые движки не
   растрят, а почти-чёрный кроют. На глаз — та же тень. */
.tour-hole-shade { transition: none; background: rgba(28, 30, 38, 0.72); }
.tour-rings { position: fixed; inset: 0; pointer-events: none; }
.tour-ring2 {
    position: fixed;
    border: 2px solid var(--accent);
    border-radius: 12px;
    box-shadow: 0 0 0 3px var(--accent-light);
    pointer-events: none;
    transition: all .15s ease;
}

.tour-callouts { position: fixed; inset: 0; pointer-events: none; }
.tour-callout {
    position: fixed;
    width: min(340px, calc(100vw - 20px));
    padding: 14px 16px 14px 44px;
    background: var(--card);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow-lg);
    pointer-events: auto;
}
/* Номер выноски совпадает с порядком, в котором меняются вещи. */
.tour-callout-dot {
    position: absolute;
    left: 14px;
    top: 14px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    font-size: 12.5px;
    font-weight: 700;
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Текст в выносках — такой же крупный, как в главной подсказке, и настраиваемый
   (ключи те же, что редактируются во вкладке «Тексты тура»). */
.tour-callout h4 { margin: 0 0 7px; font-size: 16px; }
.tour-callout-body p { margin: 0 0 7px; font-size: 16px; line-height: 1.55; color: var(--text); }
.tour-callout-body p:last-child { margin-bottom: 0; }
.tour-callout-body b { font-weight: 650; }
.tour-callout-body ul { margin: 2px 0 7px; padding-left: 2px; list-style: none; }
.tour-callout-body li { position: relative; padding-left: 20px; margin-bottom: 5px;
    font-size: 16px; line-height: 1.5; }
.tour-callout-body li::before { content: ''; position: absolute; left: 5px; top: 9px;
    width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }

/* Панель управления многооконного шага — узкая, снизу по центру. */
.tour-tip.tour-tip-bar { width: min(460px, calc(100vw - 24px)); }
.tour-tip-bar .tour-tip-body p { font-size: 15px; margin: 0; }

/* --- Голос: тестовая вкладка синтеза речи ---------------------------------- */
/* Замер здесь важнее звука, поэтому цифры стоят крупной строкой над плеером, а
   не подписями к нему. */
.tts-result { padding-top: 6px; }
.tts-metrics { display: flex; flex-wrap: wrap; gap: 22px; padding: 12px 0 4px; }
.tts-metric { display: flex; flex-direction: column; gap: 2px; }
.tts-metric b { font-size: 18px; font-weight: 650; color: var(--text); }
.tts-metric span { font-size: 12px; color: var(--text2); }
.tts-player { width: 100%; max-width: 460px; margin-top: 12px; }
/* Текст пробы в строке истории: одна строка с многоточием, целиком — в подсказке
   браузера. Иначе абзац растягивает таблицу и прячет колонки с замерами. */
.tts-text-cell { max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tts-row-actions { display: flex; gap: 6px; }
.tts-cmd {
    margin: 10px 0 0;
    padding: 10px 12px;
    border-radius: 8px;
    background: var(--hover-bg);
    color: var(--text);
    font-size: 13px;
    overflow-x: auto;
}

/* Поля выбора файла у своих голосов. Нативная кнопка браузера не знает ни темы,
   ни шрифта панели, поэтому перекрашивается под обычную вторичную кнопку. */
.kind-form input[type="file"] {
    font-size: 13px;
    color: var(--text2);
    padding: 7px 10px;
}
.kind-form input[type="file"]::file-selector-button {
    margin-right: 10px;
    padding: 5px 12px;
    border: none;
    border-radius: 8px;
    background: var(--hover-bg);
    color: var(--text);
    font: inherit;
    font-size: 12px;
    cursor: pointer;
}
.kind-form input[type="file"]::file-selector-button:hover { background: rgba(0, 0, 0, 0.08); }

/* Черты сценария: что он включает, а что снимает. Список, а не сетка полей —
   это не настройки, а описание выбранного, и правится оно выбором выше. */
.scenario-traits {
    list-style: none;
    margin: 8px 0 0;
    padding: 0;
    display: grid;
    gap: 3px;
}

.scenario-traits li {
    position: relative;
    padding-left: 22px;
    font-size: 13px;
    line-height: 1.5;
}

.scenario-traits li::before {
    position: absolute;
    left: 0;
    top: 0;
}

.scenario-traits .trait-on { color: var(--text); }
.scenario-traits .trait-on::before { content: '✓'; color: var(--accent); }

/* Снятое — тем же списком и приглушённо: «чего здесь нет» отвечает на вопрос
   не хуже, чем «что есть», а вторым списком читалось бы как ещё один набор. */
.scenario-traits .trait-off { color: var(--text2); text-decoration: line-through; }
.scenario-traits .trait-off::before { content: '—'; text-decoration: none; }

/* Выбор сценария: плитки рядом, в каждой — свой список черт. Чипсом было
   видно только выбранное имя, а «чем они отличаются» открывалось лишь после
   переключения, то есть после того, как выбор уже сделан. */
.scen-pick {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    align-items: start;
    gap: 12px;
}
.scen-tile {
    display: block;
    width: 100%;
    text-align: left;
    padding: 12px 14px;
    border: 1.5px solid var(--border);
    border-radius: 12px;
    background: var(--card);
    color: inherit;
    font: inherit;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease;
}
.scen-tile:hover { border-color: var(--accent); }
.scen-tile.is-on { border-color: var(--accent); background: var(--hover-bg); }
.scen-tile-head { display: flex; align-items: center; gap: 8px; }
.scen-tile-name { font-size: 14.5px; font-weight: 600; }
/* Кружок выбора — тот же язык, что у радиокнопки: плитка без него читается
   как кнопка «сделать», а не как «выбрано сейчас». */
.scen-mark {
    width: 16px;
    height: 16px;
    flex-shrink: 0;
    border: 1.5px solid var(--border-strong);
    border-radius: 50%;
}
.scen-tile.is-on .scen-mark {
    border-color: var(--accent);
    background: radial-gradient(var(--accent) 0 45%, transparent 46%);
}
/* «Сейчас в работе» — про сохранённый сценарий, а не про выбранный в форме:
   между нажатием плитки и кнопкой «Сохранить» это разные вещи. */
.scen-now {
    margin-left: auto;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.4px;
    text-transform: uppercase;
    color: var(--text2);
}
.scen-note { margin: 12px 0 0; }

/* Недоставленный текст в строке ленты: моноширинно и приглушённо — это улика,
   а не реплика разговора. */
.feed-blocked {
    display: inline-block;
    margin-left: 8px;
    padding: 1px 7px;
    border-radius: 6px;
    background: var(--hover-bg);
    font-family: var(--font-mono);
    font-size: 12px;
    color: var(--text2);
}

/* Предупреждение о правах под чертами сценария: не ошибка, но и не мелкий
   текст — его должны прочитать до сохранения. */
.scen-warn { margin-top: 10px; }

/* Одна кнопка на все карточки настроек заявок: она вне их, потому что
   принадлежит всем сразу. */
.res-save {
    display: flex;
    align-items: center;
    gap: 14px;
    padding: 4px 2px 2px;
}
.res-save .form-hint { margin: 0; }

/* Удержанное рядом с начислением: своим цветом и мельче — это не вторая сумма
   к выплате, а объяснение, почему первая меньше ожидаемой. */
.pay-penalty {
    margin-left: 6px;
    font-size: 12px;
    color: var(--danger);
    cursor: help;
}

/* Сводка попыток: текст сообщения моноширинно и с переносом — его читают
   целиком, а не пробегают глазами. */
.viol-body {
    display: inline-block;
    max-width: 460px;
    white-space: pre-wrap;
    word-break: break-word;
    font-family: var(--font-mono);
    font-size: 12px;
}
.viol-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.chk-inline { display: inline-flex; align-items: center; gap: 8px; margin: 8px 0; }

/* --- настройки фильтра переписки ------------------------------------------ */

/* Примерка стоит вплотную к словарям: правят их и проверяют одним движением. */
.filter-try {
    display: grid;
    gap: 8px;
    margin-top: 0;
    padding: 12px 14px;
    border: 1px dashed var(--border-strong);
    border-radius: 12px;
    background: var(--hover-bg);
}
.filter-try .field { margin: 0; }
.filter-try input { width: 100%; }

/* Вердикт — словом и цветом: «пройдёт» и «не пройдёт» читаются раньше текста. */
.filter-verdict { font-size: 13px; font-weight: 600; }
.filter-verdict.is-blocked { color: var(--danger); }
.filter-verdict.is-passed { color: var(--ok, var(--accent)); }

.filter-stats { margin-top: 10px; font-size: 12.5px; }
.filter-note { max-width: 620px; margin-top: 8px; }

/* --- словари фильтра как списки ------------------------------------------- */

/* Словарь и примерка рядом: правят список и тут же проверяют фразу, не теряя
   его из виду. Стопкой примерка уезжала под полотно чипов, а справа от него
   пустовала треть карточки. */
.dict-panel {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    align-items: start;
    gap: 14px;
}
@media (max-width: 1000px) {
    .dict-panel { grid-template-columns: 1fr; }
}
/* Правила и порог цифр — в одну строку внутри группы: тумблеры упираются в свой
   потолок ширины, и справа от них оставалась пустая треть. */
.rules-group { display: grid; grid-template-columns: minmax(0, auto) minmax(140px, 200px);
    align-items: start; gap: 10px 16px; }
.rules-group > .set-group-title { grid-column: 1 / -1; }
.rules-aside { align-self: start; }

.dict-tabs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.dict-tabs .chip { cursor: pointer; }
.dict-tabs .chip-on { border-color: var(--accent); color: var(--accent); font-weight: 600; }
/* Счётчик на вкладке: сколько строк в закрытом словаре, не открывая его. */
.dict-tab-n { margin-left: 6px; opacity: 0.7; font-variant-numeric: tabular-nums; }

.dict-field { max-width: none; }

.dict {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--card);
    overflow: hidden;
}

/* Строка управления: добавление слева, поиск рядом, счётчик у правого края —
   порядок повторяет порядок работы со списком. */
.dict-bar {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--hover-bg);
}
.dict-bar input {
    padding: 7px 11px;
    border: 1.5px solid var(--border-strong);
    border-radius: 9px;
    background: var(--input-bg);
    color: var(--text);
    font-size: 13px;
}
.dict-pair { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 240px; }
.dict-add { flex: 1 1 auto; min-width: 140px; }
.dict-search { flex: 0 1 180px; min-width: 120px; }
.dict-count { font-size: 12px; color: var(--text2); }

/* Сам список: чипы в несколько рядов, с прокруткой — длинный словарь не должен
   растягивать карточку настроек на два экрана. */
.dict-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    padding: 10px;
    max-height: 320px;
    overflow-y: auto;
}

.dict-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px 4px 3px 10px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--bg);
    font-family: var(--font-mono);
    font-size: 12px;
    line-height: 1.6;
}

/* Корень отличается от целого слова одним знаком, и знак этот меняет смысл
   правила — поэтому он цветной, а не спрятан в конце строки. */
.dict-chip.is-stem { border-color: var(--accent); }
.dict-star { color: var(--accent); font-weight: 700; }

.dict-x {
    border: none;
    background: none;
    color: var(--text2);
    font-size: 15px;
    line-height: 1;
    padding: 0 5px;
    cursor: pointer;
    border-radius: 999px;
}
.dict-x:hover { color: var(--danger); background: var(--hover-bg); }

