/* Telegram CRM — dark, quiet interface. */

:root {
  --bg: #0e0e10;
  --sidebar: #151517;
  --surface: #17171a;
  --surface-hover: #1e1e22;
  --border: #26262b;
  --border-soft: #1f1f23;

  --text: #ececef;
  --text-muted: #9a9aa4;
  --text-faint: #6b6b75;

  --accent: #ffffff;
  --green: #3fb950;
  --yellow: #d29922;
  --red: #f85149;
  --blue: #58a6ff;

  --radius: 0.75rem;
  --radius-sm: 0.5rem;
  --sidebar-w: 15.5rem;

  --font: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono: "SF Mono", "JetBrains Mono", Consolas, "Courier New", monospace;
}

* { box-sizing: border-box; }

/* Every size below is in rem, so the scale of the whole interface is set here:
   120% of the browser's usual 16px. */
html {
  font-size: 120%;
  margin: 0;
  padding: 0;
  background: var(--bg);
}

body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.875rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}

a { color: inherit; text-decoration: none; }

::-webkit-scrollbar { width: 0.625rem; height: 0.625rem; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: #2c2c33; border-radius: 0.375rem; border: 0.188rem solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: #3a3a42; }

/* ----------------------------------------------------------------- layout */

.layout { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--sidebar);
  border-right: 0.062rem solid var(--border-soft);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 1.125rem 1.125rem 0.875rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}

.brand-mark {
  width: 1.625rem;
  height: 1.625rem;
  border-radius: 0.438rem;
  background: linear-gradient(140deg, #4f8cff, #8b5cf6);
  display: grid;
  place-items: center;
  font-size: 0.812rem;
  font-weight: 700;
  color: #fff;
}

.nav { padding: 0.375rem 0.625rem; overflow-y: auto; flex: 1; }

/* Only the open section keeps the air below it; the closed ones stack into a
   list of headings. */
.nav-group { margin-bottom: 0.125rem; transition: margin-bottom 0.24s ease; }
.nav-group.open { margin-bottom: 0.875rem; }

.nav-title {
  display: flex;
  align-items: center;
  width: 100%;
  padding: 0.375rem 0.5rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  font-family: inherit;
  font-size: 0.688rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-faint);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.nav-title:hover { background: var(--surface-hover); color: var(--text-muted); }
.nav-group.open .nav-title { color: var(--text-muted); }

.nav-caret {
  margin-left: auto;
  font-size: 0.562rem;
  transition: transform 0.24s ease;
}
.nav-group.open .nav-caret { transform: rotate(90deg); }

/* The section unrolls instead of blinking: a grid row grown from 0fr to 1fr
   measures the items itself, so no height has to be guessed or scripted. */
.nav-items {
  display: grid;
  grid-template-rows: 0fr;
  opacity: 0;
  /* Held back until the roll-up ends, so a closed section keeps out of tabbing. */
  visibility: hidden;
  transition: grid-template-rows 0.24s ease, opacity 0.18s ease, visibility 0s linear 0.24s;
}
.nav-group.open .nav-items {
  grid-template-rows: 1fr;
  opacity: 1;
  visibility: visible;
  transition: grid-template-rows 0.24s ease, opacity 0.18s ease, visibility 0s;
}

.nav-list { display: flex; flex-direction: column; overflow: hidden; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.438rem 0.5rem;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  font-size: 0.844rem;
  transition: background 0.12s, color 0.12s;
}

.nav-item:hover { background: var(--surface-hover); color: var(--text); }
.nav-item.active { background: var(--surface-hover); color: var(--text); font-weight: 500; }
.nav-item.disabled { color: var(--text-faint); cursor: default; }
.nav-item.disabled:hover { background: transparent; color: var(--text-faint); }

.nav-icon { width: 1rem; text-align: center; opacity: 0.85; flex: 0 0 1rem; }

.nav-soon {
  margin-left: auto;
  font-size: 0.625rem;
  color: var(--text-faint);
  border: 0.062rem solid var(--border);
  border-radius: 0.312rem;
  padding: 0 0.312rem;
}

.sidebar-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.625rem;
  padding: 0.75rem 1.125rem;
  border-top: 0.062rem solid var(--border-soft);
  font-size: 0.75rem;
  color: var(--text-faint);
}

.sidebar-exit { color: var(--text-muted); text-decoration: none; }
.sidebar-exit:hover { color: var(--text); }

.main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.topbar {
  height: 3.5rem;
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0 1.75rem;
  border-bottom: 0.062rem solid var(--border-soft);
  position: sticky;
  top: 0;
  background: rgba(14, 14, 16, 0.85);
  backdrop-filter: blur(0.5rem);
  z-index: 10;
}

.topbar h1 { font-size: 0.938rem; font-weight: 600; margin: 0; letter-spacing: -0.01em; }
.topbar .spacer { flex: 1; }

.content { padding: 1.75rem; max-width: 73.75rem; width: 100%; }

.crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  margin-bottom: 1rem;
  font-size: 0.812rem;
  color: var(--text-muted);
}
.crumbs a { color: var(--text-muted); }
.crumbs a:hover { color: var(--text); }
.crumbs-sep { color: var(--text-faint); }

.folder-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11.25rem, 1fr));
  gap: 0.5rem;
  margin-bottom: 1.125rem;
}

.folder-card {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.625rem 0.75rem;
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text-muted);
  min-width: 0;
}
.folder-card:hover { background: var(--surface-hover); color: var(--text); }
.folder-card-icon { opacity: 0.6; flex: 0 0 auto; }
.folder-card-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
  flex: 1;
}

/* ------------------------------------------------------------------ pieces */

.grid { display: grid; gap: 0.875rem; }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(11.875rem, 1fr)); }

.card {
  background: var(--surface);
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius);
  padding: 1.125rem;
}

.stat-label { font-size: 0.781rem; color: var(--text-muted); margin-bottom: 0.375rem; }
.stat-value { font-size: 1.625rem; font-weight: 600; letter-spacing: -0.02em; }
.stat-hint { font-size: 0.75rem; color: var(--text-faint); margin-top: 0.25rem; }

.section { margin-top: 1.75rem; }
.section-head {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.section-head h2 { font-size: 0.875rem; font-weight: 600; margin: 0; }
.section-head .spacer { flex: 1; }

.panel {
  background: var(--surface);
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius);
  overflow: hidden;
}

.panel-head {
  padding: 0.625rem 0.75rem;
  border-bottom: 0.062rem solid var(--border-soft);
  display: flex;
  align-items: center;
  gap: 0.75rem;
}
.panel-head h3 { margin: 0; font-size: 0.844rem; font-weight: 600; }
.panel-head .spacer { flex: 1; }
.panel-body { padding: 0.875rem; }

/* A bot without a token: the page still opens, but most of it will not work,
   and that has to be said before anything is typed into it. */
.token-warning {
  border-color: #6a5a1f;
  background: linear-gradient(0deg, rgba(212, 176, 60, 0.05), rgba(212, 176, 60, 0.05)),
    var(--surface);
  margin-bottom: 0.875rem;
}
.token-warning-title { font-weight: 600; color: #e6c65c; margin-bottom: 0.375rem; }
.token-warning p { margin: 0 0 0.75rem; line-height: 1.5; }

/* ------------------------------------------------------------------ tables */

table { width: 100%; border-collapse: collapse; }

/* Every table here is a register scanned across rows rather than read line by
   line, so they are all kept to one dense scale. */
th {
  text-align: left;
  font-size: 0.719rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-faint);
  font-weight: 600;
  padding: 0.438rem 0.75rem;
  border-bottom: 0.062rem solid var(--border-soft);
  white-space: nowrap;
}

td {
  padding: 0.312rem 0.75rem;
  font-size: 0.781rem;
  border-bottom: 0.062rem solid var(--border-soft);
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: rgba(255, 255, 255, 0.018); }

/* The controls at the end of a row: links and buttons mixed, so they are laid out
   rather than left to the whitespace between tags. */
.row-actions {
  text-align: right;
  white-space: nowrap;
}
.row-actions > * { vertical-align: middle; }
.row-actions > * + * { margin-left: 0.5rem; }

.num { font-variant-numeric: tabular-nums; color: var(--text-muted); }
.rank { width: 2.125rem; color: var(--text-faint); font-variant-numeric: tabular-nums; }

.mono { font-family: var(--mono); font-size: 0.781rem; }
.muted { color: var(--text-muted); }
.faint { color: var(--text-faint); }
.nowrap { white-space: nowrap; }

/* ------------------------------------------------------------------ badges */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.312rem;
  font-size: 0.719rem;
  padding: 0.125rem 0.5rem;
  border-radius: 1.25rem;
  border: 0.062rem solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}

.badge.green { color: var(--green); border-color: rgba(63, 185, 80, 0.3); background: rgba(63, 185, 80, 0.08); }
.badge.yellow { color: var(--yellow); border-color: rgba(210, 153, 34, 0.3); background: rgba(210, 153, 34, 0.08); }
.badge.red { color: var(--red); border-color: rgba(248, 81, 73, 0.3); background: rgba(248, 81, 73, 0.08); }
.badge.blue { color: var(--blue); border-color: rgba(88, 166, 255, 0.3); background: rgba(88, 166, 255, 0.08); }

.dot { width: 0.375rem; height: 0.375rem; border-radius: 50%; background: currentColor; }

.type-tag {
  font-size: 0.719rem;
  color: var(--text-muted);
  border: 0.062rem solid var(--border);
  border-radius: 0.312rem;
  padding: 0.062rem 0.438rem;
  white-space: nowrap;
}
/* A list of tags is written without spaces between the tags, so the gap is here.
   Each tag is a box of its own and a line may break between them, which would
   stack a chain of actions one per line and treble the height of its row. */
td .type-tag + .type-tag { margin-left: 0.375rem; }
td:has(.type-tag + .type-tag) { white-space: nowrap; }

/* ----------------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.438rem;
  padding: 0.438rem 0.875rem;
  border-radius: var(--radius-sm);
  border: 0.062rem solid var(--border);
  background: transparent;
  color: var(--text);
  font-family: inherit;
  font-size: 0.812rem;
  font-weight: 500;
  /* Stated rather than inherited: a link would take the page's line height and end
     up taller than a button next to it. */
  line-height: 1.25;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, opacity 0.12s;
  white-space: nowrap;
}

.btn:hover { background: var(--surface-hover); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }

