/* === 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); }
/* 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 {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    text-align: left;
}
.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;
    border-radius: 9px;
    cursor: pointer;
    transition: background 0.12s ease;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.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. */
.stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 14px;
}
.stat {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 16px 18px;
}
.stat-label { font-size: 12px; color: var(--text2); margin-bottom: 6px; }
.stat-value { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; }

/* 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; }
    .stat-grid { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); }
}

/* 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; }

/* === 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; }
.stat-label { font-size: 13px; }
.stat-value { font-size: 28px; }

/* =============================================================================
   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);
    z-index: 4000;
    animation: modalFade 0.16s ease;
}
[data-theme="dark"] .modal-scrim { background: rgba(0, 0, 0, 0.6); }

.modal {
    position: fixed;
    z-index: 4001;
    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; }

/* --- 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; }
.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.
   ============================================================================= */

.ms-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; margin: 6px 0 18px; }
.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; }
.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: center;
    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; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.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-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));
}

.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::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; }

/* Шапка над таблицей: чей это канал и сколько ждут. */
.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); }

/* Сводка по группам: плитка — это и цифра, и фильтр. */
.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: 190px; }
.col-telegram .stacked-cell input { min-width: 0; width: 160px; }

/* Полоса вкладок: разделы слева, область видимости справа. */
.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-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: 12px 0 14px; }
.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: 13.5px;
    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: 11.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: 8px 12px; }
.tx-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tx-field-wide { grid-column: 1 / -1; }
.tx-field label { font-size: 11px; font-weight: 600; color: var(--text2); }
/* Поле ровно под текст: высоту textarea считает JS по содержимому, поэтому
   здесь только то, от чего этот расчёт отталкивается — одна строка и никаких
   собственных отступов сверх padding. */
.tx-field input, .tx-field textarea {
    width: 100%;
    padding: 6px 9px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--input-bg);
    color: var(--text);
    font: inherit;
    font-size: 13px;
    line-height: 1.45;
}
.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; }
}