.btn-primary {
  background: var(--accent);
  color: #0e0e10;
  border-color: var(--accent);
  font-weight: 600;
}
.btn-primary:hover { background: #e2e2e6; }

.btn-sm { padding: 0.25rem 0.625rem; font-size: 0.75rem; }
/* A button that is nothing but its mark: the box is stated rather than left to
   the content, so it is square and as tall as the labelled buttons beside it. */
.btn-square { width: 2rem; height: 2rem; padding: 0; }
.btn-icon { padding: 0.312rem 0.5rem; color: var(--text-faint); }
.btn-icon:hover { color: var(--text); }
.btn-danger:hover { color: var(--red); border-color: rgba(248, 81, 73, 0.4); }

/* ------------------------------------------------------------------ inputs */

label.field { display: block; margin-bottom: 0.875rem; }
.field-label { display: block; font-size: 0.781rem; color: var(--text-muted); margin-bottom: 0.375rem; }

input[type="text"], input[type="number"], input[type="password"], textarea, select {
  width: 100%;
  background: var(--bg);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-family: inherit;
  font-size: 0.844rem;
  padding: 0.562rem 0.75rem;
  outline: none;
  transition: border-color 0.12s;
}

input:focus, textarea:focus, select:focus { border-color: #3d3d46; }
textarea { resize: vertical; min-height: 6rem; line-height: 1.6; }

/* A placeholder is a hint, not a value: everywhere it is written a shade quieter
   and a shade smaller than what will be typed over it. */
::placeholder { color: var(--text-faint); font-size: 0.95em; }

select { appearance: none; cursor: pointer; }

.row { display: flex; gap: 0.75rem; flex-wrap: wrap; }
.row > * { flex: 1; min-width: 10rem; }

.check { display: flex; align-items: center; gap: 0.5rem; font-size: 0.812rem; color: var(--text-muted); cursor: pointer; }
.check input { width: auto; accent-color: #6d7cff; }

/* ------------------------------------------------------------------ switch */

.switch { position: relative; display: inline-block; width: 2.125rem; height: 1.188rem; flex: 0 0 2.125rem; }
.switch input { opacity: 0; width: 0; height: 0; }
.switch span {
  position: absolute;
  inset: 0;
  background: #35353d;
  border-radius: 1.188rem;
  cursor: pointer;
  transition: background 0.16s;
}
.switch span::before {
  content: "";
  position: absolute;
  width: 0.938rem;
  height: 0.938rem;
  left: 0.125rem;
  top: 0.125rem;
  background: #fff;
  border-radius: 50%;
  transition: transform 0.16s;
}
.switch input:checked + span { background: #3fb950; }
.switch input:checked + span::before { transform: translateX(0.938rem); }

.col-switch { width: 2.75rem; vertical-align: middle; }
.strategies-table .col-switch { padding-right: 0.25rem; }
.strategies-table td.col-switch { line-height: 0; }

/* ------------------------------------------------------------------- misc */

.empty {
  padding: 2.75rem 1.125rem;
  text-align: center;
  color: var(--text-faint);
  font-size: 0.844rem;
}
.empty-title { color: var(--text-muted); font-size: 0.875rem; margin-bottom: 0.25rem; }

.progress {
  height: 0.25rem;
  background: var(--border-soft);
  border-radius: 0.25rem;
  overflow: hidden;
}
.progress-bar {
  height: 100%;
  background: linear-gradient(90deg, #4f8cff, #8b5cf6);
  border-radius: 0.25rem;
  transition: width 0.3s ease;
}

.log {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.75;
  max-height: 16.25rem;
  overflow-y: auto;
  background: var(--bg);
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius-sm);
  padding: 0.75rem 0.875rem;
}
.log-line { display: flex; gap: 0.5rem; }
.log-line .sign { flex: 0 0 0.75rem; }
.log-info .sign { color: var(--blue); }
.log-ok .sign { color: var(--green); }
.log-warn { color: var(--yellow); }
.log-error { color: var(--red); }

.query-block { margin-bottom: 1.25rem; }
.query-head {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  padding: 0 0 0.5rem;
}
.query-name { font-size: 0.875rem; font-weight: 600; }

.toast {
  position: fixed;
  bottom: 1.375rem;
  right: 1.375rem;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.688rem 1rem;
  font-size: 0.812rem;
  box-shadow: 0 0.625rem 1.875rem rgba(0, 0, 0, 0.5);
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.2s, transform 0.2s;
  z-index: 100;
  max-width: 26.25rem;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.error { border-color: rgba(248, 81, 73, 0.45); color: #ffb3ae; }

.link { color: var(--blue); }
.link:hover { text-decoration: underline; }

/* A caption that happens to be clickable: no frame, so it does not read as one
   more control in a row of buttons. */
.text-btn {
  border: 0;
  background: transparent;
  padding: 0;
  color: var(--text-faint);
  font-family: inherit;
  font-size: 0.781rem;
  cursor: pointer;
}
.text-btn:hover { color: var(--text); text-decoration: underline; }

.title-cell { max-width: 23.75rem; }
.hint { font-size: 0.781rem; color: var(--text-faint); margin-top: 0.5rem; }

/* ------------------------------------------------------------------ avatars */

.avatar {
  width: 2.125rem;
  height: 2.125rem;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  background: var(--surface-hover);
}

.avatar-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-muted);
  border: 0.062rem solid var(--border);
}

.avatar-lg { width: 3.5rem; height: 3.5rem; font-size: 1.25rem; }

.filter-bar {
  display: flex;
  gap: 0.625rem;
  border-bottom: 0.062rem solid var(--border-soft);
}
.filter-bar select { flex: 0 0 auto; }
.filter-bar input { flex: 1; }

/* ------------------------------------------------------------------ bots */

.toolbar {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  flex-wrap: wrap;
}
.toolbar .divider {
  width: 0.062rem;
  height: 1.375rem;
  background: var(--border);
  margin: 0 0.25rem;
}

/* The three lights in the corner of the bots table. Each is a tally and a filter
   at once: dim while the list shows everything, lit while it shows only its own
   state. */
.state-lights { display: inline-flex; gap: 0.375rem; }

.state-lights .light {
  display: inline-flex;
  align-items: center;
  gap: 0.312rem;
  padding: 0.125rem 0.5rem;
  border-radius: 1.25rem;
  border: 0.062rem solid var(--border);
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.719rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.state-lights .light i { width: 0.375rem; height: 0.375rem; border-radius: 50%; background: currentColor; }
.state-lights .light:hover { border-color: currentColor; }

.state-lights .light.green { color: var(--green); }
.state-lights .light.yellow { color: var(--yellow); }
.state-lights .light.red { color: var(--red); }

.state-lights .light.green.on { border-color: rgba(63, 185, 80, 0.45); background: rgba(63, 185, 80, 0.14); }
.state-lights .light.yellow.on { border-color: rgba(210, 153, 34, 0.45); background: rgba(210, 153, 34, 0.14); }
.state-lights .light.red.on { border-color: rgba(248, 81, 73, 0.45); background: rgba(248, 81, 73, 0.14); }

.bot-card { display: flex; align-items: center; gap: 1.125rem; flex-wrap: wrap; }
.bot-card-main { flex: 1; min-width: 13.75rem; }
.bot-card-title {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  font-size: 1rem;
  margin-bottom: 0.25rem;
}
.bot-card-facts {
  display: flex;
  gap: 1.75rem;
  font-size: 0.812rem;
  color: var(--text);
}

.plain { color: var(--text); }
.plain:hover { text-decoration: underline; }
.red-text { color: var(--red); }
.green-text { color: var(--green); }

td input[type="checkbox"], th input[type="checkbox"] {
  width: auto;
  accent-color: #6d7cff;
  cursor: pointer;
}

/* ------------------------------------------------------- keyword chips */

.chips { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.312rem; }
.chips:empty { display: none; }
.filters .chips { margin-top: 0.75rem; }
.chips-lg { gap: 0.375rem; margin-top: 0; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  max-width: 15rem;
  padding: 0.062rem 0.438rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: 0.312rem;
  font-size: 0.719rem;
  color: var(--text-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* A keyword reads by how well the resource stands on it: in the top three,
   somewhere below, or no longer found. */
.chip-ranked,
.chip-top {
  color: #7ee18a;
  border-color: rgba(63, 185, 80, 0.4);
  background: rgba(63, 185, 80, 0.09);
}
.chip-mid {
  color: #e3b341;
  border-color: rgba(210, 153, 34, 0.35);
  background: rgba(210, 153, 34, 0.08);
}

.chips-lg .chip { font-size: 0.781rem; padding: 0.188rem 0.562rem; max-width: none; }

.chip-remove {
  background: none;
  border: 0;
  padding: 0;
  color: inherit;
  opacity: 0.5;
  cursor: pointer;
  font-size: 0.688rem;
}
.chip-remove:hover { opacity: 1; color: var(--red); }

/* ------------------------------------------------- position table */

.switcher {
  display: inline-flex;
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.switcher-item {
  padding: 0.375rem 0.875rem;
  font-size: 0.812rem;
  color: var(--text-muted);
}
.switcher-item:hover { background: var(--surface-hover); color: var(--text); }
.switcher-item.active { background: var(--surface-hover); color: var(--text); }

.table-scroll { overflow-x: auto; }

.rank-table { min-width: 100%; }
.rank-table th.rank-head { min-width: 15rem; }
.rank-table td, .rank-table th { vertical-align: middle; }

.rank-group + .rank-group .group-row td { border-top: 0.062rem solid var(--border); }

.group-row td { background: #1c1c21; padding: 0.438rem 0.75rem; }
.group-head { display: flex; align-items: center; gap: 0.625rem; }
.group-title { font-size: 0.844rem; color: var(--text); font-weight: 500; }

.avatar-sm { width: 1.375rem; height: 1.375rem; font-size: 0.688rem; }

.rank-name { max-width: 20rem; }
.rank-name .faint { margin-left: 0.375rem; font-size: 0.75rem; }

.pos-head, td.pos { border-left: 0.062rem solid var(--border-soft); }
.pos-head { text-align: center; width: 3.625rem; padding: 0.375rem 0.25rem; }
.run-day { display: block; font-size: 0.719rem; color: var(--text-muted); }
.run-time { display: block; font-size: 0.656rem; color: var(--text-faint); }

td.pos {
  text-align: center;
  font-family: var(--mono);
  font-size: 0.781rem;
  color: var(--text-muted);
}
td.pos-top { color: #7ee18a; }
td.pos-out { color: var(--text-faint); }
td.pos-skip::after { content: "·"; color: #2a2a30; }
/* The sweep that followed a ban: no place, and none coming. */
td.pos.pos-ban { color: #f0837f; }

.pos-badge {
  display: inline-block;
  min-width: 1.5rem;
  padding: 0.062rem 0.375rem;
  border-radius: 0.312rem;
  font-family: var(--mono);
  font-size: 0.781rem;
  text-align: center;
}
.pos-badge.pos-top { background: rgba(63, 185, 80, 0.16); color: #7ee18a; }
.pos-badge.pos-in { background: rgba(210, 153, 34, 0.14); color: #e3b341; }

.pos-legend {
  display: inline-block;
  padding: 0 0.312rem;
  margin-left: 0.25rem;
  border-radius: 0.25rem;
  font-size: 0.688rem;
}
.pos-legend.pos-top { background: rgba(63, 185, 80, 0.16); color: #7ee18a; }
.pos-legend.pos-in { background: rgba(210, 153, 34, 0.14); color: #e3b341; }
.pos-legend.pos-out { color: var(--text-faint); }

td.trend { font-size: 0.75rem; white-space: nowrap; }
td.trend .up { color: var(--green); }
td.trend .down { color: var(--red); }

/* ------------------------------------------------------- message composer */

.tg-editor {
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  overflow: hidden;
}

.tg-toolbar {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  padding: 0.312rem 0.375rem;
  border-bottom: 0.062rem solid var(--border-soft);
  background: var(--surface);
  flex-wrap: wrap;
}

.tg-tool {
  min-width: 1.625rem;
  height: 1.5rem;
  padding: 0 0.375rem;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.781rem;
  cursor: pointer;
}
.tg-tool:hover { background: var(--surface-hover); color: var(--text); }

.tg-tool-sep { width: 0.062rem; height: 1rem; background: var(--border); margin: 0 0.25rem; }

.tg-tool-select {
  width: auto;
  max-width: 10.625rem;
  padding: 0.188rem 0.5rem;
  font-size: 0.75rem;
  background: transparent;
  border-color: var(--border-soft);
  color: var(--text-muted);
}

.tg-input {
  min-height: 6rem;
  padding: 0.688rem 0.812rem;
  font-size: 0.844rem;
  line-height: 1.6;
  outline: none;
  white-space: pre-wrap;
  word-break: break-word;
}
.tg-input:empty::before {
  content: attr(data-placeholder);
  color: var(--text-faint);
  font-size: 0.95em;
}

/* Telegram tags rendered inside the editor. */
.tg-input tg-spoiler {
  background: #2c2c33;
  border-radius: 0.25rem;
  color: var(--text-muted);
  padding: 0 0.125rem;
}
.tg-input link {
  color: var(--blue);
  border-bottom: 0.062rem dashed rgba(88, 166, 255, 0.5);
}
.tg-input a { color: var(--blue); }
.tg-input code { font-family: var(--mono); font-size: 0.781rem; background: #22222a; padding: 0 0.25rem; border-radius: 0.25rem; }

.composer { display: flex; flex-direction: column; gap: 0.625rem; }

.composer-media { display: flex; align-items: center; gap: 0.625rem; flex-wrap: wrap; }
.composer-buttons { display: flex; flex-direction: column; gap: 0.5rem; align-items: flex-start; }
.composer-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; }

.tg-input tg-link {
  color: var(--accent, #58a6ff);
  text-decoration: underline;
  cursor: text;
}

.media-strip { display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap; min-height: 1.625rem; }

.media-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem;
  border: 0.062rem solid var(--border);
  border-radius: 0.438rem;
}
.media-chip img { width: 2.125rem; height: 2.125rem; object-fit: cover; border-radius: 0.312rem; display: block; }
.media-chip .media-video {
  width: 2.125rem;
  height: 2.125rem;
  display: grid;
  place-items: center;
  background: #22222a;
  border-radius: 0.312rem;
  font-size: 0.75rem;
  color: var(--text-muted);
}
.media-chip .chip-remove { padding: 0 0.25rem; }

.btn-rows { display: flex; flex-direction: column; gap: 0.375rem; width: 100%; }

.btn-row { display: flex; gap: 0.375rem; align-items: center; }
.btn-row input, .btn-row select { flex: 1; min-width: 0; padding: 0.375rem 0.625rem; font-size: 0.781rem; }
.btn-row .btn-text { flex: 0 0 10.625rem; }
.btn-row .btn-target { flex: 0 0 13.125rem; }

/* «param=1» says nothing about where it leads, so the row spells the address out. */
.btn-row .btn-url-note {
  flex: 0 1 auto;
  max-width: 12.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.719rem;
}
.btn-row .btn-url-note:empty { display: none; }
.btn-row .btn-url-note.bad { color: var(--red); }

/* ------------------------------------------------------------ media gallery */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(8, 8, 10, 0.72);
  display: grid;
  place-items: center;
  z-index: 100;
  padding: 1.5rem;
}

.modal {
  width: min(47.5rem, 100%);
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.modal-head, .modal-foot {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.75rem 1rem;
}
.modal-head { border-bottom: 0.062rem solid var(--border-soft); }
.modal-foot { border-top: 0.062rem solid var(--border-soft); }
.modal-head h3 { margin: 0; font-size: 0.844rem; font-weight: 600; }
.modal-head .spacer, .modal-foot .spacer { flex: 1; }
.modal-body { padding: 1rem; overflow-y: auto; }

.media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  gap: 0.5rem;
}

.media-cell {
  position: relative;
  padding: 0;
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  cursor: pointer;
  overflow: hidden;
  text-align: left;
}
.media-cell:hover { border-color: #3d3d46; }
.media-cell.selected { border-color: var(--blue); box-shadow: inset 0 0 0 0.062rem var(--blue); }
.media-cell img { width: 100%; height: 4.625rem; object-fit: cover; display: block; }
.media-cell .media-video {
  height: 4.625rem;
  display: grid;
  place-items: center;
  background: #22222a;
  color: var(--text-muted);
}
.media-cell .media-name {
  display: block;
  padding: 0.25rem 0.375rem;
  font-size: 0.688rem;
  color: var(--text-faint);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --------------------------------------------------------------- bot editor */

/* Every bot setting is its own card: a titled head, the fields, and a footer
   that holds the button saving exactly this card. The three edit screens — one
   bot, mass edit, templates — are built from the same card, so a block always
   ends and begins in the same visible way and the save is always in the footer
   rather than beside a random field. */
.edit-stack { display: flex; flex-direction: column; gap: 0.875rem; }
.edit-stack > .panel,
.edit-stack > .edit-card { margin: 0; }

.edit-card {
  background: var(--surface);
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius);
  overflow: hidden;
}

.edit-card-head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.688rem 1.125rem;
  border-bottom: 0.062rem solid var(--border-soft);
  background: rgba(255, 255, 255, 0.014);
}
.edit-card-head h3 { margin: 0; font-size: 0.812rem; font-weight: 600; }
.edit-card-head .spacer { flex: 1; }
.edit-card-head .faint { font-size: 0.75rem; }

.edit-card-body { padding: 1rem 1.125rem; }
/* A field is a field whether it wraps a control in a label or holds a row of
   buttons in a div: inside a card they are spaced the same way. */
.edit-card-body > .field { margin-bottom: 0.875rem; }
.edit-card-body > .field:last-child { margin-bottom: 0; }
.edit-card-body > .hint:first-child { margin-top: 0; }

.edit-card-foot {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  flex-wrap: wrap;
  padding: 0.688rem 1.125rem;
  border-top: 0.062rem solid var(--border-soft);
  background: rgba(0, 0, 0, 0.14);
}
.edit-card-foot .spacer { flex: 1; }
.edit-card-foot .faint { font-size: 0.75rem; }

.edit-head { display: flex; align-items: baseline; gap: 0.625rem; margin-bottom: 0.625rem; }
.edit-head h3 { margin: 0; font-size: 0.844rem; font-weight: 600; }
.edit-head .spacer { flex: 1; }
.edit-head .field-label { margin-bottom: 0; font-weight: 600; color: var(--text); }

/* The reply is switched on next to the welcome message, so it needs a line of
   its own to separate the two composers. */
.reply-block { margin-top: 1rem; padding-top: 1rem; border-top: 0.062rem solid var(--border-soft); }

.hint { font-size: 0.75rem; color: var(--text-faint); margin-top: 0.5rem; line-height: 1.5; }

.link-row { display: flex; gap: 0.5rem; align-items: center; margin-bottom: 0.375rem; }
.link-row .link-param {
  flex: 0 0 5.25rem;
  font-family: var(--mono);
  font-size: 0.719rem;
  color: var(--text-faint);
  cursor: pointer;
}
.link-row .link-param:hover { color: var(--text); }
.link-row input { flex: 1; min-width: 0; padding: 0.438rem 0.625rem; font-size: 0.781rem; }
.link-row .link-label { flex: 0 0 9.375rem; }

.lang-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.75rem, 1fr));
  gap: 0.125rem 0.625rem;
  max-height: 10.5rem;
  overflow-y: auto;
  padding: 0.5rem 0.625rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--bg);
}
.lang-grid .check { font-size: 0.781rem; }

.name-row {
  display: flex;
  gap: 0.5rem;
  align-items: flex-start;
  padding: 0.625rem 0;
  border-top: 0.062rem solid var(--border-soft);
}
.name-row:first-child { border-top: 0; }
.name-row .name-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.375rem; }
.name-row .name-langs { font-size: 0.75rem; color: var(--text-faint); }

.avatar-row { display: flex; align-items: center; gap: 0.875rem; flex-wrap: wrap; }
.avatar-pool { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }

/* ------------------------------------------------------------ scenario steps */

.step-list { display: flex; flex-direction: column; gap: 0.75rem; }

.step {
  border: 0.062rem solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}

.step-head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.625rem 0.875rem;
  border-bottom: 0.062rem solid var(--border-soft);
  background: #1c1c21;
}
.step-head .spacer { flex: 1; }
.step-num {
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.375rem;
  background: #2a2a32;
  display: grid;
  place-items: center;
  font-size: 0.688rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.step-kind { font-size: 0.812rem; font-weight: 500; }
.step-title-input {
  flex: 0 0 13.75rem;
  padding: 0.312rem 0.562rem;
  font-size: 0.781rem;
  background: var(--bg);
}
/* An action the task grew on its own: nothing in it to set, so it is a header
   alone, and dimmed enough to read as somebody else's. */
.step-fixed .step-head { border-bottom: none; }
.step-fixed .step-kind { color: var(--text-muted); }

.step-body { padding: 0.875rem; }
/* A field of its own under the row of short ones: the names of the bots need the
   width, so they sit below rather than beside. */
.step-body > .field { margin: 0.75rem 0 0; }
.step-body > .field:first-child { margin-top: 0; }
.step-bot-title {
  display: block;
  width: 100%;
  margin-top: 0.625rem;
  min-height: 4.5rem;
  resize: vertical;
  font: inherit;
}
.step-bot-title[hidden] { display: none !important; }
.step-schedule {
  display: flex;
  gap: 0.625rem;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 0.75rem;
}
.step-schedule .field { margin-bottom: 0; }
.step-schedule select, .step-schedule input { max-width: 12.5rem; }

.step-add { display: flex; gap: 0.5rem; margin-top: 0.875rem; flex-wrap: wrap; }

.empty-steps {
  padding: 1.625rem 1.125rem;
  text-align: center;
  color: var(--text-faint);
  border: 0.062rem dashed var(--border);
  border-radius: var(--radius);
}

/* ---------------------------------------------------------------- analytics */

/* One table holds the whole hierarchy: folders, keywords, search results. The
   indent comes from --depth on the first cell so every column stays aligned. */
.tree-table tbody tr { cursor: pointer; }
.tree-table tbody tr[data-leaf="1"] { cursor: default; }
.tree-table tbody tr:hover > td { background: var(--surface-hover); }

.tree-table .cell-line { padding-left: calc(var(--depth, 0) * 1.125rem); }

.tree-table .row-folder > td:first-child { font-weight: 500; }
.tree-table .row-folder > td { background: #1b1b20; }
.tree-table .row-folder:hover > td { background: #22222a; }
.tree-table .row-resource > td:first-child { font-weight: 500; }

.tree-table .row-note td {
  padding: 0.5rem 0.875rem;
  color: var(--text-faint);
  font-size: 0.781rem;
}

.row-caret {
  flex: 0 0 0.562rem;
  color: var(--text-faint);
  font-size: 0.625rem;
  transition: transform 0.12s ease;
}
tr.open > td .row-caret { transform: rotate(90deg); }
.row-caret.leaf { visibility: hidden; }

.row-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.tree-badge {
  flex: 0 0 auto;
  padding: 0 0.375rem;
  border-radius: 0.312rem;
  background: #23232a;
  color: var(--text-faint);
  font-size: 0.688rem;
  font-variant-numeric: tabular-nums;
}
.tree-badge.top { background: rgba(63, 185, 80, 0.16); color: #7ee18a; }
.tree-badge.mid { background: rgba(210, 153, 34, 0.14); color: #e3b341; }

/* A folder says what it is before it is read: a small filled shape with a tab,
   drawn rather than set in a glyph so it lines up with the name at any size. */
.folder-icon {
  position: relative;
  flex: 0 0 0.812rem;
  width: 0.812rem;
  height: 0.625rem;
  margin-top: 0.125rem;
  border-radius: 0 0.125rem 0.125rem 0.125rem;
  background: #55555f;
}
.folder-icon::before {
  content: "";
  position: absolute;
  top: -0.188rem;
  left: 0;
  width: 0.375rem;
  height: 0.188rem;
  border-radius: 0.125rem 0.125rem 0 0;
  background: #55555f;
}
tr.open > td .folder-icon,
tr:hover > td .folder-icon { background: #6d6d79; }
tr.open > td .folder-icon::before,
tr:hover > td .folder-icon::before { background: #6d6d79; }

/* Everything a folder can be filled with sits behind one handle at the end of
   the row, always in the same place instead of appearing under the cursor. */
.row-menu-btn {
  flex: 0 0 1.125rem;
  margin-left: auto;
  width: 1.125rem;
  height: 1.125rem;
  padding: 0;
  border: 0;
  border-radius: 0.25rem;
  background: transparent;
  cursor: pointer;
}
.row-menu-btn i,
.row-menu-btn i::before,
.row-menu-btn i::after {
  display: block;
  height: 0.062rem;
  background: var(--text-faint);
  border-radius: 0.062rem;
}
.row-menu-btn i {
  position: relative;
  width: 0.688rem;
  margin: 0 auto;
}
.row-menu-btn i::before,
.row-menu-btn i::after {
  content: "";
  position: absolute;
  left: 0;
  width: 0.688rem;
}
.row-menu-btn i::before { top: -0.25rem; }
.row-menu-btn i::after { top: 0.25rem; }
/* On a keyword row the handle is only wanted under the cursor: the table is read as
   a column of positions, and a button on every line would fight that. */
.tree-table tr.row-query .row-menu-btn { visibility: hidden; }
.tree-table tr.row-query:hover .row-menu-btn { visibility: visible; }

.row-menu-btn:hover { background: var(--surface-hover); }
.row-menu-btn:hover i,
.row-menu-btn:hover i::before,
.row-menu-btn:hover i::after { background: var(--text-muted); }

.row-menu {
  position: fixed;
  z-index: 60;
  min-width: 10.5rem;
  padding: 0.25rem;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 0.625rem 1.5rem rgba(0, 0, 0, 0.45);
}

/* What can be done with the ticked rows appears only once something is ticked. */
.selection-bar { display: flex; align-items: center; gap: 0.625rem; }

.dropdown { position: relative; }
.dropdown-menu {
  position: absolute;
  top: calc(100% + 0.25rem);
  right: 0;
  z-index: 20;
  min-width: 11.125rem;
  padding: 0.25rem;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 0.625rem 1.5rem rgba(0, 0, 0, 0.45);
}
.dropdown-item {
  display: block;
  width: 100%;
  padding: 0.438rem 0.562rem;
  border: 0;
  border-radius: 0.312rem;
  background: transparent;
  color: var(--text);
  font-size: 0.781rem;
  text-align: left;
  cursor: pointer;
}
.dropdown-item:hover { background: var(--surface-hover); }
.dropdown-item.danger { color: #f0837f; }

.sweep-pill {
  padding: 0.25rem 0.625rem;
  border: 0.062rem solid rgba(88, 166, 255, 0.35);
  border-radius: 62.438rem;
  background: rgba(88, 166, 255, 0.12);
  color: #8ec5ff;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

.modal-sm { width: min(28.75rem, 100%); }
.modal-sm textarea { min-height: 9.375rem; resize: vertical; }

.delete-list {
  margin: 0.625rem 0 0;
  padding: 0;
  max-height: 11.875rem;
  overflow-y: auto;
  list-style: none;
  font-size: 0.781rem;
  color: var(--text-muted);
}
.delete-list li { padding: 0.188rem 0; }
.delete-list li.folder { color: var(--text); }
.delete-list li.folder::before { content: "▸ "; color: var(--text-faint); }

/* Keywords of a resource at a glance: all of them, then those in the top three. */
.query-badge {
  flex: 0 0 auto;
  padding: 0 0.375rem;
  border-radius: 0.312rem;
  background: #23232a;
  color: var(--text);
  font-size: 0.688rem;
  font-style: normal;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.query-badge b { font-weight: 500; color: #7ee18a; }
.query-badge em { font-style: normal; color: #3a3a44; margin: 0 0.25rem; }

.cell-line { display: flex; align-items: center; gap: 0.438rem; flex-wrap: nowrap; }
/* Long Telegram titles would otherwise wrap to three lines and stretch the row. */
/* The table keeps its natural width and scrolls sideways; the name column stays
   pinned so a row can still be read after scrolling to an older sweep. */
.rank-table.analytics { width: max-content; min-width: 100%; }
.rank-table.analytics td.rank-name { min-width: 16.25rem; max-width: 21.25rem; }
.rank-table.analytics th.pick,
.rank-table.analytics td.pick {
  position: sticky;
  left: 0;
  z-index: 1;
  width: 2.125rem;
  padding: 0 0 0 0.75rem;
  background: var(--surface);
}

/* A column of ticks runs down the whole table, so the boxes are drawn by hand:
   the native control is a bright square on a dark page and would read as the
   loudest thing on it. The cell keeps its default middle alignment, which is
   what puts the box on the line of the name next to it. */
th.pick input[type="checkbox"],
td.pick input[type="checkbox"] {
  appearance: none;
  display: block;
  margin: 0;
  position: relative;
  width: 0.812rem;
  height: 0.812rem;
  border: 0.062rem solid #3c3c46;
  border-radius: 0.188rem;
  background: transparent;
  cursor: pointer;
  transition: border-color 0.1s ease, background 0.1s ease;
}
th.pick input[type="checkbox"]:hover,
td.pick input[type="checkbox"]:hover { border-color: #55555f; }
th.pick input[type="checkbox"]:checked,
th.pick input[type="checkbox"]:indeterminate,
td.pick input[type="checkbox"]:checked,
td.pick input[type="checkbox"]:indeterminate {
  background: #3f4661;
  border-color: #4d5578;
}

th.pick input[type="checkbox"]:checked::after,
td.pick input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  top: 0.062rem;
  left: 0.25rem;
  width: 0.188rem;
  height: 0.375rem;
  border: solid #c3c9e4;
  border-width: 0 0.094rem 0.094rem 0;
  transform: rotate(42deg);
}
/* Half of a folder ticked: a dash, not a tick that would claim all of it. */
th.pick input[type="checkbox"]:indeterminate::after,
td.pick input[type="checkbox"]:indeterminate::after {
  content: "";
  position: absolute;
  top: 0.312rem;
  left: 0.125rem;
  width: 0.438rem;
  height: 0.094rem;
  border-radius: 0.062rem;
  background: #c3c9e4;
}
.rank-table.analytics th.rank-head,
.rank-table.analytics td.rank-name {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  border-right: 0.062rem solid var(--border-soft);
}
/* Where rows can be ticked, the name column pins behind the tick column. */
.rank-table.analytics.has-pick th.rank-head,
.rank-table.analytics.has-pick td.rank-name { left: 2.125rem; }
.rank-table.analytics tr:hover > td.rank-name,
.rank-table.analytics tr:hover > td.pick { background: var(--surface-hover); }
.rank-table.analytics .row-folder > td.rank-name,
.rank-table.analytics .row-folder > td.pick { background: #1b1b20; }
.rank-table.analytics .row-folder:hover > td.rank-name,
.rank-table.analytics .row-folder:hover > td.pick { background: #22222a; }
/* The pinned cell needs an opaque colour of its own, matching the tinted row. */
.rank-table.analytics tr.ours-row > td.rank-name { background: #1a2029; }
.rank-table.analytics tr.ours-row:hover > td.rank-name { background: #1e2531; }
/* The folder mark keeps its tab, which is drawn outside its own box. */
.rank-table.analytics .cell-line > span:not(.type-tag):not(.folder-icon),
.rank-table.analytics .cell-line > a {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rank-table.analytics .cell-line > .avatar { flex: 0 0 auto; }
.rank-table.analytics .cell-line > .type-tag { flex: 0 0 auto; }
/* Wrapping the wordy headers instead of stretching them keeps the sweep columns
   in view without horizontal scrolling. */
.rank-table.analytics th.freq-head { width: 7.25rem; white-space: normal; line-height: 1.25; }
/* Demand and audience read as one column of magnitudes rather than a ledger, so
   they sit centred under their heading like the sweep columns next to them. */
.rank-table.analytics th.freq-head,
.rank-table.analytics td.freq { text-align: center; }
/* The number sits in the middle of the column and the arrow is taken out of the
   flow at the right edge, so it is in the same place in every row and the
   padding on both sides keeps the two from ever meeting. */
.rank-table.analytics td.freq {
  position: relative;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  padding-left: 2rem;
  padding-right: 2rem;
}
.rank-table.analytics td.freq .pos-move {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  margin: 0;
  font-size: 0.594rem;
}

/* A cell states its own move, so the table needs no separate trend column.
   Everything in a sweep column is written in lanes of a fixed width, so what a
   cell holds never depends on how long its numbers are: the divider stays put
   whether the row counts nine keywords or nine hundred, and three digits with a
   move of three more still stand apart. The lane widths add up to the column,
   and each number is set against the edge its neighbour reads from.

   The lanes are counted from the left edge rather than centred as a group:
   centred, a cell with nothing to report would be a lane narrower and pull the
   divider along with it, which is the drifting this is here to stop. */
.rank-table.analytics .pos-head,
.rank-table.analytics td.pos {
  width: 7.25rem;
  padding-left: 0.5rem;
  padding-right: 0.5rem;
}
.rank-table.analytics td.pos {
  text-align: left;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Cells holding one mark instead of a reading have no lanes to line up with. */
.rank-table.analytics td.pos.pos-out,
.rank-table.analytics td.pos.pos-ban,
.rank-table.analytics td.pos.pos-skip { text-align: center; }
.rank-table.analytics td.pos .pos-value {
  display: inline-block;
  width: 4.25rem;
  text-align: right;
}
/* The arrow keeps a lane of its own, and the gap is inside it: a number ending
   hard against the lane edge still has air before the arrow. */
.rank-table.analytics td.pos .pos-move,
.rank-table.analytics td.freq .pos-move {
  display: inline-block;
  box-sizing: border-box;
  width: 2rem;
  padding-left: 0.312rem;
  margin: 0;
  text-align: left;
  font-size: 0.594rem;
}
/* The place is coloured by how good it is; the arrow by which way it went. */
.rank-table.analytics td.pos.tier-top .pos-value { color: #7ee18a; }
.rank-table.analytics td.pos.tier-mid .pos-value { color: #e3b341; }
.pos-move { margin-left: 0.188rem; font-size: 0.625rem; }
.pos-move.new { color: #8ec5ff; }
.pos-move.up { color: #7ee18a; }
.pos-move.down { color: #f0837f; }

/* Counted rows: how many there are, and how many of them hold the top three.
   The one worth watching ends where a plain place ends, so a folder and the
   keywords under it read down the same edge. */
td.pos.tally { white-space: nowrap; font-size: 0.75rem; }
td.pos.tally .t-all,
td.pos.tally .t-sep,
td.pos.tally .t-top { display: inline-block; margin: 0; }
td.pos.tally .t-all { width: 2rem; text-align: right; color: var(--text); }
td.pos.tally .t-sep { width: 0.75rem; text-align: center; color: #3a3a44; }
td.pos.tally .t-top { width: 1.5rem; text-align: right; color: #7ee18a; }
/* Someone else's standing is stated, not judged: the colours belong to the
   resources we answer for. */
td.pos.tally.plain .t-top { color: var(--text); }
.query-badge.plain b { color: var(--text); }
/* Our own resources are tinted so they can be picked out of a long list. */
.rank-table tr.ours-row td { background: rgba(88, 166, 255, 0.09); }
.rank-table tr.ours-row td:first-child { box-shadow: inset 0.125rem 0 0 var(--blue); }
.rank-table tr.ours-row:hover td { background: rgba(88, 166, 255, 0.14); }
/* With a tick column in front the marker belongs on the name, not on the box. */
.rank-table tr.ours-row td.pick { box-shadow: none; }
.rank-table tr.ours-row td.pick + td.rank-name { box-shadow: inset 0.125rem 0 0 var(--blue); }

.tag-new {
  padding: 0 0.312rem;
  border-radius: 0.25rem;
  background: rgba(88, 166, 255, 0.16);
  color: #8ec5ff;
  font-size: 0.688rem;
}

/* The panel clips its corners; the suggestion list has to escape it. */
.filters { padding: 0.625rem 0.75rem; margin-bottom: 0.75rem; overflow: visible; }
.filter-row { display: flex; gap: 0.875rem; align-items: flex-end; flex-wrap: wrap; }

/* The fields shrink rather than dropping the reset button to a line of its own;
   below tablet width the row is allowed to break again. */
.filters .filter-row { flex-wrap: nowrap; }

/* The breakpoints moved with the scale: an interface 20% larger runs out of
   room in a window 20% wider. */
@media (max-width: 1080px) {
  .filters .filter-row { flex-wrap: wrap; }
}

.suggest { position: relative; display: block; }
.suggest-list {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.188rem);
  left: 0;
  right: 0;
  max-height: 16.25rem;
  overflow-y: auto;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 0.625rem 1.875rem rgba(0, 0, 0, 0.5);
}
.suggest-item {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  padding: 0.375rem 0.625rem;
  font-size: 0.781rem;
  cursor: pointer;
}
.suggest-item:hover, .suggest-item.active { background: var(--surface-hover); }
.suggest-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggest-item .faint { flex: 0 0 auto; font-size: 0.688rem; }
.filter-row .field { margin-bottom: 0; flex: 1 1 10.625rem; min-width: 0; }
.filter-row .field.narrow { flex: 0 0 8rem; }
/* Wide enough to read, never so wide that a row of two filters draws two fields
   the length of the page. The grid variant sets its own columns. */
.filter-row:not(.filter-grid) .field { max-width: 16.25rem; }
/* A row of buttons is as wide as its labels and refuses to shrink, so a field
   holding one would spill over the gap and touch the filter beside it. Those
   keep their own width; the room goes to the fields that can use it. */
.filter-row .field:has(> .segmented),
.filter-row .field:has(> .btn) { flex: 0 0 auto; max-width: none; }
/* Whatever room is left over lands before the reset, not between two filters. */
.filter-row:not(.filter-grid) > .btn:last-child { margin-left: auto; }
/* Captions only: the suggestion wrapper is a span too, and the spacing meant for
   a caption would lift its input above the fields beside it. */
.filter-row .field > span:not(.suggest) {
  display: block;
  font-size: 0.688rem;
  color: var(--text-faint);
  margin-bottom: 0.188rem;
}
.filter-row input, .filter-row select { width: 100%; padding: 0.25rem 0.5rem; font-size: 0.75rem; }
.filters .btn { padding: 0.312rem 0.75rem; font-size: 0.75rem; }

/* Everything in a filter row is drawn to the height of the inputs, so the row
   keeps one baseline whatever kind of control a filter needs. */
.filter-row .segmented button { padding: 0.25rem 0.625rem; font-size: 0.75rem; }

/* A filter that is not narrowing anything is written like a placeholder: the
   panel then shows at a glance what is actually set. Neutral options are the
   empty ones, plus the odd list whose «all» has to carry a value of its own. */
.filters select:has(option[value=""]:checked),
.filters select:has(option[data-any]:checked),
.filters .dr-trigger:not(.set) .dr-label {
  color: var(--text-faint);
  font-size: 0.719rem;
}

/* The name sits above its quieter details, so both fit one table row. */
.res-main { min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.res-title, .res-sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.res-sub { font-size: 0.688rem; color: var(--text-faint); font-weight: 400; }

/* Stays out of the way until the row is hovered, then offers the username. */
.copy-btn {
  flex: 0 0 auto;
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-faint);
  font-size: 0.75rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease;
}
tr:hover .copy-btn, .copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { background: var(--surface-hover); color: var(--text); }

.list-more { padding: 0.5rem 0.75rem; border-top: 0.062rem solid var(--border-soft); text-align: center; }

/* ------------------------------------------------------------- automation */

.auto-panel { margin-bottom: 1rem; }
.auto-panel .panel-head { align-items: flex-start; }
.head-text { min-width: 0; }
.head-note { font-size: 0.75rem; color: var(--text-faint); margin-top: 0.188rem; }

.state-pill {
  flex: 0 0 auto;
  padding: 0.188rem 0.625rem;
  border-radius: 62.438rem;
  border: 0.062rem solid var(--border);
  font-size: 0.719rem;
  color: var(--text-faint);
  white-space: nowrap;
}
.state-pill.on { color: var(--green); border-color: rgba(63, 185, 80, 0.35); }

/* A record reads as a row in a list, not as a card inside a card. */
.records { display: flex; flex-direction: column; }
.record {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 0.812rem 1.125rem;
  border-top: 0.062rem solid var(--border-soft);
  cursor: pointer;
}
.record:first-child { border-top: none; }
.record:hover { background: rgba(255, 255, 255, 0.018); }

.record-main { flex: 1; min-width: 0; }
.record-name { font-size: 0.844rem; font-weight: 500; }
.record-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.5rem;
  margin-top: 0.125rem;
  font-size: 0.75rem;
  color: var(--text-faint);
}
.record-meta span + span::before { content: "·"; margin-right: 0.5rem; }
.record-meta .accent { color: var(--text-muted); }

.record-next {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.062rem;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.record-next .faint { font-size: 0.688rem; }
.record-next b { font-weight: 500; }
/* A slot that produced nothing says so here, and has to be readable at a glance. */
.record-next .warn {
  font-size: 0.688rem;
  max-width: 16.25rem;
  text-align: right;
  color: var(--yellow);
}

.record-actions { flex: 0 0 auto; display: flex; gap: 0.125rem; }
.icon-btn {
  width: 1.75rem;
  height: 1.75rem;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 0.438rem;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.812rem;
  cursor: pointer;
}
.icon-btn:hover { background: var(--surface-hover); color: var(--text); }
.icon-btn.danger:hover { color: var(--red); }

.record-empty {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  padding: 1rem 1.125rem;
  font-size: 0.812rem;
  color: var(--text-faint);
}

/* The two alert bots, side by side. */
.alert-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(16.25rem, 1fr)); gap: 0.75rem; }
.alert-card {
  display: flex;
  flex-direction: column;
  padding: 0.875rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: #141418;
  min-width: 0;
}
.alert-card-head { display: flex; align-items: center; gap: 0.5rem; }
.alert-card-head .dot {
  width: 0.438rem;
  height: 0.438rem;
  border-radius: 50%;
  background: var(--green);
  flex: 0 0 0.438rem;
}
.alert-card.off .dot { background: var(--text-faint); }
.alert-card-title { font-size: 0.844rem; font-weight: 500; }
.alert-card-note { font-size: 0.75rem; color: var(--text-faint); margin-top: 0.188rem; }

.alert-card-id, .alert-card-token {
  margin-top: 0.625rem;
  font-size: 0.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.alert-card-id { display: flex; gap: 0.5rem; align-items: baseline; }
.alert-card-name { color: var(--text); font-size: 0.812rem; }
.alert-card-token { margin-top: 0.188rem; color: var(--text-faint); }
.alert-card-id.warn { color: var(--yellow); }

.alert-card-actions { display: flex; gap: 0.375rem; margin-top: auto; padding-top: 0.875rem; }
.alert-card-actions .btn { padding: 0.25rem 0.75rem; font-size: 0.781rem; }

.recipient-row { margin-top: 0.75rem; }
.recipient-row input { padding: 0.438rem 0.625rem; }

/* Picking keywords or bots: folders with their contents, one indent deep. */
.pick-head { display: flex; align-items: baseline; gap: 0.625rem; }
.pick-head .spacer { flex: 1; }
.pick-head .field-label { margin-bottom: 0; }
.pick-head .faint { font-size: 0.75rem; white-space: nowrap; }

.pick-search {
  width: 100%;
  margin: 0.5rem 0;
  padding: 0.312rem 0.562rem;
  font-size: 0.75rem;
}

.picker {
  max-height: 18.75rem;
  overflow-y: auto;
  border: 0.062rem solid var(--border-soft);
  border-radius: 0.5rem;
  padding: 0.375rem;
  background: #141418;
}
.pick-empty { padding: 0.625rem; text-align: center; }
/* Scoped to the picker: ``pick`` is too plain a word to own on its own, and a
   loose rule for it lands on the tick column of the tables as well. */
.picker .pick-row,
.picker .pick {
  display: flex;
  align-items: center;
  gap: 0.438rem;
  padding: 0.25rem 0.375rem 0.25rem calc(0.375rem + var(--depth, 0) * 1rem);
  border-radius: 0.312rem;
  font-size: 0.812rem;
}
.picker .pick { cursor: pointer; }
.picker .pick-row { padding-right: 0.375rem; }
.picker .pick-row:hover,
.picker .pick-item:hover { background: var(--surface-hover); }
.picker .pick-row .pick { flex: 1; min-width: 0; padding: 0; }
.picker .pick input { margin: 0; flex: 0 0 auto; }
.picker .pick-name { font-weight: 500; }
.picker .pick-item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker .pick-row .faint { flex: 0 0 auto; font-size: 0.719rem; }
.pick-caret {
  flex: 0 0 0.75rem;
  color: var(--text-faint);
  font-size: 0.625rem;
  cursor: pointer;
  transition: transform 0.12s ease;
}
.pick-caret.open { transform: rotate(90deg); }
/* An empty folder covers nobody, so it is shown but cannot be ticked. */
.picker .pick-row.pick-off .pick { cursor: default; }
.picker .pick-row.pick-off .pick-name { color: var(--text-faint); }

.setting-schedule {
  display: flex;
  gap: 0.75rem;
  align-items: flex-end;
  flex-wrap: wrap;
}
.setting-schedule .field { margin-bottom: 0; flex: 1 1 8.75rem; min-width: 0; }
.setting-schedule .field.narrow { flex: 0 0 7.5rem; }
.setting-schedule .field span { display: block; font-size: 0.75rem; color: var(--text-faint); margin-bottom: 0.25rem; }
.setting-schedule input, .setting-schedule select { width: 100%; padding: 0.375rem 0.562rem; font-size: 0.781rem; }

.divider { height: 0.062rem; background: var(--border-soft); margin: 1rem 0; }

.flag-list { display: flex; flex-direction: column; gap: 0.5rem; }
.flag { display: flex; align-items: center; gap: 0.5rem; font-size: 0.812rem; color: var(--text-muted); cursor: pointer; }
.flag input { width: auto; accent-color: #6d7cff; cursor: pointer; }
/* The record-wide toggle owns the group below it, so it is indented under it. */
.flag-group { display: flex; flex-direction: column; gap: 0.5rem; padding-left: 1.5rem; }
.flag-list .hint { margin-top: 0; padding-left: 1.5rem; }

/* ----------------------------------------------------- date range calendar */

.dr-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.438rem;
  padding: 0.438rem 0.625rem;
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg);
  color: var(--text-faint);
  font-size: 0.781rem;
  cursor: pointer;
  white-space: nowrap;
}
.dr-trigger:hover { border-color: #3d3d46; color: var(--text-muted); }
.dr-trigger.set { color: var(--text); border-color: #3d3d46; }

/* Inside a filter bar the trigger behaves like the inputs beside it, and its
   own text must escape the rule that styles field captions. */
/* Matched to the inputs beside it down to the line box, so the row keeps one
   baseline and the captions above it stay level. */
.filter-row .dr-trigger {
  display: flex;
  width: 100%;
  padding: 0.25rem 0.5rem;
  font-size: 0.75rem;
  line-height: 1rem;
}
.filter-row .dr-label { display: inline; font-size: inherit; color: inherit; margin: 0; }

.daterange {
  position: fixed;
  z-index: 70;
  width: 14.75rem;
  padding: 0.625rem;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  box-shadow: 0 0.75rem 1.75rem rgba(0, 0, 0, 0.5);
  user-select: none;
}

.dr-head { display: flex; align-items: center; gap: 0.375rem; margin-bottom: 0.5rem; }
.dr-title { flex: 1; text-align: center; font-size: 0.781rem; text-transform: capitalize; }
.dr-nav {
  width: 1.25rem;
  height: 1.25rem;
  padding: 0;
  border: 0;
  border-radius: 0.25rem;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.875rem;
  line-height: 1.125rem;
  cursor: pointer;
}
.dr-nav:hover { background: var(--surface-hover); color: var(--text); }

.dr-week, .dr-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 0.125rem; }
.dr-week span { text-align: center; font-size: 0.656rem; color: var(--text-faint); padding-bottom: 0.25rem; }

.dr-day {
  position: relative;
  height: 1.688rem;
  padding: 0;
  border: 0;
  border-radius: 0.312rem;
  background: transparent;
  color: var(--text-muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
}
.dr-day.empty { cursor: default; }
.dr-day:not(.empty):hover { background: var(--surface-hover); color: var(--text); }
.dr-day.today { color: #8ec5ff; }
/* The two ends of the span are solid; the days between only tinted, so it reads
   as one stretch instead of a row of separate picks. */
.dr-day.inside { background: rgba(109, 124, 255, 0.14); color: var(--text); border-radius: 0; }
.dr-day.edge { background: #4b5486; color: #eef0ff; }

/* A day that has something behind it, so a period can be chosen by eye. */
.dr-day.marked::after {
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0.188rem;
  width: 0.188rem;
  height: 0.188rem;
  margin-left: -0.094rem;
  border-radius: 50%;
  background: var(--blue);
}
.dr-day.edge.marked::after { background: #eef0ff; }

.dr-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.562rem;
  padding-top: 0.5rem;
  border-top: 0.062rem solid var(--border-soft);
}
.dr-hint { flex: 1; font-size: 0.688rem; color: var(--text-faint); }
.dr-clear {
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-faint);
  font-size: 0.719rem;
  cursor: pointer;
}
.dr-clear:hover { color: var(--text); text-decoration: underline; }

/* An author `display` outranks the browser's own rule for [hidden], which would
   otherwise leave flex blocks visible after they were hidden from script. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------------ mailings */

.mailing-preview {
  display: block;
  max-width: 12.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mail-delayed { display: block; font-size: 0.688rem; line-height: 1.2; color: var(--text-faint); }

.mail-count { font-variant-numeric: tabular-nums; }
.mail-count.sent { color: var(--green); }
.mail-count.failed { color: var(--red); }
.mail-sep { color: #3a3a44; margin: 0 0.25rem; }

.mail-toggle { display: flex; align-items: center; gap: 0.625rem; cursor: pointer; }

.mail-when { display: flex; gap: 0.75rem; align-items: flex-end; flex-wrap: wrap; }
.mail-when .field { margin-bottom: 0; flex: 0 0 12.5rem; }
.mail-when .field.narrow { flex: 0 0 7.5rem; }
.mail-when .dr-trigger { display: flex; width: 100%; }
.mail-when .dr-label { color: inherit; }
.mail-when .faint { padding-bottom: 0.562rem; }

/* The time field is not covered by the shared input rule, and its native picker
   needs to be told the page is dark. */
.mail-when input[type="time"] {
  width: 100%;
  background: var(--bg);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  color-scheme: dark;
  font-family: inherit;
  font-size: 0.844rem;
  padding: 0.5rem 0.75rem;
  outline: none;
}
.mail-when input[type="time"]:focus { border-color: #3d3d46; }

.picker .mail-sender .spacer { flex: 1; }
/* A bot without a token stays visible, so it is clear why it cannot be picked. */
.picker .mail-sender.off { color: var(--text-faint); cursor: default; }
.picker .mail-sender.off input { cursor: default; }

/* The keyword suggestions drop out of the panel, which otherwise clips them. */
.senders-panel { overflow: visible; }
.sender-filters { margin-bottom: 0.75rem; }

/* --------------------------------------------------------------- bot list */

.bots-table th.pick, .bots-table td.pick { width: 2.125rem; padding: 0; }
.bots-table th.pick input, .bots-table td.pick input { margin: 0 auto; }
.bots-table th.row-end, .bots-table td.row-end { width: 2.25rem; text-align: right; }
.bots-table .avatar { width: 1.375rem; height: 1.375rem; font-size: 0.625rem; }
.bots-table .badge { padding: 0.062rem 0.438rem; }
.bots-table .row-menu-btn { margin-left: 0; }
.bots-table td.bot-folder {
  max-width: 11.875rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bot-cell { display: flex; align-items: center; gap: 0.562rem; min-width: 0; }

.pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.875rem;
  padding: 0.75rem 0.875rem;
  border-top: 0.062rem solid var(--border-soft);
  font-size: 0.781rem;
}
.pager .faint { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------- mass edit */

.report-row { padding: 0.5rem 0; border-bottom: 0.062rem solid var(--border-soft); }
.report-row:last-child { border-bottom: none; }
.report-bot { font-size: 0.812rem; margin-bottom: 0.25rem; }

/* A template row leads with the picture it carries, because that is what one
   recognises a template by faster than its name. The rows are kept shallow: the
   list is scanned for the right template, not read. */
.template-row { display: flex; align-items: center; gap: 0.625rem; }
.template-row .avatar { flex: 0 0 auto; width: 2rem; height: 2rem; border-radius: 0.438rem; }
.template-main { min-width: 0; }
.template-main .faint { font-size: 0.75rem; margin-top: 0.062rem; }

/* A long note is cut rather than allowed to double the height of its row. */
.template-notes {
  max-width: 23.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Cards for the saved records that are read by their description rather than by
   a column of figures: creation templates and tasks. A table clipped the notes
   to one line, which is the one thing that says what the record is for. */
.record-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(26.25rem, 1fr));
  gap: 0.75rem;
}

.record-card {
  display: flex;
  flex-direction: column;
  gap: 0.562rem;
  padding: 0.875rem 1rem;
  background: var(--surface);
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius);
}

.record-card-head { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.record-card-head .spacer { flex: 1; }
.record-card-head .plain { font-size: 0.875rem; }

/* What the record is made of, read before the description: the kinds of action in
   it and the size it is ordered in. */
.record-card-tags {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  flex-wrap: wrap;
  font-size: 0.75rem;
}

.record-card-notes {
  flex: 1;
  min-height: 2.625rem;
  color: var(--text-faint);
  font-size: 0.781rem;
  line-height: 1.5;
  white-space: pre-wrap;
}

.record-card-foot {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding-top: 0.25rem;
  border-top: 0.062rem solid var(--border-soft);
}
.record-card-foot .spacer { flex: 1; }

/* A promoted link can be any length, and one of them must not push the state
   column off the screen. */
.bot-link-cell {
  max-width: 15rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ------------------------------------------------------------------ tasks */

/* A queue of steps ticked off as they go through. Applying a template talks to
   Telegram several times and an avatar upload alone can take seconds, so the
   dialog names the step it is on instead of just saying «применяю…». */
.task-list { display: flex; flex-direction: column; margin-top: 0.75rem; }

.task {
  display: grid;
  grid-template-columns: 1rem 1fr;
  gap: 0.562rem;
  align-items: baseline;
  padding: 0.438rem 0;
  font-size: 0.812rem;
  color: var(--text-muted);
  border-top: 0.062rem solid var(--border-soft);
}
.task:first-child { border-top: none; }
.task-mark { justify-self: center; font-size: 0.75rem; }
.task.done { color: var(--text); }
.task.done .task-mark { color: var(--green); }
.task.failed .task-mark { color: var(--red); }
.task-errors { grid-column: 2; }
.task-error { margin-top: 0.188rem; font-size: 0.75rem; color: var(--red); }

.spinner {
  display: inline-block;
  width: 0.688rem;
  height: 0.688rem;
  vertical-align: -0.063rem;
  border: 0.094rem solid var(--border);
  border-top-color: var(--text-muted);
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* -------------------------------------------------------------- promotion */

/* The service picker drops a list below its field, which a card that clips its
   overflow would cut in half. */
.promo-form { overflow: visible; }

/* The link is either typed or picked from our own resources. */
.promo-link-row { display: flex; gap: 0.5rem; }
.promo-link-row input { flex: 1; min-width: 0; }
.promo-link-row .btn { flex: 0 0 auto; }

/* One resource per order: rows act on a click instead of collecting ticks. */
.picker .pick-one {
  width: 100%;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  text-align: left;
}
.picker .pick-one:hover { background: var(--surface-hover); }
.picker .pick-one .spacer { flex: 1; }
.picker .pick-one .faint { flex: 0 0 auto; font-size: 0.719rem; }

/* Quantity, time and price stand side by side: the last two only answer what the
   first one is set to. */
.promo-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.875rem;
}
.promo-grid .field { margin-bottom: 0; }

.promo-value {
  padding: 0.562rem 0 0;
  font-size: 0.938rem;
  font-variant-numeric: tabular-nums;
}

/* What a confirmation is made of: label above, figure below, nothing else. */
.promo-facts { display: flex; flex-direction: column; }
.promo-fact {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  padding: 0.438rem 0;
  font-size: 0.812rem;
  border-top: 0.062rem solid var(--border-soft);
}
.promo-fact:first-child { border-top: none; }
.promo-fact .faint { flex: 0 0 8.125rem; font-size: 0.781rem; }
.promo-fact b { font-weight: 500; word-break: break-all; }

/* What the panel says about the link: neutral when it is fine, yellow when it is
   something to look at before paying. */
.promo-form .hint.warn { color: var(--yellow); }
.hint.warn { color: var(--yellow); }

/* Statuses are spelled out in words, so the field holding them needs more room
   than the usual narrow filter. */
.orders-filters .field.narrow { flex: 0 0 11.875rem; }

.orders-table td.promo-link {
  max-width: 13.75rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.orders-table td.promo-progress { width: 9.375rem; }
.orders-table .promo-progress .faint { margin-bottom: 0.312rem; font-variant-numeric: tabular-nums; }
.orders-table .faint { font-size: 0.719rem; }

/* --------------------------------------------------------- promotion orders */

/* The form on the left, what the strategy will do on the right, both as tall as
   the taller one: the plan is read against the numbers being typed. */
.promo-order-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 21.25rem;
  gap: 0.875rem;
  align-items: stretch;
}
.promo-order-layout > .edit-card { margin-bottom: 0; }

@media (max-width: 1320px) {
  .promo-order-layout { grid-template-columns: minmax(0, 1fr); }
}

.promo-link-row textarea { flex: 1; min-width: 0; min-height: 3.875rem; resize: vertical; }
.promo-link-row .btn { align-self: flex-start; }

.promo-when { margin-bottom: 0; }
.promo-when-row { display: flex; align-items: center; gap: 0.5rem; margin-top: 0.5rem; }
.promo-when-row[hidden] { display: none !important; }
.promo-when-row input[type="time"] {
  width: auto;
  background: var(--bg);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text);
  color-scheme: dark;
  font-family: inherit;
  font-size: 0.812rem;
  padding: 0.375rem 0.625rem;
  outline: none;
}

.promo-plan .edit-card-body { display: flex; flex-direction: column; gap: 0.75rem; }
.plan-notes { margin: 0; font-size: 0.812rem; color: var(--text-muted); }
.plan-facts { display: flex; gap: 1.125rem; font-size: 0.781rem; }
.plan-steps { display: flex; flex-direction: column; gap: 0.5rem; }

.plan-step {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  padding: 0.5rem 0.625rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-size: 0.781rem;
}
.plan-step .plan-num {
  flex: 0 0 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--surface-hover);
  color: var(--text-faint);
  font-size: 0.688rem;
  text-align: center;
  line-height: 1.125rem;
}
.plan-step .plan-body { flex: 1; min-width: 0; }
.plan-step .plan-title { display: flex; align-items: center; gap: 0.375rem; margin-bottom: 0.188rem; }
.plan-step .plan-numbers { flex: 0 0 auto; text-align: right; font-variant-numeric: tabular-nums; }
/* A wait is not something bought, so it is drawn as a gap in the chain. */
.plan-step.plan-timeout { border-style: dashed; background: none; }

/* The name and the description carry the meaning here, so they get the room; the
   tags and numbers take what they need. */
.strategies-table td:first-child { min-width: 10.625rem; }
.strategies-table td:nth-child(2) { min-width: 17.5rem; }

/* Inside an action the fields sit in one row rather than one under another. */
.step-grid { margin-bottom: 0; }
.step-grid .field { margin-bottom: 0; }
.step-grid .field[hidden] { display: none !important; }
.step-audience { max-width: 13.75rem; }

/* A wait is typed: the number and the unit sit on one line, both narrow. */
.wait-row { display: flex; gap: 0.5rem; max-width: 13.75rem; }
.wait-row .step-wait { flex: 1 1 5.625rem; min-width: 0; }
.wait-row .step-units { flex: 0 0 auto; width: auto; }
.wait-row input[type="time"] { flex: 1 1 0; min-width: 0; }

/* ---------------------------------------------------------------- traffic */

/* A handful of exclusive answers reads better as one control than as a select. */
.segmented {
  display: inline-flex;
  border: 0.062rem solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg);
}
.segmented button {
  padding: 0.375rem 0.688rem;
  border: 0;
  border-left: 0.062rem solid var(--border);
  background: transparent;
  color: var(--text-faint);
  font-family: inherit;
  font-size: 0.781rem;
  line-height: 1rem;
  white-space: nowrap;
  cursor: pointer;
}
.segmented button:first-child { border-left: 0; }
.segmented button:hover { color: var(--text-muted); }
.segmented button.on { background: var(--surface-hover); color: var(--text); }

/* The one number the top chart is about, set against its own heading. */
.chart-total { font-size: 1.25rem; font-weight: 600; font-variant-numeric: tabular-nums; }

.chart { position: relative; }
.chart-svg { display: block; }
.chart-empty { padding: 3.75rem 0; text-align: center; color: var(--text-faint); font-size: 0.812rem; }

.grid-line { stroke: var(--border-soft); stroke-width: 1; }
.grid-label {
  fill: var(--text-faint);
  font-size: 0.625rem;
  font-family: var(--font);
  text-anchor: end;
}
.day-label { text-anchor: middle; }
.bar-fill { fill: var(--blue); opacity: 0.75; }
.bar-hit { fill: transparent; }
.bar:hover .bar-fill { opacity: 1; }
.bar:hover .bar-hit { fill: rgba(255, 255, 255, 0.03); }

/* Several lines over one period. The colour is set on each line as it is drawn,
   since it is what tells one from another. */
.line-path { fill: none; stroke-linejoin: round; stroke-linecap: round; }
.line-hit { fill: transparent; }
.line-hit:hover { fill: rgba(255, 255, 255, 0.03); }
.chart-crosshair { stroke: var(--border); stroke-width: 1; stroke-dasharray: 2 3; }

/* Read down the whole day at once, so the tooltip is a small table rather than
   the one figure a bar chart needs. */
.chart-lines .chart-tip {
  white-space: nowrap;
  padding: 0.375rem 0.5rem;
  display: grid;
  gap: 0.125rem;
}
.tip-day { color: var(--text-faint); font-size: 0.688rem; }
.tip-row { display: flex; align-items: center; gap: 0.375rem; }
.tip-row b { color: var(--text); font-variant-numeric: tabular-nums; margin-left: auto; }
.tip-name { max-width: 12rem; overflow: hidden; text-overflow: ellipsis; }

/* Also the switch: a line is taken out of the drawing by its own legend entry,
   which is how one crowded chart is read a few lines at a time. */
.chart-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 0.75rem;
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 0.062rem solid var(--border-soft);
}
.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.125rem 0.25rem;
  border: 0;
  border-radius: 0.312rem;
  background: transparent;
  color: var(--text-muted);
  font-family: inherit;
  font-size: 0.75rem;
  cursor: pointer;
}
.legend-item:hover { background: var(--surface-hover); color: var(--text); }
.legend-item.off { opacity: 0.4; }
.legend-item.off .legend-name { text-decoration: line-through; }
.legend-dot { width: 0.5rem; height: 0.5rem; border-radius: 50%; flex: 0 0 auto; }
.legend-name { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.legend-value { color: var(--text-faint); font-variant-numeric: tabular-nums; }

/* What is counted, and whether the empty rows are worth the room. Kept out of
   the filter panel: these two are changed while reading, the filters are set
   once and left alone. */
.traffic-bar {
  display: flex;
  align-items: center;
  gap: 0.875rem;
  margin-bottom: 0.875rem;
}
.traffic-bar .spacer { flex: 1; }
.switch-line {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.781rem;
  color: var(--text-muted);
  cursor: pointer;
}

/* The tree stands beside the drawing rather than behind a dialog: here the
   selection is changed while reading, and a tree in view is one click per
   change instead of four. Narrow on purpose — the charts are the page. */
.traffic-layout {
  display: grid;
  grid-template-columns: 16.25rem minmax(0, 1fr);
  gap: 0.875rem;
  margin-bottom: 0.875rem;
}
.traffic-aside { display: flex; flex-direction: column; min-height: 0; }
.traffic-aside .panel-head { padding-right: 0.5rem; }
.traffic-aside .panel-head .faint { font-size: 0.719rem; white-space: nowrap; }
.aside-search { padding: 0.625rem 0.75rem 0; }
.aside-search .pick-search { margin: 0; }
/* The tree takes whatever height the charts beside it settle on, and scrolls
   inside its own box rather than stretching the row. */
.traffic-aside .aside-tree {
  flex: 1 1 auto;
  min-height: 11rem;
  max-height: none;
  margin: 0.5rem 0.375rem 0.375rem;
  border: 0;
  border-radius: 0;
  background: transparent;
}
.traffic-charts { display: flex; flex-direction: column; gap: 0.875rem; }
.traffic-charts > .panel { flex: 1 1 auto; }

@media (max-width: 1080px) {
  .traffic-layout { grid-template-columns: minmax(0, 1fr); }
  .traffic-aside .aside-tree { max-height: 18.75rem; }
}

/* Day by day under the charts: the days read across, the rows read down, and
   the busiest row is the first one. A month of columns is scrolled through, so
   the name and the total are pinned — a column of figures on its own says
   nothing about whose they are. */
.rank-table.analytics.traffic-table th.rank-head,
.rank-table.analytics.traffic-table td.rank-name {
  min-width: 15rem;
  max-width: 15rem;
  width: 15rem;
}
.rank-table.analytics.traffic-table th.freq-head,
.rank-table.analytics.traffic-table td.freq {
  position: sticky;
  left: 15rem;
  z-index: 1;
  width: 7rem;
  background: var(--surface);
  border-right: 0.062rem solid var(--border-soft);
}
.rank-table.analytics.traffic-table tr:hover > td.freq { background: var(--surface-hover); }
.traffic-table .place {
  flex: 0 0 1.5rem;
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-size: 0.719rem;
}
.rank-table.analytics.traffic-table td.freq { padding-left: 0.75rem; padding-right: 0.75rem; }
.traffic-table .freq-value { font-variant-numeric: tabular-nums; }
.traffic-table .freq-share { margin-left: 0.375rem; color: var(--text-faint); font-size: 0.688rem; }
.rank-table.analytics.traffic-table .pos-head,
.rank-table.analytics.traffic-table td.pos { width: 3.75rem; text-align: center; }
.rank-table.analytics.traffic-table td.pos .pos-value {
  width: auto;
  text-align: center;
  color: var(--text);
}
.traffic-table .row-note td { padding: 1.25rem 0.75rem; text-align: center; }
/* The column the order stands on, tinted rather than explained. */
.rank-table.analytics.traffic-table .pos-head.ranked,
.rank-table.analytics.traffic-table td.pos.ranked { background: rgba(88, 166, 255, 0.07); }
.rank-table.analytics.traffic-table .pos-head.ranked .run-day { color: var(--text-muted); }
.rank-table.analytics.traffic-table tr:hover > td.pos.ranked {
  background: rgba(88, 166, 255, 0.12);
}

/* --------------------------------------------------------------------- login */

.login-screen {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 1.5rem;
}

.login-card {
  width: 20rem;
  padding: 1.375rem 1.375rem 1.5rem;
  background: var(--surface);
  border: 0.062rem solid var(--border);
  border-radius: var(--radius);
}

.plain-screen { max-width: 28.75rem; margin: 4.5rem auto; padding: 0 1.5rem; }
.plain-screen .brand { padding: 0 0 0.375rem; }
.plain-title { margin: 0 0 0.875rem; font-size: 2.125rem; letter-spacing: -0.02em; }

.login-card .brand { padding: 0 0 1.125rem; }
.login-card .btn { width: 100%; justify-content: center; }
.login-error { margin: -0.25rem 0 0.75rem; font-size: 0.781rem; color: var(--red); }

/* ------------------------------------------------------------------ finances */

.money-cards { margin-top: 0.875rem; }
.money-cards .stat-value { font-size: 1.312rem; }

/* Two registers side by side while the window allows it: each is short enough
   to be read whole, and pairing them keeps the scroll down. */
.money-tables {
  margin-top: 0.875rem;
  grid-template-columns: repeat(auto-fit, minmax(26.875rem, 1fr));
  align-items: start;
}

.money-top { margin-top: 0.875rem; }
.money-table .num { text-align: right; }
.money-table td { white-space: nowrap; }

/* ------------------------------------------------------------------ accounts */

/* Dropping a folder is the whole import, so the target for it is the page's main
   surface rather than a field with a button next to it. */
.drop-zone {
  padding: 1.625rem 1.25rem;
  border: 0.062rem dashed var(--border);
  border-radius: var(--radius-sm);
  background: #141418;
  text-align: center;
  transition: border-color 0.12s ease, background 0.12s ease;
}
.drop-zone.over { border-color: var(--blue); background: rgba(79, 140, 255, 0.06); }
.drop-zone.busy { opacity: 0.7; }
.drop-title { font-size: 0.844rem; color: var(--text-muted); }
.drop-actions { display: flex; gap: 0.5rem; justify-content: center; margin-top: 0.875rem; }
.drop-state { margin-top: 0.75rem; font-size: 0.781rem; color: var(--text-muted); }

.accounts-table th.rank-head { min-width: 12.5rem; }
.accounts-table .avatar { width: 1.25rem; height: 1.25rem; font-size: 0.625rem; }
.accounts-table td .badge { margin-right: 0.375rem; padding: 0.062rem 0.438rem; }
.accounts-table .type-tag + .type-tag { margin-left: 0.312rem; }
.accounts-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.accounts-table .cell-line > .res-title { min-width: 0; }

/* An inline tag sits on the text baseline, which leaves its own text low in the
   box; the roles are told apart by colour before the word is read. */
.type-tag { display: inline-flex; align-items: center; line-height: 1.35; }
.type-tag.role-checker {
  color: #8fb8ff;
  border-color: rgba(88, 166, 255, 0.32);
  background: rgba(88, 166, 255, 0.09);
}
.type-tag.role-worker {
  color: #d3a6ff;
  border-color: rgba(178, 122, 255, 0.32);
  background: rgba(178, 122, 255, 0.09);
}
.type-tag.role-reserve {
  color: #9fe0c0;
  border-color: rgba(86, 211, 158, 0.32);
  background: rgba(86, 211, 158, 0.09);
}

/* Whether the account carries a cloud password: a plus if it does, a dash if
   not. The plus is the good news, so it is the one that gets the colour. */
.twofa-mark { font-weight: 600; }
.twofa-mark.on { color: #7ee18a; }
.twofa-mark.off { color: rgba(255, 255, 255, 0.35); }
.accounts-table td:has(.twofa-mark) { text-align: center; }

/* One mark for whether anything is restricting the account: a green tick when it
   is clear, a red cross when a block, a rest or being worn out holds it back. */
.limit-mark { font-weight: 600; }
.limit-mark.ok { color: #7ee18a; }
.limit-mark.bad { color: #ff6b6b; }
.accounts-table td:has(.limit-mark) { text-align: center; }

/* Made, alive, lost — one figure explained by the two after it. */
.res-tally { font-variant-numeric: tabular-nums; white-space: nowrap; }
.res-tally b { font-weight: 500; }
.res-tally b.live { color: #7ee18a; }
.res-tally b.lost { color: #ff8f8f; }
.res-tally em { font-style: normal; color: #3a3a44; margin: 0 0.25rem; }

/* Nine filters do not fit one line and two flex lines never line up, so they are
   laid on a grid: every field keeps its column whichever row it is on. */
.filters .filter-row.filter-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(8.75rem, 1fr));
  gap: 0.75rem 0.625rem;
  align-items: end;
}
.filter-grid .reset { grid-column: 5; justify-self: end; }

@media (max-width: 1440px) {
  .filters .filter-row.filter-grid { grid-template-columns: repeat(3, minmax(8.75rem, 1fr)); }
  .filter-grid .reset { grid-column: auto; }
}

/* Four filters that must read as four of the same thing. A flex row gives each
   one the width its own control asks for — a date label and a suggestion box ask
   for more than a plain input — so the columns are stated instead. */
.filters .filter-row.filter-even {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 16.25rem)) 1fr;
  gap: 0.75rem 0.625rem;
  align-items: end;
}
.filter-even .reset { justify-self: end; }

@media (max-width: 1080px) {
  .filters .filter-row.filter-even { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* «Ресурсов до»: a number and what it counts, read as one filter. */
.field-pair .pair { display: flex; gap: 0.375rem; }
.field-pair .pair input { min-width: 0; }
.field-pair .pair select { width: auto; flex: 0 0 auto; }

/* The roles cell is the way to change them, so it has to look pressable without
   turning into a button in the middle of a table. */
.role-cell {
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-align: left;
}
.role-cell:hover .type-tag { border-color: var(--border-strong); color: var(--text); }
.role-cell .type-tag + .type-tag { margin-left: 0.375rem; }

.role-list { display: flex; flex-direction: column; gap: 0.25rem; margin: 0.75rem 0 0.25rem; }
.role-pick {
  display: flex;
  align-items: flex-start;
  gap: 0.562rem;
  padding: 0.5rem 0.625rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: 0.438rem;
  cursor: pointer;
  font-size: 0.812rem;
}
.role-pick:hover { background: var(--surface-hover); }
.role-pick input { width: auto; margin: 0.125rem 0 0; accent-color: #6d7cff; }
.role-pick b { display: block; font-weight: 500; }

/* ------------------------------------------------------------------ creation */

.creation-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 21.25rem;
  gap: 0.875rem;
  align-items: start;
}

@media (max-width: 1320px) {
  .creation-layout { grid-template-columns: minmax(0, 1fr); }
}

/* Two fields on the template page, three on the launch page: the row takes as many
   as it was given rather than leaving a hole where the count would be. */
.creation-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10.625rem, 1fr));
  gap: 0.875rem;
}
.creation-grid .field { margin-bottom: 0; }

/* Where the name comes from, the name itself, and the masking switch: one line, and
   the middle of it is either a field to type in or a button that opens the keywords. */
.title-row { display: flex; align-items: center; gap: 0.875rem; flex-wrap: wrap; }
.title-row input { flex: 1; min-width: 0; }
.title-row .check { flex: 0 0 auto; white-space: nowrap; }
.title-row .mode-pick { flex: 0 0 auto; width: auto; }
.title-row .btn { flex: 1; min-width: 11.25rem; justify-content: flex-start; }
.title-row input[hidden], .title-row .btn[hidden] { display: none !important; }

/* One name per line: the textarea sits under the mode row, not inside it. */
#bot-title {
  display: block;
  width: 100%;
  margin-top: 0.625rem;
  min-height: 4.875rem;
  resize: vertical;
  font: inherit;
}
#bot-title[hidden] { display: none !important; }

/* A second heading inside the same card: the order continues, the subject changes. */
.edit-card-split { border-top: 0.062rem solid var(--border-soft); }

.account-picker { max-height: 20rem; }
.account-picker .pick .spacer { flex: 1; }
.account-picker .pick .faint { flex: 0 0 auto; font-size: 0.719rem; }

#account-mode { margin-bottom: 0.125rem; }

/* The name of the template carries the row, the note beside it is a reminder rather
   than a thing to read, so it is cut off instead of wrapping. */
.presets-table td:first-child { min-width: 13.75rem; }
.presets-table .type-tag { margin-left: 0.5rem; }

/* The examples belong to the fields above them, not to a column of their own. */
.preview { margin-top: 0.875rem; padding-top: 0.75rem; border-top: 0.062rem solid var(--border-soft); }
.preview-title { font-size: 0.75rem; color: var(--text-faint); margin-bottom: 0.375rem; }
.preview-row { display: flex; flex-direction: column; gap: 0.062rem; font-size: 0.812rem; }
.preview-row + .preview-row { margin-top: 0.5rem; }
.preview-row b { font-weight: 500; }

.run-row + .run-row { margin-top: 0.875rem; padding-top: 0.875rem; border-top: 0.062rem solid var(--border-soft); }
.run-head { display: flex; align-items: baseline; gap: 0.5rem; font-size: 0.812rem; }
.run-head .spacer { flex: 1; }
.run-head .faint { font-size: 0.75rem; }
.run-attempts { margin-top: 0.5rem; }
.run-attempt {
  display: flex;
  align-items: baseline;
  gap: 0.625rem;
  padding: 0.188rem 0;
  font-size: 0.781rem;
  border-top: 0.062rem solid var(--border-soft);
}
.run-attempt .spacer { flex: 1; }
.attempt-defect .faint:last-child { color: var(--yellow); }
.attempt-failed .faint:last-child { color: var(--red); }

/* --------------------------------------------------------------------- tasks */

/* Six kinds of action need six buttons, and on a narrow window they wrap rather
   than push the card wider. */
.task-adders { flex-wrap: wrap; }

/* The plan beside the form is the cascade read out loud: what happens, in order. */
.task-plan { display: flex; flex-direction: column; gap: 0.5rem; }
.plan-row {
  display: flex;
  align-items: baseline;
  gap: 0.562rem;
  padding: 0.5rem 0.625rem;
  border: 0.062rem solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: var(--bg);
  font-size: 0.781rem;
}
.plan-row .step-num {
  flex: 0 0 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--surface-hover);
  color: var(--text-faint);
  font-size: 0.688rem;
  text-align: center;
  line-height: 1.125rem;
}
.plan-row .plan-body { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.plan-row .plan-body b { font-weight: 500; }

.task-tree { max-height: 16.25rem; }
.task-tree .pick-row .spacer, .task-tree .pick-item .spacer { flex: 1; }
.task-tree .pick-item .pick-name { flex: 0 0 auto; max-width: 11.25rem; overflow: hidden; text-overflow: ellipsis; }

/* A launched task is a card with a to-do list: the marks say where it has got to
   without the operator reading a single line of text.

   Shut, the card is that one line — the name, where it has got to, and what it is
   on now. The list itself is for the run being read, not for the twenty above it. */
.task-run + .task-run { margin-top: 0.5rem; }
.task-run-head { cursor: pointer; gap: 0.5rem; }
.task-run-head .row-caret { flex: 0 0 auto; }
.task-run.open .task-run-head .row-caret { transform: rotate(90deg); }
.task-run .run-id { font-variant-numeric: tabular-nums; }
.task-run .run-full { display: none; }
.task-run.open .run-full { display: block; }

.task-run:not(.open) .panel-head { border-bottom: none; padding-bottom: 0.25rem; }
.task-run .run-brief {
  /* Lined up under the name, past the caret, not under the caret. */
  padding: 0 0.75rem 0.625rem 2.062rem;
  font-size: 0.781rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.task-run.open .run-brief,
.task-run .run-brief:empty { display: none; }

.task-run .panel-head h3 { font-size: 0.875rem; }
.task-run .panel-head .type-tag { margin-left: 0.25rem; }
.task-facts { font-size: 0.781rem; }
.task-run .task-list { margin-top: 0.625rem; }
.task-run .task-body { display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.5rem; }
.task-run .task-body b { font-weight: 500; color: var(--text); }
.task-run .task-result { flex: 1 1 100%; font-size: 0.75rem; }
.task-run .task { align-items: baseline; }
.task-run .task.waiting { color: var(--text-faint); }
.task-run .task.waiting .task-body b { color: var(--text-faint); }
.task-run .task.skipped { opacity: 0.6; }
.task-run .task-error { margin-top: 0.5rem; }

/* Inside a long action: the same to-do list, one tab in. */
.task-parts {
  flex: 1 1 100%;
  margin-top: 0.25rem;
  padding-left: 0.688rem;
  border-left: 0.062rem solid var(--border-soft);
}
.task-part {
  grid-template-columns: 0.812rem 1fr;
  gap: 0.438rem;
  padding: 0.188rem 0;
  border-top: none;
  font-size: 0.781rem;
}
.task-run .task-part .task-body b { font-weight: 400; }
.task-part .task-mark { font-size: 0.688rem; }
.task-part .spinner { width: 0.562rem; height: 0.562rem; border-width: 0.094rem; }
.task-part.done .task-body b { color: var(--text-muted); }

/* The count of a wait is the point of the row it sits in, so it stays readable
   even where everything around it has gone faint. */
.task-clock { color: var(--text); font-variant-numeric: tabular-nums; }
.task-part .task-clock { color: var(--text-muted); }
.status-paused, .status-failed { color: var(--red); border-color: var(--red); }
.status-running { color: var(--text); }

.chart-tip {
  position: absolute;
  top: 0;
  transform: translateX(-50%);
  padding: 0.188rem 0.438rem;
  border: 0.062rem solid var(--border);
  border-radius: 0.312rem;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 0.719rem;
  white-space: nowrap;
  pointer-events: none;
}
