/* ==========================================================================
   Hubbs — оформление. Шрифты только системные, изображения только
   встроенные: внешних сетевых запросов интерфейс не делает вообще.
   ========================================================================== */

:root {
  --sidebar-bg: #1e1f21;
  --sidebar-fg: #c7c9cc;
  --sidebar-fg-dim: #8b8e94;
  --sidebar-hover: #2c2d30;
  --sidebar-active: #35373b;

  --bg: #ffffff;
  --bg-muted: #f8f8f9;
  /*
   * The band under a section's heading (list sections, sections inside a
   * task, in the list and in the card): one quiet step under the row hover.
   * Opaque on purpose: the sticky name cell takes the row's background, and
   * a see-through fill showed the columns sliding under the name.
   */
  --section-fill: #f6f6f7;
  --border: #edeae9;
  --border-strong: #cfcbc9;

  --fg: #1e1f21;
  --fg-muted: #6b6f76;
  --fg-faint: #9ca0a8;
  /* Название задачи: открытая — самым тёмным, выполненная — заметно
     светлее. Разница между ними важнее, чем красота каждого по
     отдельности: по ней в длинном списке видно, где ещё работа. */
  --title-open: #111214;
  --title-done: #a9aeb6;

  --brand: #f2584d;
  /* The accent is our blue, the brand red stays with the logo alone. Red
     marked tabs, switches, selection and "unread" alike, and next to the real
     red of overdue and delete it read as one alarm everywhere. Defined
     through --blue, so each theme's blue carries over by itself. */
  --accent: var(--blue);
  --accent-hover: var(--primary-hover);
  --accent-soft: var(--blue-soft);

  --green: #1f9d6b;
  --amber: #d99a2b;
  --red: #d3453c;
  --red-soft: #fdecea;
  --violet: #7b61c9;
  --blue: #3a6fd8;
  /* Main action (Create, Add task, dialog submit): blue, not the brand red.
     Red is left for danger and overdue, so a button no longer reads as an alarm. */
  --primary: #3a6fd8;
  --primary-hover: #2f5fc2;
  /* Focus and "chosen" are blue, not the accent: a field being typed in is
     not a warning, and red around it read as an error. Derived from --blue,
     so the dark theme follows without a copy of its own. */
  --focus: var(--blue);
  --focus-soft: color-mix(in srgb, var(--blue) 18%, transparent);
  /* Выделение строк: спокойный синий, отдельно от акцента. */
  --pick-soft: #eaf1fe;
  --pick-hover: #e1ebfd;
  --pick-line: #a8c5f5;

  /* Поверхность карточек и панелей. Отдельно от --bg: в тёмной теме
     они расходятся, а белый текст на цветном должен остаться белым. */
  --surface: #ffffff;
  --border-soft: #f4f2f1;
  --amber-soft: #fff5e6;
  --amber-fg: #a1660f;
  --amber-line: #f2dcb8;
  /* Зелёный набор — пара к янтарному: «ждёт» и «дождалась». Раньше эти
     три цвета стояли числами прямо в правиле «Выполнена». */
  --green-soft: #e9f7f0;
  --green-fg: #1f7a54;
  --green-line: #a9ddc4;
  --red-fg: #a5302a;
  --red-line: #f6c9c5;
  --blue-soft: #eef4fb;
  --blue-line: #d3e0f3;
  /* Подложка упоминания: заметнее синего фона плашек — имя посреди реплики
     должно читаться как отдельная вещь, но не как ошибка. */
  --mention-bg: #dfe9fb;
  --mention-line: #c6d8f5;
  --toast-bg: #1e1f21;
  --toast-fg: #ffffff;

  --scroll-thumb: #d3d5d8;
  --scroll-thumb-hover: #b9bcc0;
  --sidebar-line: #303134;
  --sidebar-thumb: #46484c;

  --radius: 6px;
  --radius-lg: 10px;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .06);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, .10);
  --shadow-lg: -8px 0 32px rgba(0, 0, 0, .13);

  --sidebar-w: 260px;
  --rail-w: 52px;
  --sidebar-head-h: 52px;
  --topbar-h: 56px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
          Arial, "Noto Sans", sans-serif;
}

/*
 * Projects' palette: the very colours of the options' dots (--chip-dot below),
 * so a project coloured "yellow" is the yellow of a field's option. The two
 * palettes had the same names and slightly different shades.
 */
.c-rose { --project-color: #e8615a; }
.c-amber { --project-color: #f0913a; }
.c-gold { --project-color: #f5b400; }
.c-yellow { --project-color: #e3cf2f; }
.c-olive { --project-color: #a8c43c; }
.c-lime { --project-color: #62b85a; }
.c-emerald { --project-color: #2fb487; }
.c-teal { --project-color: #36b8b0; }
.c-sky { --project-color: #4a9be8; }
.c-indigo { --project-color: #6b72e8; }
.c-violet { --project-color: #9c6ee0; }
.c-magenta { --project-color: #c95fc9; }
.c-fuchsia { --project-color: #e8559b; }
.c-pink { --project-color: #f29db4; }
.c-slate { --project-color: #8d97a3; }

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  overflow: hidden;
  font-family: var(--font);
  font-size: 14px;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

button, input, textarea, select { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: var(--blue); }
/*
 * A page opened from a control (a sidebar item, a tile, a chip, a tab, a
 * crumb) is a link, so that it opens in a new tab (pageLink in dom.js,
 * iteration 17, item 6). It keeps the look the control had as a button: the
 * blue and the underline above are a text link's. Written at the weight of
 * the rule above, so any class of the control still sets its own colour.
 */
a:where(.page-link) { color: inherit; text-decoration: none; }

/*
 * An icon keeps its drawn size in a row: each is drawn on whole pixels for
 * 12, 16 or 24 (dom.js), and a flex row squeezed it beside a long label to
 * 12-13.6 px of a 16 drawing, a smear, or to nothing. The text next to it
 * gives way instead.
 */
svg.icon { flex: none; }

/*
 * On a 1x screen a drawing of straight and 45-degree lines only (.crisp,
 * pixelArt() in dom.js) is drawn without smoothing. Its points sit on pixel
 * centres, so each stroke is whole pixels; smoothed, its diagonals were
 * grey two-pixel smears beside black straight lines, which read as lines
 * changing colour (the owner, iteration 16, the card head). Arcs, curves
 * and other slants keep smoothing: without it circles came out octagons
 * and slants uneven stairs (the owner, iteration 17, item 11). Only the
 * drawn sizes and only exactly 1dppx: on the doubled 32/48 sizes, 2x
 * screens and 125-150% scaling, crisp edges turn slants into stairs and
 * make strokes one or two pixels at random, so those keep smoothing.
 * checkGlyph() has no .icon class and keeps its smoothed 1.5 px tick.
 */
@media (resolution: 1dppx) {
  svg.icon.crisp:is([width="12"], [width="16"], [width="24"]) { shape-rendering: crispEdges; }
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: 6px; border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: content-box; }

#root { height: 100%; }

.hidden { display: none !important; }

/* ============================================================ вход ===== */

.login {
  min-height: 100%; display: flex; align-items: center; justify-content: center; padding: 24px;
  background:
    radial-gradient(900px 500px at 10% 0%, #fff1ef 0%, transparent 60%),
    radial-gradient(700px 500px at 100% 100%, #eef0ff 0%, transparent 60%),
    var(--bg-muted);
}
.login-stage {
  display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(320px, 420px); gap: 32px; align-items: center;
  width: 100%; max-width: 980px;
}
.login-intro { position: relative; padding: 24px 8px; overflow: hidden; }
.login-intro h1 { font-size: 34px; line-height: 1.15; letter-spacing: -.6px; margin: 22px 0 12px; font-weight: 700; }
.login-intro p { color: var(--fg-muted); font-size: 15px; line-height: 1.6; max-width: 440px; margin: 0 0 22px; }
.login-points { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.login-points li { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--fg); }
.login-points li svg { color: var(--accent); flex: 0 0 auto; }
.login-orbs { position: absolute; inset: 0; pointer-events: none; z-index: -1; }
.login-orbs i { position: absolute; border-radius: 50%; background: var(--accent); opacity: .08; }
.login-orbs i:nth-child(1) { width: 260px; height: 260px; right: -60px; top: -80px; }
.login-orbs i:nth-child(2) { width: 120px; height: 120px; left: -30px; bottom: 10px; background: #5b5fc7; }
.login-orbs i:nth-child(3) { width: 60px; height: 60px; right: 40%; bottom: -10px; background: #1f9d6b; }

.login-card {
  width: 100%; background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; box-shadow: 0 18px 50px rgba(30, 31, 33, .10); padding: 32px;
}
.login-card h2 { margin: 0 0 6px; font-size: 22px; letter-spacing: -.3px; }
.login-logo { display: flex; align-items: center; gap: 12px; }
.login-logo b { font-size: 24px; letter-spacing: -.3px; }
.login-sub { color: var(--fg-muted); margin: 0 0 22px; font-size: 13px; line-height: 1.55; }
.login-form .field input { padding: 11px 12px; font-size: 14.5px; border-radius: 9px; }
.input-with-eye { position: relative; }
.input-with-eye input { padding-right: 40px !important; }
/*
 * Centred by auto margins, not by translateY(-50%): the button is 31 px
 * tall, and half of it moved the eye half a pixel, which blurs a smoothed
 * icon's straight lines and doubled some pixels of a crisp one (svg.icon
 * above). A layout offset is snapped to the pixel grid, a transform is not.
 */
.input-with-eye .eye {
  position: absolute; right: 6px; top: 0; bottom: 0; height: fit-content; margin: auto 0; border: 0; background: none;
  color: var(--fg-faint); padding: 6px; border-radius: 6px; cursor: pointer; line-height: 0;
}
.input-with-eye .eye:hover { color: var(--fg); background: var(--bg-muted); }
.login-remember { display: flex; gap: 10px; align-items: flex-start; margin: 4px 0 18px; cursor: pointer; font-size: 13px; }
.login-remember input { margin-top: 3px; accent-color: var(--accent); width: 16px; height: 16px; }
.login-remember b { display: block; font-weight: 500; }
.login-remember small { color: var(--fg-muted); font-size: 12px; }
.btn-lg { padding: 12px 16px; font-size: 15px; border-radius: 9px; }
.login-back { display: block; text-align: center; margin-top: 14px; font-size: 13px; color: var(--fg-muted); }
.pw-strength { height: 4px; border-radius: 2px; background: var(--bg-muted); margin-top: 8px; overflow: hidden; }
.pw-strength i { display: block; height: 100%; width: 0; background: var(--red); transition: width .2s, background .2s; }
.pw-strength[data-score="1"] i { width: 25%; }
.pw-strength[data-score="2"] i { width: 50%; background: var(--amber); }
.pw-strength[data-score="3"] i { width: 75%; background: #6fbf73; }
.pw-strength[data-score="4"] i { width: 100%; background: var(--green); }
@media (max-width: 820px) {
  .login-stage { grid-template-columns: 1fr; max-width: 440px; gap: 8px; }
  .login-intro h1 { font-size: 24px; }
  .login-intro p, .login-points { display: none; }
  .login-intro { padding: 8px 4px 12px; }
  .login-card { padding: 24px 20px; }
}
/* The same for a dark theme that comes from the system: these used to
   apply only to one chosen by hand, and a phone in dark mode got light
   cells and a light login card. */
:root[data-theme="dark"] .login {
  background: radial-gradient(900px 500px at 10% 0%, #2a1f1e 0%, transparent 60%), radial-gradient(700px 500px at 100% 100%, #1e2030 0%, transparent 60%), var(--bg);
}
:root[data-theme="dark"] .login-card { box-shadow: 0 18px 50px rgba(0, 0, 0, .45); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .login {
    background: radial-gradient(900px 500px at 10% 0%, #2a1f1e 0%, transparent 60%), radial-gradient(700px 500px at 100% 100%, #1e2030 0%, transparent 60%), var(--bg);
  }
  :root:not([data-theme="light"]) .login-card { box-shadow: 0 18px 50px rgba(0, 0, 0, .45); }
}

.field { margin-bottom: 14px; }
.field label { display: block; font-size: 12px; font-weight: 600; color: var(--fg-muted); margin-bottom: 6px; }
.field input, .field textarea, .field select {
  width: 100%;
  padding: 9px 11px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  outline: none;
  transition: border-color .12s, box-shadow .12s;
}
.field input:focus, .field textarea:focus, .field select:focus {
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--focus-soft);
}
/* The project picker in a form looks like the field it replaced (project-pick.js). */
.project-select {
  display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0;
  padding: 9px 11px; border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--fg); font: inherit; text-align: left; cursor: pointer;
}
.project-select:hover { border-color: var(--fg-faint); }
.project-select:focus-visible, .project-select.cell-open { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); outline: none; }
.project-select .label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.project-select-dot { display: inline-flex; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 9px 16px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--bg-muted);
  font-weight: 500;
  transition: background .12s, border-color .12s;
}
/* A step darker than the button, in either theme: a fixed light grey turned a
   hovered button near white with light text on it in the dark theme. */
.btn:hover { background: color-mix(in srgb, var(--fg) 7%, var(--bg-muted)); }
.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-ghost { background: transparent; color: var(--fg-muted); }
.btn-ghost:hover { background: var(--bg-muted); color: var(--fg); }
.btn-outline { background: var(--surface); border-color: var(--border-strong); }
.btn-outline:hover { background: var(--bg-muted); }
.btn-block { width: 100%; }
.btn[disabled] { opacity: .55; cursor: default; }
.btn-sm { padding: 5px 10px; font-size: 13px; }

.alert {
  padding: 9px 12px;
  border-radius: var(--radius);
  background: var(--red-soft);
  color: var(--red-fg);
  font-size: 13px;
  margin-bottom: 14px;
  border: 1px solid var(--red-line);
}
/* A task that just changed section: a ring that fades out. */
.menu-point { position: fixed; width: 1px; height: 1px; pointer-events: none; }
.just-moved { animation: just-moved 1.8s ease-out; }
@keyframes just-moved {
  from { box-shadow: 0 0 0 2px var(--focus), 0 0 0 6px var(--focus-soft); }
  to { box-shadow: 0 0 0 2px transparent, 0 0 0 6px transparent; }
}
.alert.info { background: var(--blue-soft); color: var(--fg); border-color: var(--blue-line); }

.login-hint {
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--fg-muted);
  line-height: 1.7;
}
.login-hint code { background: var(--bg-muted); padding: 1px 5px; border-radius: 4px; font-size: 11.5px; }

/* ========================================================== каркас ===== */

.shell { display: flex; height: 100%; position: relative; }

.sidebar {
  width: var(--sidebar-w);
  flex: 0 0 var(--sidebar-w);
  background: var(--sidebar-bg);
  color: var(--sidebar-fg);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/*
 * One grid for both widths of the panel: every icon sits 18px from the left
 * edge (8 of the panel, 10 of the row) and every row has its own fixed
 * height. Folding then only hides the words: nothing is centred anew and
 * nothing changes height, so no icon moves. The folded panel is 52px wide,
 * which puts the 16px icon in its middle.
 */
.sidebar-head {
  height: var(--sidebar-head-h);
  display: flex;
  align-items: center;
  padding: 0 8px;
  flex: 0 0 auto;
}
.sidebar-brand {
  display: flex; align-items: center; gap: 9px; padding: 7px; border: 0; border-radius: var(--radius);
  background: transparent; color: #fff; cursor: pointer; min-width: 0;
}
.sidebar-brand:hover { background: var(--sidebar-hover); }
.sidebar-brand b { font-size: 15px; letter-spacing: -.1px; }

/* Search and Create: what the top bar used to hold. */
.sidebar-tools { display: flex; flex-direction: column; gap: 8px; padding: 0 8px 8px; flex: 0 0 auto; }
.sidebar-search { max-width: none; flex: 0 0 auto; }
.sidebar .sidebar-search input {
  background: var(--sidebar-hover); color: var(--sidebar-fg); border-color: transparent;
  border-radius: var(--radius); padding: 0 30px 0 34px; height: 32px;
}
.sidebar .sidebar-search input::placeholder { color: var(--sidebar-fg-dim); }
.sidebar .sidebar-search input:focus { background: var(--surface); color: var(--fg); border-color: var(--border-strong); }
.sidebar .sidebar-search > .icon { left: 10px; color: var(--sidebar-fg-dim); }
/* Its own 32px whatever the field does: the shared .search grows by flex,
   and with the field opened over the page it shrank to nothing. */
.sidebar .sidebar-search { flex: 0 0 32px; height: 32px; }
.search-key {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  font: 11px/1 var(--font); color: var(--sidebar-fg-dim); border: 1px solid var(--sidebar-line);
  border-radius: 4px; padding: 2px 5px; pointer-events: none;
}
.sidebar-search:focus-within .search-key { display: none; }
.sidebar .create-btn { justify-content: flex-start; width: 100%; height: 32px; padding: 0 8px 0 9px; gap: 11px; }
.sidebar .create-btn .create-label { flex: 1; text-align: left; }

/* The fold button sits on the panel's edge: it takes no room from either
   side. Always shown: appearing on hover, it blinked each time the mouse
   left the page or came back, where nothing was happening. */
.sidebar-fold {
  position: absolute; z-index: 40; top: 16px; left: calc(var(--sidebar-w) - 12px);
  width: 24px; height: 24px; border-radius: 50%; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--fg-muted);
  box-shadow: var(--shadow-sm); cursor: pointer;
}
.sidebar-fold .icon { transform: rotate(90deg); }
.sidebar-fold:hover { color: var(--fg); border-color: var(--fg-faint); }

.sidebar-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 12px 8px 24px; }
.sidebar-scroll::-webkit-scrollbar-thumb { background: var(--sidebar-thumb); background-clip: content-box; }

.nav-item {
  display: flex;
  align-items: center;
  gap: 11px;
  width: 100%;
  height: 32px;
  padding: 0 10px;
  white-space: nowrap;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--sidebar-fg);
  text-align: left;
  font-size: 13.5px;
}
.nav-item:hover { background: var(--sidebar-hover); color: #fff; }

/*
 * Избранное переставляют мышью: строка едет между соседями по-настоящему,
 * поэтому полоски вставки здесь нет — её место занимает сама строка.
 */
.nav-favs { display: flex; flex-direction: column; }
.nav-favs .nav-item.dragging { opacity: .45; }
.nav-item.active { background: var(--sidebar-active); color: #fff; font-weight: 500; }
.nav-item .icon { flex: 0 0 16px; opacity: .8; }
.nav-item .count {
  margin-left: auto;
  font-size: 11px;
  color: var(--sidebar-fg-dim);
  background: #313336;
  padding: 1px 6px;
  border-radius: 9px;
}

.nav-group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  position: relative;
  height: 36px;
  padding: 12px 10px 0;
  white-space: nowrap;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--sidebar-fg-dim);
  font-weight: 600;
}
.nav-group button {
  border: 0; background: transparent; color: var(--sidebar-fg-dim);
  padding: 2px 5px; border-radius: 4px; line-height: 1; font-size: 15px; height: 20px;
}
.nav-group button:hover { background: var(--sidebar-hover); color: #fff; }

.project-dot {
  width: 9px; height: 9px; border-radius: 3px;
  background: var(--project-color, #888); flex: 0 0 9px;
}

/* A project's square with its icon (ui.js, projectMark): sidebar, search, pickers, inbox. */
.project-mark {
  --mark: 16px;
  width: var(--mark); height: var(--mark); flex: 0 0 var(--mark); border-radius: 4px;
  background: var(--project-color, #888); color: #fff;
  display: inline-flex; align-items: center; justify-content: center; overflow: hidden;
}
.project-mark.s18 { --mark: 18px; border-radius: 5px; }
/* A picture of one's own fills the square in place of the colour and the icon. */
.project-mark.pic, .project-icon.pic { background: transparent; }
.project-pic { width: 100%; height: 100%; object-fit: contain; display: block; }


/* ========================================================== верх ======= */

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

.topbar-me {
  display: inline-flex; align-items: center; gap: 4px; padding: 3px 6px 3px 3px; border: 0; border-radius: 999px;
  background: transparent; color: var(--fg-faint); cursor: pointer;
}
.topbar-me:hover { background: var(--bg-muted); color: var(--fg-muted); }
.main { position: relative; }
/* Wide screen: no bar, only the avatar in the page's top right corner. */
.topbar {
  position: absolute; top: 12px; right: 18px; z-index: 8;
  display: flex; align-items: center; gap: 8px;
}
.topbar-brand { display: none; }

.search {
  position: relative;
  flex: 1;
  max-width: 460px;
}
.search input {
  width: 100%;
  padding: 7px 11px 7px 34px;
  border: 1px solid transparent;
  background: var(--bg-muted);
  border-radius: 20px;
  outline: none;
}
.search input:focus { background: var(--surface); border-color: var(--border-strong); box-shadow: 0 0 0 3px var(--focus-soft); }
.search > .icon { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--fg-faint); }

.avatar {
  width: 28px; height: 28px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 600; color: #fff;
  flex: 0 0 auto; user-select: none;
  position: relative; overflow: hidden;
}
/* Картинка накрывает инициалы, когда загрузится: до этого виден цветной круг. */
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.avatar.has-image { color: transparent; }
.avatar.lg { width: 34px; height: 34px; font-size: 13px; }
.avatar.sm { width: 22px; height: 22px; font-size: 9.5px; }
.avatar.none { background: var(--surface); border: 1.5px dashed var(--border-strong); color: var(--fg-faint); }

/* ====================================================== содержимое ===== */

.view { flex: 1; overflow-y: auto; background: var(--surface); }

.page-head { padding: 22px 32px 0; }
.page-title { display: flex; align-items: center; gap: 12px; }
.page-title h1 { margin: 0; font-size: 22px; font-weight: 600; letter-spacing: -.3px; }
.page-title .project-dot { width: 14px; height: 14px; border-radius: 4px; }
.page-desc { color: var(--fg-muted); margin: 7px 0 0; max-width: 760px; font-size: 13px; }

.tabs { display: flex; gap: 4px; margin-top: 18px; border-bottom: 1px solid var(--border); }
.tab {
  padding: 8px 14px; border: 0; background: transparent; color: var(--fg-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: 13.5px;
}
.tab:hover { color: var(--fg); }
.tab.active { color: var(--fg); border-bottom-color: var(--accent); font-weight: 500; }
/*
 * A tab's text keeps the width of its heavier, selected face: an invisible
 * heavy copy under it sets the width, so selecting a tab moves nothing.
 * Aligned to the top of its line: at the baseline the hidden copy made the
 * line, and so a segmented button that is not a flex box, 3px taller.
 */
.tab-label { display: inline-block; text-align: center; vertical-align: top; }
.tab-label::after {
  content: attr(data-label); display: block; height: 0; overflow: hidden; visibility: hidden;
  font-weight: var(--tab-on-weight, 500); user-select: none; pointer-events: none;
}
/* These draw the chosen one at 600, so the reserved copy must be as heavy. */
.k-tab, .status-editor button { --tab-on-weight: 600; }
/* Shortcut groups are a left-aligned column: centring would stagger their starts. */
.k-tab .tab-label { text-align: left; }

.toolbar {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 32px; border-bottom: 1px solid var(--border);
  /* Панель всегда в одну строку. Появление кнопки (например «Сохранить
     вид») не должно переносить соседей на второй ряд: высота панели
     задаёт сдвиг закреплённого заголовка таблицы, и вся вёрстка ниже
     прыгала бы туда-обратно. Не помещается — прокручивается вбок. */
  flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.toolbar::-webkit-scrollbar { height: 0; }
.toolbar > * { flex: 0 0 auto; }
.toolbar .tool-btn, .toolbar .btn { white-space: nowrap; }
/* Поиск по своим задачам: справа в панели, как лупа в Asana. */
.my-search {
  width: 210px; padding: 6px 10px; font-size: 13px;
  border: 1px solid var(--border-strong); border-radius: 7px;
  background: var(--surface); color: var(--fg); outline: none;
}
.my-search:focus { border-color: var(--focus); }
/*
 * Верхний блок проекта закреплён: шапка с названием и вкладками, за ней
 * панель фильтров, за ней заголовок таблицы. В проекте на сотни задач
 * возвращаться за ними наверх — лишняя работа, и в Asana они тоже стоят
 * на месте. Сдвиги считаются от настоящих высот: их измеряет отрисовка и
 * кладёт в --head-h и --bar-h, потому что высота шапки зависит от того,
 * что в неё попало (статус, участники, длинное название в две строки).
 */
.view > .project-head { position: sticky; top: 0; z-index: 6; background: var(--surface); }
.view:has(> .project-head) .toolbar {
  position: sticky; top: var(--head-h, 0px); z-index: 5; background: var(--surface);
}
.toolbar .spacer { flex: 1; }
.toolbar select {
  padding: 6px 9px; border: 1px solid var(--border-strong);
  border-radius: var(--radius); background: var(--surface); outline: none;
}

/* ------------------------------------------------------- список задач -- */

.section { border-bottom: 1px solid var(--border); }
.section-head {
  display: flex; align-items: center; gap: 9px;
  padding: 13px 32px 9px; cursor: pointer; user-select: none;
}
.section-head .caret { color: var(--fg-faint); transition: transform .15s; }
.section.collapsed .caret { transform: rotate(-90deg); }
.section.collapsed .section-body { display: none; }
.section-head h3 { margin: 0; font-size: 14px; font-weight: 600; }
.section-head .n { color: var(--fg-faint); font-size: 12.5px; }

.task-row {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 32px; border-top: 1px solid #f1f2f3;
  cursor: pointer;
}
.task-row:hover { background: var(--bg-muted); }
.task-row.selected { background: var(--accent-soft); }
.task-row .title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-row .title { color: var(--title-open); font-weight: 450; }
.task-row.done .title { color: var(--title-done); font-weight: 400; }

/* Кружок выполнения. Галочка внутри есть всегда — просто бледная, пока
   задача не сделана: пустой кружок ничего не говорит о том, что с ним
   делать, а бледная галочка читается как «отметить». Так же в Asana. */
.check {
  width: 18px; height: 18px; flex: 0 0 18px;
  border: 1.5px solid var(--border-strong); border-radius: 50%;
  background: var(--surface); display: inline-flex; align-items: center; justify-content: center;
  color: var(--fg-faint); padding: 0;
}
.check:hover { border-color: var(--green); color: var(--green); }
.check.on { background: var(--green); border-color: var(--green); color: #fff; }
/* A blocked task: an hourglass with no circle, as in Asana. The amber circle
   read as one more open task at a glance; the lone hourglass reads as
   "not yet". */
.check.blocked { border-color: transparent; background: transparent; color: var(--fg-muted); border-radius: 0; }
.check.blocked:hover { border-color: transparent; color: var(--green); }
/* Согласование: квадрат вместо кружка — тип задачи виден раньше, чем цвет,
   и не путается с отметкой выполнения. Цвет говорит о решении: серое ждёт,
   жёлтое просит изменений, красное отказано, зелёное одобрено. */
.check.appr { border-radius: 5px; color: var(--fg-faint); }
/* Правила решений идут после общего :hover у кружка — иначе наведение
   перекрашивало бы принятое решение в зелёный «отметить выполненной». */
.check.appr.pending { border-color: var(--border-strong); color: var(--fg-faint); }
.check.appr.changes { border-color: var(--amber); background: var(--amber-soft); color: var(--amber-fg); }
.check.appr.rejected { border-color: var(--red); background: var(--red-soft); color: var(--red-fg); }
.check.appr.approved { background: var(--green); border-color: var(--green); color: #fff; }
.check.appr.pending:hover { border-color: var(--fg-muted); color: var(--fg-muted); }

/* A milestone: a diamond in place of the circle, as in Asana, violet as
   the milestone's mark used to be after the name. Drawn by a rotated
   square behind the tick, so the button itself is not turned and the
   pop when it is done still works. Hovered and done, green like a circle. */
.check.milestone { position: relative; border-color: transparent; background: transparent; border-radius: 0; }
.check.milestone::before {
  content: ''; position: absolute; inset: 2.5px; border: 1.5px solid var(--violet); border-radius: 2px;
  background: var(--surface); transform: rotate(45deg);
}
.check.milestone > svg { position: relative; }
.check.milestone:hover { border-color: transparent; }
.check.milestone:hover::before { border-color: var(--green); }
.check.milestone.on { background: transparent; border-color: transparent; color: #fff; }
.check.milestone.on::before { background: var(--green); border-color: var(--green); }

/* Дубль: вместо кружка — стрелка к первоисточнику. Рамки нет намеренно,
   это не отметка выполнения, а ссылка; в Asana она выглядит так же. */
.check.dup {
  border-color: transparent; background: transparent;
  color: var(--fg-faint); border-radius: 5px;
}
.check.dup:hover { color: var(--accent); background: var(--accent-soft); }
/* Название дубля приглушено, как у выполненной, но без зачёркивания:
   работа не сделана, её просто делают в другой задаче. */
.tv-row .task-title.dup, .task-title.dup, .card-title .dup { color: var(--fg-faint); }

.meta { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

.pill {
  font-size: 11.5px; padding: 2px 8px; border-radius: 11px;
  background: var(--bg-muted); color: var(--fg-muted); white-space: nowrap;
  border: 1px solid var(--border);
}
/* Срок сегодня или завтра: за это берутся сейчас. */
.pill.due-soon { background: var(--green-soft); color: var(--green-fg); border-color: var(--green-line); }
/* Tokens, as its neighbour above: the colours as numbers stayed light in the dark theme. */
.pill.overdue { background: var(--red-soft); color: var(--red-fg); border-color: var(--red-line); }
.pill.project { background: var(--surface); }
.pill.project::before {
  content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 2px;
  background: var(--project-color, #888); margin-right: 6px; vertical-align: -1px;
}

.add-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 32px; border-top: 1px solid #f1f2f3;
  color: var(--fg-faint);
}
.add-row input {
  flex: 1; border: 0; outline: none; background: transparent; padding: 2px 0;
}
.add-row:hover { background: var(--bg-muted); }

.empty {
  padding: 56px 32px; text-align: center; color: var(--fg-muted);
}
.empty h3 { margin: 12px 0 6px; font-size: 16px; color: var(--fg); }
.empty p { margin: 0; font-size: 13px; }

/* ------------------------------------------------------------- доска --- */

.board { display: flex; gap: 14px; padding: 18px 32px 32px; align-items: flex-start; overflow-x: auto; min-height: 100%; }
.column {
  flex: 0 0 292px; background: var(--bg-muted);
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  display: flex; flex-direction: column; max-height: calc(100vh - 210px);
}
.column.dragover { border-color: var(--accent); background: var(--accent-soft); }
.column-head {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 13px; border-bottom: 1px solid var(--border);
}
.column-head h3 { margin: 0; font-size: 13.5px; font-weight: 600; }
/* Стрелка колонки: повёрнута, когда колонка свёрнута, — как каретка задачи. */
.column-head .col-caret { display: inline-flex; color: var(--fg-faint); }
.column-head .col-caret .icon { transition: transform .15s; }
.column.folded .col-caret .icon { transform: rotate(-90deg); }
/* Колонку переставляют, потянув за заголовок. */
/* Место под новый раздел: колонка-призрак в хвосте доски. */
.column-new { background: transparent; border: 1px dashed var(--border); align-self: flex-start; }
/* As a column's own "+ Задача": a bare browser button stood out grey and
   framed in the ghost column (audit 4, area 7, Н5). */
.column-new .add-section {
  width: 100%; border: 0; background: transparent; color: var(--fg-muted);
  padding: 10px 12px; border-radius: var(--radius); text-align: left; font-size: 13px; cursor: pointer;
}
.column-new .add-section:hover { color: var(--accent); background: var(--bg-muted); }
.column-head[draggable="true"] { cursor: grab; }
.column-head[draggable="true"]:active { cursor: grabbing; }
.column.col-dragging { opacity: .6; }
.column-head .n { color: var(--fg-faint); font-size: 12px; }
.column-body { padding: 9px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; flex: 1; }
/* End of the cards built so far (board.js): reaching it builds the next ones. */
.column-more { flex: none; height: 1px; margin-top: -8px; }

.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 10px 11px; box-shadow: var(--shadow-sm); cursor: pointer;
}
.card:hover { border-color: var(--border-strong); }
.card.dragging { opacity: .4; }
.card .card-title { display: flex; gap: 9px; align-items: flex-start; }
.card .card-title span { flex: 1; line-height: 1.35; color: var(--title-open); font-weight: 450; }
.card.done .card-title span { color: var(--title-done); font-weight: 400; }

/*
 * Поле имени на карточке доски. Оно подменяет текст под курсором, поэтому
 * обязано занимать ровно то же место: тот же шрифт, та же высота строки,
 * никаких полей и рамок — иначе карточки дёргались бы, стоило провести
 * по ним мышью.
 */
.card .card-title .card-title-edit { line-height: 1.35; }
/* A draggable card takes no text selection, and the field inherits that:
   without this its title could not be selected to copy it. */
.card .card-title-text, .card .card-title-edit { user-select: text; -webkit-user-select: text; }
/* Правку видно по самой карточке: она подсвечивается, как поле ввода. */
.card:has(.card-title-edit:focus) {
  border-color: var(--focus); box-shadow: 0 0 0 2px var(--focus-soft);
}
.card-meta { display: flex; align-items: center; gap: 7px; margin-top: 9px; min-width: 0; }
.card-meta .spacer { flex: 1; }
/* A long project name ends in an ellipsis inside the card: it went past the card's edge (Н5). */
.card-meta .pill.project { min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.column-add {
  padding: 8px 9px; border-top: 1px solid var(--border);
}
.column-add input {
  width: 100%; border: 1px solid transparent; background: transparent;
  padding: 5px 6px; border-radius: var(--radius); outline: none;
}
.column-add input:focus { background: var(--surface); border-color: var(--border-strong); }
/* Поле, открытое Enter в имени карточки: оно стоит между карточками. */
.column-add.after { padding: 0; border-top: 0; }
.column-add.after input { background: var(--surface); border-color: var(--accent); }

/* Поле, открытое кнопкой «+» в шапке: оно над карточками, а не под ними. */
.column-add.top { border-top: 0; border-bottom: 1px solid var(--border); padding-bottom: 0; }
.column-add.top:empty { display: none; }
.column-add.top input { background: var(--surface); border-color: var(--accent); }
/* «+» в шапке колонки: задача встаёт в раздел первой. */
.column-head .col-add { margin-right: auto; }

/* ------------------------------------------------------- сводка/дом --- */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(178px, 1fr)); gap: 12px; padding: 20px 32px 4px; }
.tile {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 15px 17px; background: var(--surface);
}
.tile .v { font-size: 26px; font-weight: 600; letter-spacing: -.5px; }
.tile .l { color: var(--fg-muted); font-size: 12.5px; margin-top: 3px; }
.tile.warn .v { color: var(--red); }
.tile.ok .v { color: var(--green); }

.cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 13px; padding: 16px 32px 32px; }
.project-card {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 16px; background: var(--surface); cursor: pointer; text-align: left;
}
.project-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-strong); }
.project-card h3 { margin: 10px 0 5px; font-size: 15px; font-weight: 600; }
.project-card p { margin: 0; color: var(--fg-muted); font-size: 12.5px; line-height: 1.5; min-height: 38px; }
.project-card .bar { height: 4px; border-radius: 2px; background: var(--bg-muted); margin-top: 13px; overflow: hidden; }
.project-card .bar i { display: block; height: 100%; background: var(--project-color, #888); }
.project-card .stat { color: var(--fg-faint); font-size: 11.5px; margin-top: 7px; }
.project-card .swatch { width: 26px; height: 26px; border-radius: 7px; background: var(--project-color, #888); }

/* ------------------------------------------------------------ панель --- */

/* Затемнение с перехватом нажатий остаётся только на узком экране, где
   карточка занимает почти всё. На широком открытая карточка не должна
   запирать список: его продолжают листать, чтобы открыть следующую задачу
   или свериться с соседними. Поэтому список просто ужимается, освобождая
   карточке место, а подложка ничего не ловит. */
.drawer-backdrop {
  position: fixed; inset: 0; background: rgba(30, 31, 33, .18);
  opacity: 0; pointer-events: none; transition: opacity .16s;
}

.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: var(--drawer-w); max-width: var(--drawer-max);
  background: var(--surface); border-left: 1px solid var(--border);
  transform: translateX(100%);
  /* Hidden once slid out (and inert, drawer.js): a closed card took Tab and
     a screen reader's attention, and its shadow showed as a strip along the
     window's right edge (audit 4, area 7, С1). */
  visibility: hidden;
  transition: transform .18s ease-out, visibility 0s linear .18s;
  display: flex; flex-direction: column; z-index: 10;
}
.drawer.open {
  transform: translateX(0); visibility: visible; box-shadow: var(--shadow-lg);
  transition: transform .18s ease-out, visibility 0s;
}

/*
 * Ширину держим в переменной: её же занимает место, освобождённое списком.
 * По умолчанию — доля окна, как в Asana: на широком экране карточке достаётся
 * больше, но список остаётся рабочим, а не полоской в четверть. Край карточки
 * можно тащить, и выбранная ширина запоминается.
 */
/*
 * Two variables: --drawer-own is the width the card would have, the default
 * share of the window or the one dragged (drawer.js, applyWidth), and
 * --drawer-w the width it is given. They differ on the inbox, which narrows
 * the card on a narrow window (the inbox's block below); a screen deriving
 * the width from --drawer-w itself would be a cycle.
 */
:root { --drawer-own: clamp(400px, 44vw, 860px); --drawer-w: var(--drawer-own); }
/* Whole pixels, as a dragged width is (drawer.js, applyWidth): with 44vw's
   fraction the inbox list's width (it is computed from this) came out a
   sub-pixel apart with and without the card, and chips in the rows shifted
   by 0.1px as a thread opened. Inside @supports: an unknown function in a
   custom property would leave the card with no width at all. */
@supports (width: round(down, 10.5px, 1px)) {
  :root { --drawer-own: round(down, clamp(400px, 44vw, 860px), 1px); }
}
body.drawer-open .view { margin-right: var(--drawer-w); }
body.drawer-open.drawer-wide { --drawer-w: var(--drawer-wide-w); }

/*
 * How wide the card may be. From 901px, where the card and the list share
 * the window, the card stops at the sidebar's edge: the wide card
 * (1100px) knew nothing of the sidebar and slid under it on windows under
 * ~1372px, and the fold button, painted over the card, sat on «Отметить
 * выполненной» (iteration 18, item 11). --side-now is the sidebar as it
 * stands, folded to the rail or not. All three live on body, beside the
 * class that folds the sidebar: a var() is resolved where the property is
 * declared, so declared on :root they would never see the fold. Up to
 * 900px the card is laid over the dimmed page and may cover the sidebar,
 * so the fold button goes while it is open (the block below).
 */
body { --side-now: var(--sidebar-w); --drawer-max: 94vw; --drawer-wide-w: min(1100px, 96vw); }
@media (min-width: 861px) { body.sidebar-folded { --side-now: var(--rail-w); } }
@media (min-width: 901px) {
  body {
    --drawer-max: min(94vw, 100vw - var(--side-now));
    --drawer-wide-w: min(1100px, 96vw, 100vw - var(--side-now));
  }
}

/*
 * The strip that drags the card's width. It is a sibling of the card, not
 * inside it: the card is drawn anew whole. It lies over the card's own
 * first 7px (its border and the padding of the head, body and foot, where
 * nothing is clicked), not just outside: the list's scrollbar ends exactly
 * at the card's edge, and the strip outside covered 7 of its 10px, leaving
 * one pixel of the thumb to grab (iteration 17, item 4). The strip follows
 * the width the card is actually given: --drawer-max is the card's own
 * max-width, and the wide card has a width of its own and no max-width, so
 * the strip stays on the card's edge on a window too narrow for either.
 */
.drawer-grip {
  position: fixed; top: 0; bottom: 0; right: calc(min(var(--drawer-w), var(--drawer-max)) - 7px); width: 7px;
  cursor: col-resize; background: transparent; z-index: 11; display: none;
}
body.drawer-wide .drawer-grip { right: calc(var(--drawer-wide-w) - 7px); }
body.drawer-open .drawer-grip { display: block; }
/* A line on the card's edge, where the strip now lies, rather than a fill over the card's text margin. */
.drawer-grip:hover, body.drawer-sizing .drawer-grip { box-shadow: inset 2px 0 0 var(--accent); }
body.drawer-sizing { user-select: none; cursor: col-resize; }
body.drawer-sizing .drawer { transition: none; }

@media (max-width: 900px) {
  /* Места на двоих нет: карточка накрывает список, подложка снова ловит
     нажатия — иначе мимо карточки попадают в то, чего не видят. */
  body.drawer-open .view { margin-right: 0; }
  .drawer-backdrop.open { opacity: 1; pointer-events: auto; }
  body.drawer-open .drawer-grip { display: none; }
  /* The card may cover the sidebar here (see --drawer-max), and the fold
     button, above everything, would sit on the card's head. */
  body.drawer-open .sidebar-fold { display: none; }
}

.drawer-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; border-bottom: 1px solid var(--border);
}
.drawer-head .spacer { flex: 1; }
/*
 * The spacing around the title was sized for the notice and the "done" pill
 * that once stood under it; both are gone, and the gaps only pushed the
 * card into a scrollbar. The title keeps its 6px padding: that is the frame
 * shown on hover and focus. Being a textarea it is inline-block by default,
 * and the line it sat on added a strut gap of 3px below it.
 */
/*
 * The scrollbar's place is kept whether the content fits or not: one that
 * came and went took 10px of the content's width with it, and every line
 * of the card wrapped anew (iteration 17, item 7).
 */
.drawer-body { flex: 1; overflow-y: auto; padding: 12px 22px 32px; scrollbar-gutter: stable; }

.drawer-title {
  display: block;
  width: 100%; border: 1px solid transparent; border-radius: var(--radius);
  font-size: 25px; font-weight: 500; padding: 6px 8px; margin: 0 0 8px -8px;
  outline: none; resize: none; overflow: hidden; line-height: 1.3;
  /* Своего фона и цвета у поля быть не должно: браузер подставляет системные,
     и в тёмной теме заголовок выглядит полем ввода. */
  font-family: inherit; background: transparent; color: inherit;
}
.drawer-title:hover { border-color: var(--border); }
.drawer-title:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }

.props { display: grid; grid-template-columns: 116px 1fr; gap: 11px 14px; align-items: center; }
.props .k { color: var(--fg-muted); font-size: 12.5px; }
.props .v select, .props .v input {
  width: 100%; padding: 6px 9px; border: 1px solid var(--border);
  border-radius: var(--radius); background: var(--surface); outline: none;
}
.props .v select:focus, .props .v input:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }

.drawer-section { margin-top: 24px; }
.drawer-section > h4 {
  margin: 0 0 10px; font-size: 13.5px; font-weight: 600;
  letter-spacing: 0; color: var(--fg-dim);
}
/*
 * A section heading with the section's own actions at its right (Files):
 * the heading keeps the look of a bare one, and the row is no taller than
 * the 24px of its quiet buttons, so it costs the space of a heading. On a
 * phone the buttons do not fit beside it: they go under it as a whole, not
 * word by word.
 */
.drawer-section-head { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; min-height: 24px; margin: 0 0 6px; }
.drawer-section-head > h4 { margin: 0; font-size: 13.5px; font-weight: 600; letter-spacing: 0; color: var(--fg-dim); white-space: nowrap; }
.drawer-section-head .section-actions { margin-left: auto; display: flex; gap: 2px; }
.drawer-section-head .section-actions .btn { padding: 3px 7px; font-size: 12.5px; gap: 5px; white-space: nowrap; }
/* The "…" of the Subtasks heading: quiet, and blue while it hides the done ones. */
.drawer-section-head .section-actions .sub-more { padding: 3px 5px; color: var(--fg-faint); }
.drawer-section-head .section-actions .sub-more:hover { color: var(--fg); }
.drawer-section-head .section-actions .sub-more.on { color: var(--accent); }
.drawer-section textarea {
  width: 100%; min-height: 96px; padding: 9px 11px; border: 1px solid var(--border);
  border-radius: var(--radius); outline: none; resize: vertical; line-height: 1.55;
  font-family: inherit; background: transparent;
}
.drawer-section textarea:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }
/* Описание: рамка появляется только при наведении и вводе. */
.drawer-section textarea.plain { border-color: transparent; min-height: 72px; }
.drawer-section textarea.plain:hover { border-color: var(--border); }
.drawer-section textarea.plain:focus { border-color: var(--focus); background: var(--surface); }

.comment { display: flex; gap: 10px; padding: 11px 0; border-top: 1px solid var(--border); }
.comment .c-body { flex: 1; min-width: 0; }
.comment .c-head { display: flex; gap: 8px; align-items: baseline; margin-bottom: 3px; }
.comment .c-head b { font-size: 13px; font-weight: 600; }
.comment .c-head time { font-size: 11.5px; color: var(--fg-faint); }
.comment p { margin: 0; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }

.comment-form { display: flex; gap: 10px; margin-top: 14px; align-items: flex-start; }
.comment-form textarea { flex: 1; min-height: 64px; }

.saving { font-size: 11.5px; color: var(--fg-faint); }

/* ------------------------------------------------------------ модалка -- */

.modal-backdrop {
  position: fixed; inset: 0; background: rgba(30, 31, 33, .35);
  display: flex; align-items: center; justify-content: center; z-index: 20; padding: 24px;
}
/*
 * Рама диалога — та же, что у окна сочетаний клавиш: заголовок сверху,
 * кнопки снизу, между ними прокручивается только содержимое.
 *
 * Заголовок и кнопки прилипают к краям, а не уезжают вместе с текстом:
 * в длинном диалоге (конструктор формы, шаблон, правило) кнопку
 * «Сохранить» приходилось искать прокруткой, а шапка уплывала, и было
 * не видно, что вообще открыто. Липкость вместо трёх коробок с прокруткой
 * внутри — чтобы разметка всех двадцати диалогов осталась прежней.
 */
.modal {
  background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-md);
  width: 100%; max-width: 440px; padding: 0;
  max-height: calc(100vh - 48px); overflow-y: auto;
}
/* Боковые поля — у содержимого, а не у самой коробки: иначе липкие шапка
   и подвал не дотягиваются до краёв и из-под них видно текст. */
.modal > * { padding-left: 22px; padding-right: 22px; }
.modal > h2 {
  position: sticky; top: 0; z-index: 2;
  margin: 0; padding-top: 16px; padding-bottom: 14px; font-size: 17px;
  background: var(--surface); border-bottom: 1px solid var(--border);
  display: flex; align-items: center; gap: 12px;
}
.modal > h2 > span:first-child { flex: 1; min-width: 0; }
/* Подзаголовок: о чём это окно. Стоит под заголовком и не прилипает —
   это пояснение, а не управление. */
.modal > .modal-note { margin: 14px 0 0; font-size: 12.5px; color: var(--fg-muted); }
.modal > .modal-note + * { margin-top: 12px; }
.modal > *:not(h2):not(.modal-actions):first-of-type { margin-top: 16px; }
.modal-close {
  border: 0; background: transparent; color: var(--fg-muted); flex: none;
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
}
.modal-close:hover { background: var(--bg-muted); color: var(--fg); }
.modal-actions {
  position: sticky; bottom: 0; z-index: 2;
  display: flex; gap: 9px; align-items: center; justify-content: flex-end;
  margin-top: 20px; padding-top: 14px; padding-bottom: 16px;
  background: var(--surface); border-top: 1px solid var(--border);
}
/* Пояснение или ошибка — слева от кнопок, мелким серым. */
.modal-actions .modal-hint { flex: 1; min-width: 0; font-size: 12px; color: var(--fg-faint); text-align: left; }
/* On a phone the hint goes above the buttons: beside them it was squeezed
   into a column of six or seven lines (the new rule; audit 4, area 7, Н4). */
@media (max-width: 520px) {
  .modal-actions { flex-wrap: wrap; }
  .modal-actions .modal-hint { flex: 1 0 100%; }
}

.color-picker { display: flex; gap: 8px; flex-wrap: wrap; }
.color-picker button {
  width: 26px; height: 26px; border-radius: 7px; border: 2px solid transparent;
  background: var(--project-color);
}
.color-picker button.on { border-color: var(--fg); }
/* A project's colour: one square, the palette of squares over it. */
.project-color-btn { width: 26px; height: 26px; border-radius: 7px; border: 0; background: var(--project-color); cursor: pointer; }
.color-palette.squares button { width: 24px; height: 24px; border-radius: 6px; border: 2px solid transparent; background: var(--project-color); cursor: pointer; padding: 0; }
.color-palette.squares button:hover { transform: scale(1.1); }
.color-palette.squares button.on { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--project-color); }

/* ------------------------------------------------------------ таблицы -- */

/* Таблицы шире экрана прокручиваются внутри блока, а не рвут страницу. */
.table-wrap { padding: 8px 32px 32px; overflow-x: auto; }
.cell-actions { display: inline-flex; gap: 6px; align-items: center; justify-content: flex-end; }
.btn.icon-only { padding: 5px 7px; line-height: 0; }
table.grid { width: 100%; border-collapse: collapse; font-size: 13px; }
table.grid th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--fg-muted); padding: 9px 11px; border-bottom: 1px solid var(--border); font-weight: 600;
}
table.grid td { padding: 9px 11px; border-bottom: 1px solid #f1f2f3; vertical-align: middle; }
table.grid tr:hover td { background: var(--bg-muted); }
table.grid code { font-size: 11.5px; color: var(--fg-muted); }
/* Длинная почта и подробности события не должны растягивать таблицу:
   обрезаем по многоточию, целиком они видны во всплывающей подсказке. */
table.grid td.ellipsis { max-width: 240px; }
table.grid td.ellipsis > * { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.grid td.wide-ellipsis { max-width: 420px; }
table.grid td.wide-ellipsis > * { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Столбец действий остаётся у правого края даже при прокрутке таблицы:
   иначе на узком экране кнопки уезжают и до них не добраться. */
table.grid th.actions, table.grid td.actions {
  position: sticky; right: 0; text-align: right; white-space: nowrap;
  background: var(--surface); box-shadow: -10px 0 10px -10px rgba(0, 0, 0, .25);
}
table.grid th.actions { background: var(--bg); }
table.grid tr:hover td.actions { background: var(--bg-muted); }

.tag {
  font-size: 11px; padding: 1px 7px; border-radius: 4px;
  background: var(--bg-muted); border: 1px solid var(--border); color: var(--fg-muted);
}
.tag.admin { background: var(--blue-soft); color: var(--blue); border-color: var(--blue-line); }
.tag.fail { background: var(--red-soft); color: var(--red-fg); border-color: var(--red-line); }
.tag.ok { background: var(--green-soft); color: var(--green-fg); border-color: var(--green-line); }

/* ==========================================================================
   Hubbs: компоненты экранов, близких к привычным трекерам
   ========================================================================== */

/* ------------------------------------------------------ шапка проекта --- */

.project-head {
  padding: 12px 32px 0;
  border-bottom: 1px solid var(--border);
}
/* The right end is the avatar's, which stands over the page's corner. */
.project-head .row { display: flex; align-items: center; gap: 12px; padding-right: 56px; min-height: 40px; }
/*
 * One axis for the whole line: the middle of the title's capital letters,
 * which is also the middle of the project's square. Every part is centred
 * by its box, and each box is made symmetric about what it draws: the h1
 * is trimmed to its capitals, the star and the status lose the text strut.
 * Centred by untrimmed boxes, the star rode 4 px above the letters (the
 * font's descent sat under it); on the baseline, the star stayed 1-2 px
 * high while the status pill hung 2-3 px low. Both were measured in pixels
 * with Segoe UI metrics, which is what the users' Windows draws.
 */
.project-title-line { display: flex; align-items: center; gap: 12px; min-width: 0; }
.project-icon {
  width: 34px; height: 34px; border-radius: 9px; flex: 0 0 34px;
  background: var(--project-color, #888); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
}
.project-head h1 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -.3px; }
/* Without text-box the h1 keeps its line box: the parts still share one
   axis, only the title's letters may sit up to 2 px off it in Segoe. */
@supports (text-box: trim-both cap alphabetic) {
  .project-head h1 { text-box: trim-both cap alphabetic; padding-block: 6px; }
}
.project-head .spacer { flex: 1; }

.status-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; padding: 4px 10px; border-radius: 14px;
  border: 1px solid var(--border); background: var(--surface); color: var(--fg-muted);
}
.status-pill i { width: 8px; height: 8px; border-radius: 50%; background: currentColor; }
.status-pill.on_track { color: var(--green-fg); background: var(--green-soft); border-color: var(--green-line); }
.status-pill.at_risk  { color: var(--amber-fg); background: var(--amber-soft); border-color: var(--amber-line); }
.status-pill.off_track{ color: var(--red-fg); background: var(--red-soft); border-color: var(--red-line); }
.status-pill.on_hold  { color: var(--blue); background: var(--blue-soft); border-color: var(--blue-line); }
.status-pill.complete { color: var(--violet); background: color-mix(in srgb, var(--violet) 12%, transparent); border-color: color-mix(in srgb, var(--violet) 30%, transparent); }
/* Лента обновлений статуса проекта. */
.status-feed { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.status-update { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; background: var(--surface); }
.status-update .head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12.5px; color: var(--fg-muted); }
.status-update .head b { color: var(--fg); font-weight: 600; }
.status-update .head .del { margin-left: auto; border: 0; background: none; color: var(--fg-faint); cursor: pointer; padding: 2px 6px; }
.status-update .head .del:hover { color: var(--red); }
.status-update h4 { margin: 8px 0 4px; font-size: 14px; font-weight: 600; }
.status-update .body { font-size: 13.5px; }
.status-title { width: 100%; margin-top: 10px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 6px; outline: none; font: inherit; }

/* "+ ▾", the tabs, and the open view's tools at the far end. */
.tabs-row { display: flex; align-items: center; gap: 2px; margin-top: 8px; }
.tabs-row > .split-btn { margin-right: 10px; flex: 0 0 auto; }
.tabs-row .add-main { padding: 5px 9px; }
/* Narrowed by an open card, the tabs scroll rather than run under the tools. */
.tabs-list { display: flex; gap: 2px; align-self: stretch; align-items: flex-end; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.tabs-list::-webkit-scrollbar { display: none; }
.tabs-list .tab { flex: 0 0 auto; white-space: nowrap; }
.project-head .status-set, .project-head .status-set .pill { white-space: nowrap; flex: 0 0 auto; }
.view-tools { margin-left: auto; display: flex; align-items: center; gap: 4px; padding-left: 12px; flex: 0 0 auto; }
/*
 * The tools on a line of their own under the tabs (fittedTabs): the open
 * tab and «Ещё» did not fit beside them, and the tabs list was squeezed to
 * nothing. The list takes the whole first line, from a zero basis so that
 * it does not wrap away from the add button.
 */
.tabs-row.tabs-wrapped { flex-wrap: wrap; row-gap: 2px; }
.tabs-row.tabs-wrapped > .tabs-list { flex: 1 1 0; }
.tabs-row.tabs-wrapped > .view-tools { flex: 1 0 100%; justify-content: flex-end; padding: 2px 0 4px; }
/* What stays in sight on a phone, under the tabs row (setViewTools in views/project.js). */
.view-notes { display: none; }
.view-tools .saving { margin-right: 6px; white-space: nowrap; }
/* A big project's load that stopped part way: what came, and a way on (loadRest in views/project.js). */
.view-tools .load-stalled { display: inline-flex; align-items: baseline; gap: 6px; color: var(--fg-muted); }
.view-tools .load-stalled .text-btn { font-size: inherit; }
.view-tools .tool-btn { white-space: nowrap; }
.view-tools .phone-only { display: none; }
.tabs-row .tab {
  padding: 9px 12px; border: 0; background: transparent; color: var(--fg-muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: 13.5px;
  display: inline-flex; align-items: center; gap: 6px; border-radius: 6px 6px 0 0;
}
.tabs-row .tab:hover { color: var(--fg); background: var(--bg-muted); }
.tabs-row .tab.active { color: var(--fg); border-bottom-color: var(--fg); font-weight: 500; background: transparent; }
/* A tab of the project's own: a long name is cut, its menu on the caret of the open one. */
.tabs-row .tab-own > .tab-label { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tabs-row .tab-caret { display: inline-flex; color: var(--fg-muted); padding: 2px; border-radius: 4px; }
.tabs-row .tab-own:not(.active) .tab-caret { visibility: hidden; }
.tabs-row .tab-caret:hover { background: var(--bg-muted); color: var(--fg); }
.tabs-row .tab-add { padding: 9px 8px; color: var(--fg-faint); }

/* ------------------------------------------------------------- метки --- */

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11.5px; line-height: 1; padding: 4px 8px; border-radius: 11px;
  background: var(--chip-bg, #f1f0ef); color: var(--chip-fg, #55595f);
  white-space: nowrap; border: 0;
}
.chip.removable { padding-right: 5px; }
/* ----------------------------------------------------- личные настройки */
.settings-head { padding: 26px 32px 0; max-width: 860px; }
.settings-back { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; color: var(--fg-muted); text-decoration: none; margin-bottom: 10px; }
.settings-back .icon { transform: rotate(90deg); }
.settings-back:hover { color: var(--fg); }
.settings-head h1 { margin: 0 0 6px; font-size: 24px; font-weight: 600; letter-spacing: -.3px; }
.settings-lead { margin: 0; color: var(--fg-muted); font-size: 14px; line-height: 1.5; max-width: 640px; }
.settings-body { padding: 18px 32px 40px; max-width: 860px; display: flex; flex-direction: column; gap: 14px; }
.settings-top { display: flex; flex-direction: column; gap: 10px; }
.settings-summary { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--fg-muted); }
.settings-summary .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fg-faint); }
.settings-summary .dot.on { background: var(--green); }
.presets { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 8px; }
.preset {
  text-align: left; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); cursor: pointer; display: flex; flex-direction: column; gap: 3px; color: var(--fg); font: inherit;
}
.preset:hover { border-color: var(--border-strong); }
.preset.active { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.preset-title { font-weight: 600; font-size: 13.5px; }
.preset-hint { font-size: 12px; color: var(--fg-muted); line-height: 1.4; }
.settings-card { border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); padding: 16px 18px; }
.settings-card-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 8px; }
.settings-card-head h2, .token-list h2 { margin: 0; font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.settings-card-head h2 .n, .token-list h2 .n { color: var(--fg-faint); font-weight: 400; font-size: 12.5px; }
.settings-card-head p { margin: 3px 0 0; font-size: 12.5px; color: var(--fg-muted); }
.settings-card-head .text-btn { white-space: nowrap; font-size: 12.5px; }
.pref-list { display: flex; flex-direction: column; }
.pref-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 10px 0; border-top: 1px solid var(--border-soft); }
.pref-row:first-child { border-top: 0; }
.pref-text { min-width: 0; }
.pref-title { font-size: 13.5px; font-weight: 500; }
.pref-hint { font-size: 12.5px; color: var(--fg-muted); line-height: 1.45; margin-top: 1px; }
.settings-note { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-radius: var(--radius); font-size: 13px; }
.settings-note.warn { background: var(--amber-soft); color: var(--amber-fg); border: 1px solid var(--amber-line); }
.settings-foot { margin: 0; font-size: 12.5px; color: var(--fg-faint); }
.settings-empty { font-size: 13px; color: var(--fg-muted); padding: 8px 0; }

/* Тумблер: настоящий флажок, нарисованный дорожкой с шайбой. */
.switch { position: relative; display: inline-flex; flex-shrink: 0; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track { width: 38px; height: 22px; border-radius: 11px; background: var(--border-strong); transition: background .15s; position: relative; }
.switch-knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25); transition: transform .15s; }
.switch input:checked + .switch-track { background: var(--accent); }
.switch input:checked + .switch-track .switch-knob { transform: translateX(16px); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--focus); outline-offset: 2px; }

/* Токены API */
.token-create { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin: 6px 0 4px; }
.token-create .token-name { flex: 1 1 260px; min-width: 200px; }
.token-fresh { margin-top: 12px; padding: 12px 14px; border: 1px solid var(--accent); border-radius: var(--radius-lg); background: var(--accent-soft); }
.token-fresh-title { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 13.5px; margin-bottom: 8px; }
.token-value { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.token-value code, .token-try code, .api-code { font-size: 12.5px; word-break: break-all; background: var(--bg); padding: 5px 8px; border-radius: 6px; border: 1px solid var(--border); display: inline-block; }
.token-try { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.token-list { display: flex; flex-direction: column; gap: 4px; }
.token-list h2 { margin-bottom: 4px; }
.token-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-top: 1px solid var(--border-soft); }
.token-icon { width: 32px; height: 32px; border-radius: 8px; background: var(--bg-muted); display: grid; place-items: center; color: var(--fg-muted); flex-shrink: 0; }
.token-meta { flex: 1; min-width: 0; }
.token-title { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.token-title code { font-size: 12px; color: var(--fg-faint); }
.api-steps { display: flex; flex-direction: column; gap: 10px; margin: 6px 0 14px; }
.api-step { display: flex; gap: 12px; align-items: flex-start; }
.api-step-n { width: 24px; height: 24px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12.5px; font-weight: 600; display: grid; place-items: center; flex-shrink: 0; margin-top: 1px; }
.api-step .api-code { margin-top: 4px; }
.api-links { display: flex; gap: 8px; flex-wrap: wrap; }
.api-links .btn { text-decoration: none; }

/* Карточки-входы в настройки на профиле */
.settings-links { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 10px; }
.settings-link { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface); color: var(--fg); text-decoration: none; }
.settings-link:hover { border-color: var(--border-strong); background: var(--bg-muted); }
.settings-link-mark { width: 36px; height: 36px; border-radius: 10px; background: var(--accent-soft); color: var(--accent); display: grid; place-items: center; flex-shrink: 0; }
.settings-link-title { font-weight: 600; font-size: 13.5px; }
.settings-link-text { font-size: 12.5px; color: var(--fg-muted); margin-top: 1px; }
.settings-link-arrow { margin-left: auto; color: var(--fg-faint); transform: rotate(-90deg); }
@media (max-width: 720px) {
  .settings-head, .settings-body { padding-left: 16px; padding-right: 16px; }
  .settings-card-head { flex-direction: column; }
}
.mail-stats { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
/* Administration in sections: a heading with its buttons, then the section's own table. */
.tabs-row a.tab { text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.admin-section-head { padding-top: 20px; }
.admin-section-title { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 8px; }
.admin-section-title h2 { margin: 0; font-size: 16px; font-weight: 600; }
.admin-section-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.admin-filter { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding: 12px 32px 4px; }
.admin-search { width: min(320px, 100%); padding: 6px 10px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 13px; }
.admin-body { padding: 10px 32px 12px; }
/* A row the search left out; the stacked table on a phone sets its own display. */
.grid tr[hidden] { display: none !important; }
.team-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; padding: 12px 32px 20px; }
.team-card {
  display: flex; flex-direction: column; gap: 8px; padding: 14px 16px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
}
.team-card:hover { border-color: var(--border-strong); }
.team-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.team-card-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.team-card-name { font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.team-card-desc { color: var(--fg-muted); font-size: 13px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.team-card-desc.none { color: var(--fg-faint); }
.team-card-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: auto; }
.team-modal { width: min(520px, calc(100vw - 32px)); }
.team-modal-save { display: flex; justify-content: flex-end; }
.team-modal-members .spacer { flex: 1; }
.team-modal-members { display: flex; align-items: center; gap: 8px; margin-top: 14px; }
.team-modal .admin-search { width: 100%; margin-top: 10px; }
.admin-empty { padding: 6px 32px 12px; }
@media (max-width: 860px) {
  .admin-filter, .admin-body, .admin-empty, .team-cards { padding-left: 16px; padding-right: 16px; }
  .admin-search { width: 100%; }
}
.tag.warn { background: var(--amber-soft); color: var(--amber-fg); }
.chip.add { cursor: pointer; color: var(--fg-faint); border-style: dashed; }
.chip.add:hover { color: var(--fg); border-style: solid; }
.chip .x {
  border: 0; background: transparent; color: inherit; opacity: .55;
  padding: 0 2px; line-height: 1; font-size: 13px; cursor: pointer;
}
.chip .x:hover { opacity: 1; }

/*
 * Заливки плашек. Прежние были почти белыми: колонка «Статус» читалась как
 * столбец серых слов, а в Asana она видна с одного взгляда — красное горит,
 * зелёное готово. Насыщенность подняли до её уровня, тексты затемнили,
 * чтобы контраст остался читаемым.
 */
.t-indigo { --chip-bg: #d9ddf7; --chip-fg: #2f379a; }
.t-teal   { --chip-bg: #c3e8e4; --chip-fg: #0a5754; }
.t-amber  { --chip-bg: #f9e2ac; --chip-fg: #6f4c08; }
.t-rose   { --chip-bg: #f8cdc9; --chip-fg: #8c241f; }
.t-violet { --chip-bg: #e4d6f6; --chip-fg: #57318c; }
.t-lime   { --chip-bg: #cfe7b7; --chip-fg: #3a5c1b; }
.t-sky    { --chip-bg: #cee0f5; --chip-fg: #1b4c7d; }
.t-slate  { --chip-bg: #dee2e7; --chip-fg: #3c454e; }
/* The seven that complete Asana's fifteen: more shades of yellow, green, blue-green and purple-pink. */
.t-gold { --chip-bg: #fbd9a0; --chip-fg: #7a4a00; }
.t-yellow { --chip-bg: #f5eca0; --chip-fg: #665800; }
.t-olive { --chip-bg: #e1eca8; --chip-fg: #4c5a0c; }
.t-emerald { --chip-bg: #bfe8d3; --chip-fg: #0d5a3a; }
.t-magenta { --chip-bg: #f0cdef; --chip-fg: #7d1f7a; }
.t-fuchsia { --chip-bg: #fbc9df; --chip-fg: #8f1450; }
.t-pink { --chip-bg: #fbe0e6; --chip-fg: #8a3a4d; }

/* The dot of a colour in menus and palettes: one clear hue per colour in both themes. The chip's
   own text colour, dark in the light theme, made the shades of one colour look alike. */
.t-rose { --chip-dot: #e8615a; }
.t-amber { --chip-dot: #f0913a; }
.t-gold { --chip-dot: #f5b400; }
.t-yellow { --chip-dot: #e3cf2f; }
.t-olive { --chip-dot: #a8c43c; }
.t-lime { --chip-dot: #62b85a; }
.t-emerald { --chip-dot: #2fb487; }
.t-teal { --chip-dot: #36b8b0; }
.t-sky { --chip-dot: #4a9be8; }
.t-indigo { --chip-dot: #6b72e8; }
.t-violet { --chip-dot: #9c6ee0; }
.t-magenta { --chip-dot: #c95fc9; }
.t-fuchsia { --chip-dot: #e8559b; }
.t-pink { --chip-dot: #f29db4; }
.t-slate { --chip-dot: #8d97a3; }

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

.avatar-stack { display: inline-flex; align-items: center; }
.avatar-stack .avatar { margin-left: -6px; border: 2px solid var(--surface); }
.avatar-stack .avatar:first-child { margin-left: 0; }
.avatar-stack .more {
  margin-left: -6px; border: 2px solid var(--surface); background: var(--bg-muted);
  color: var(--fg-muted); font-size: 10px;
}

/* --------------------------------------------------------- «нравится» --- */

/*
 * The thumb takes the colour of the other buttons in the card's head: in the
 * fainter grey it was the one paler glyph in the row and looked blurred next
 * to them (the owner, iteration 16). The inbox's like went the same way in 13.
 */
.like-btn {
  display: inline-flex; align-items: center; gap: 5px;
  border: 0; background: transparent; color: var(--fg-muted);
  padding: 3px 6px; border-radius: 6px; font-size: 12px;
}
.like-btn:hover { background: var(--bg-muted); color: var(--fg); }
.like-btn.on { color: var(--accent); }
.like-btn.on svg { fill: currentColor; }

/* ---------------------------------------------------------- подзадачи --- */

.subtask-row {
  display: flex; align-items: center; gap: 10px;
  padding: 7px 0; border-top: 1px solid var(--border);
}
.subtask-row:first-child { border-top: 0; }
/* Ручка переноса у подзадач: как у строк доски — видна при наведении,
   место под неё держится всегда, иначе строки прыгают под мышью. */
.subtask-row .grip, .subtask-sec .grip {
  width: 14px; flex: 0 0 14px; margin-left: -5px;
  color: transparent; cursor: grab;
  display: flex; align-items: center; justify-content: center;
}
.subtask-row:hover .grip, .subtask-sec:hover .grip { color: var(--fg-faint); }
.subtask-row .grip:hover, .subtask-sec .grip:hover { color: var(--fg-muted); }
.subtask-row.dragging, .subtask-sec.dragging {
  opacity: .75; background: var(--accent-soft);
  outline: 1px solid var(--accent); outline-offset: -1px;
}
.subtask-row .title { flex: 1; min-width: 0; font-size: 13.5px; }
/*
 * Имя подзадачи и раздела правят на месте: под курсором текст подменяется
 * полем. Держатель занимает то же место, что занимал текст, — иначе строка
 * дёргалась бы, стоило провести по ней мышью.
 */
.subtask-row .title-host, .subtask-sec .title-host { flex: 1; min-width: 0; display: block; }
/* Имя с пометками: тянется имя, значки идут сразу за ним. */
.subtask-row .subtask-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: 5px; }
.subtask-row .subtask-name .title-host { flex: 0 1 auto; }
/*
 * Внутри карточки у textarea свои отступы, рамка и высота в сотню пикселей —
 * они для описания и комментариев. Полю правки имени всё это ни к чему: оно
 * подменяет строку и обязано занимать ровно её место, иначе список подзадач
 * раздувает огромная рамка с уголком растягивания.
 */
.drawer-section .title-edit {
  width: 100%; min-height: 0; padding: 0; border: 0; border-radius: 0;
  background: transparent; resize: none; font: inherit; line-height: inherit;
}
.drawer-section .title-edit:focus { border-color: transparent; box-shadow: none; }
/* The field over a subtask's name has the name's own size: it grew the row by a line's fraction. */
.subtask-row .title-edit { font-size: 13.5px; }
.subtask-row .title-host .title, .subtask-sec .title-host .title { display: block; }
/*
 * A subtask's name is one line cut with an ellipsis, as a row of the
 * project's list: a long one took three lines of the card. The field that
 * takes its place under the pointer is one line of the same height, so the
 * row does not grow when the pointer passes; it keeps the ellipsis until
 * it is clicked into.
 */
.subtask-row .title-host .title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.subtask-row .sub-title-edit { height: 20px; text-overflow: ellipsis; }
/* The field is as wide as the whole name (inline-title.js) and stays inside its host, as the text did. */
.sub-title-edit { max-width: 100%; }
/*
 * The cursor names the zone as in the list: over the row the plain arrow
 * (a click picks the subtask, the arrow button opens it), over the name the
 * text cursor, over the grip "grab".
 */
.subtask-row { cursor: default; }
.subtask-row.picked { background: var(--pick-soft); box-shadow: inset 2px 0 0 var(--pick-line); }
.subtask-row.picked:hover { background: var(--pick-hover); }
.subtask-row .title { cursor: text; }
.subtask-row .grip:active, .subtask-sec .grip:active { cursor: grabbing; }
/* Заголовок карточки на доске тоже про правку имени, а не про открытие. */
.card .card-title span { cursor: text; }
/* Открыть подзадачу теперь можно кнопкой: имя занято правкой. */
/*
 * Стрелка «открыть подзадачу» — значок, а не кнопка: без рамки и фона, пока
 * на неё не навели. Класс кнопок строки ей не годился — он про выпадающее
 * меню и оставлял серую кнопку браузера прямо посреди строки.
 */
.subtask-row .sub-open {
  opacity: 0; flex: 0 0 auto; border: 0; background: transparent;
  color: var(--fg-faint); padding: 3px; border-radius: 6px;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.subtask-row:hover .sub-open { opacity: 1; }
.subtask-row .sub-open:hover { background: var(--bg-muted); color: var(--fg); }
.subtask-row .sub-open:focus-visible { opacity: 1; }
/*
 * The right part of a subtask row: status, due date, assignee, in the list
 * row's order. Each has a fixed width, or the chips stand in a staircase and
 * do not read as columns; with no column heads that alignment is all that
 * says which value is which.
 */
.subtask-status { flex: 0 0 96px; width: 96px; min-width: 0; }
.subtask-status .cell-btn { width: 100%; justify-content: flex-start; padding: 3px 6px; }
.subtask-due { flex: 0 0 84px; width: 84px; min-width: 0; font-size: 11px; color: var(--fg-faint); }
.subtask-due .cell-btn { width: 100%; padding: 3px 6px; min-height: 24px; }
/* Значок календаря у пустого срока — единственное, что там есть. */
.subtask-due .cell-btn .label:empty { display: none; }
.subtask-who { flex: 0 0 34px; width: 34px; display: flex; justify-content: flex-end; }
/*
 * The assignee here is only a picture, but the button is live: it is how
 * the assignee is changed. A fixed square, so its hover and open rings are
 * circles: 30px wide with a min-height, it came out 30x26 empty and 30x28
 * with a face, and the rings were ellipses (iteration 18, item 10). 28px
 * holds the 22px face and the 16px person icon with room for the border.
 */
.subtask-who .cell-btn {
  width: 28px; height: 28px; min-height: 0; padding: 0; flex: none;
  border-radius: 50%; justify-content: center;
}
.subtask-who .cell-btn .label { display: none; }
.subtask-row.done .title { color: var(--title-done); font-weight: 400; }
/*
 * A narrow card keeps room for the name. Cut to one line, the name gave its
 * place up first: on a phone (a 345 px row) and in a card dragged down to
 * 400 px, the status (96 px), the due date (84 px) and the assignee took
 * all of it, and every row showed no name at all (iteration 16 review).
 * The status goes first, as the list drops its columns on a phone, then the
 * number; the name never shrinks under a few words.
 */
.subtask-list { container: subtasks / inline-size; }
.subtask-row .subtask-name { min-width: 72px; }
@container subtasks (max-width: 500px) {
  .subtask-row { gap: 8px; }
  .subtask-row .subtask-status { display: none; }
}
@container subtasks (max-width: 380px) {
  .subtask-row .tv-num { display: none; }
}
.subtask-add {
  display: flex; align-items: center; gap: 10px; padding: 7px 0;
  border-top: 1px solid var(--border); color: var(--fg-faint);
}
.subtask-add input { flex: 1; border: 0; outline: none; background: transparent; font-size: 13.5px; }
/* With no subtasks the field is the list's first line: no rule over it, as over a first row. */
.subtask-add:first-child { border-top: 0; }
/* The field Enter opens under a subtask stands level with the rows around it. */
.subtask-add.inline { border-top: 0; padding-left: 22px; }
/* Раздел в списке подзадач: заголовок группы, а не строка работы — без
   кружка и номера, шрифтом заголовка, как разделы доски. */
.subtask-sec {
  display: flex; align-items: center; gap: 8px;
  border-top: 1px solid var(--border);
  /* The section band of the list here too. It reaches 8px past the rows on
     both sides, so the title keeps its line with the rows' names; the
     height stays the 15px of padding it had. */
  background: var(--section-fill); margin-inline: -8px; padding: 8px 8px 7px;
}
.subtask-sec:first-child { border-top: 0; }
.subtask-sec .title {
  flex: 1; min-width: 0; font-size: 13px; font-weight: 600;
  color: var(--fg-muted); cursor: text;
}
.subtask-sec .title { cursor: text; }
.subtask-sec .row-actions { opacity: 0; }
.subtask-sec:hover .row-actions { opacity: 1; }
/*
 * A section's name stands as wide as its text, so the «+» comes right after
 * it, as by a section of the project's list (iteration 17, item 14); the
 * «…» keeps the row's end. Both are plain icons until pointed at, as the
 * list's: the «…» showed as a grey browser button, and on a narrow window
 * the list's rule for .row-actions hid it, with the section's only actions.
 * Without hover (a phone) both stay in sight.
 */
.subtask-sec .title-host { flex: 0 1 auto; }
.subtask-sec .sec-add, .subtask-sec .sec-more {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; flex: none;
  border: 0; border-radius: 6px; background: transparent; cursor: pointer;
  color: var(--fg-faint); opacity: 0;
}
.subtask-sec .sec-more { margin-left: auto; }
.subtask-sec:hover .sec-add, .subtask-sec:focus-within .sec-add,
.subtask-sec:hover .sec-more, .subtask-sec:focus-within .sec-more { opacity: 1; color: var(--fg-muted); }
.subtask-sec .sec-add:hover, .subtask-sec .sec-more:hover { background: var(--border-soft); color: var(--fg); }
@media (hover: none) {
  .subtask-sec .sec-add, .subtask-sec .sec-more { opacity: 1; }
}
/*
 * The other picked subtasks of a pack being dragged: they stay in the page
 * (Chromium ends a drag whose source has left it), dimmed, and join the
 * held row where it is let go (iteration 17, item 22).
 */
.subtask-row.drag-hidden { opacity: .35; }
/*
 * No scroll anchoring while a subtask is in hand: going up, the held row
 * is put above the first row in sight, the browser's anchor, and the
 * browser scrolled the card back by a row each time (iteration 17, item 13).
 */
.drawer.sub-drag > .drawer-body { overflow-anchor: none; }

.progress-mini {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--fg-faint);
}
.progress-mini .bar { width: 44px; height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.progress-mini .bar i { display: block; height: 100%; background: var(--green); }

/* ------------------------------------------------------------- обзор --- */

.overview { padding: 22px 32px 40px; max-width: 900px; }
.overview h2 { font-size: 15px; margin: 26px 0 10px; font-weight: 600; }
.overview h2:first-child { margin-top: 0; }
.overview .card {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: 16px 18px; background: var(--surface);
}
.overview .desc { color: var(--fg-muted); line-height: 1.6; }
.editable-rich { cursor: text; border-radius: 6px; margin: -4px -6px; padding: 4px 6px; }
.editable-rich:hover { background: var(--bg-muted); }
.editable-rich-actions { display: flex; gap: 8px; margin-top: 8px; }
.overview .members { display: flex; flex-wrap: wrap; gap: 10px; }
.overview .member {
  display: flex; align-items: center; gap: 9px; padding: 8px 12px;
  border: 1px solid var(--border); border-radius: 22px;
}
.overview .member .n { font-size: 13px; }
.overview .member .r { font-size: 11px; color: var(--fg-faint); }

.status-editor { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.status-editor button { cursor: pointer; opacity: .75; font: inherit; font-size: 12.5px; }
.status-editor button:hover { opacity: 1; }
.status-editor button.on { opacity: 1; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px currentColor; font-weight: 600; }

/* -------------------------------------------------------- лента срока --- */

.timeline-wrap { padding: 16px 32px 40px; overflow-x: auto; }
.timeline {
  min-width: 720px; border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden; background: var(--surface);
}
.tl-head { display: flex; border-bottom: 1px solid var(--border); background: var(--bg-muted); }
.tl-head .label { flex: 0 0 240px; padding: 9px 14px; font-size: 12px; color: var(--fg-muted); font-weight: 600; }
.tl-head .days { flex: 1; display: flex; }
.tl-head .day {
  flex: 1; text-align: center; font-size: 10.5px; color: var(--fg-faint);
  padding: 9px 0; border-left: 1px solid var(--border);
}
.tl-head .day.today { color: var(--accent); font-weight: 700; }
.tl-head .day b { display: block; font-size: 11px; color: var(--fg-muted); font-weight: 600; }

.tl-section { padding: 8px 14px; font-size: 12px; font-weight: 600; color: var(--fg-muted); background: var(--bg-muted); border-bottom: 1px solid var(--border); }
.tl-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--border-soft); }
.tl-row:hover { background: var(--bg-muted); }
.tl-row .label {
  flex: 0 0 240px; padding: 8px 14px; font-size: 13px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer;
}
.tl-row.done .label { color: var(--fg-faint); }
.tl-row .track { flex: 1; position: relative; display: flex; }
/* Day grid as the track's background, one line per --day: no element per
   day (see timelineRow). */
.tl-row .track.daily {
  background: repeating-linear-gradient(90deg, var(--border-soft) 0 1px, transparent 1px var(--day, 44px));
}
.tl-row .track .today-col { position: absolute; top: 0; bottom: 0; background: var(--accent-soft); pointer-events: none; }
.tl-bar {
  position: absolute; top: 7px; height: 20px; border-radius: 10px;
  background: var(--project-color, #5b5fc7); color: #fff;
  font-size: 11px; line-height: 20px; padding: 0 9px; cursor: pointer;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  box-shadow: var(--shadow-sm);
}
.tl-bar.done { opacity: .45; }
.tl-bar.overdue { background: var(--red); }

/* ---------------------------------------------------------- календарь --- */

.calendar-wrap { padding: 16px 32px 40px; }
/* Wraps on a narrow screen. In one line on a phone the month/week switch
   was squeezed to 2 px, "Сегодня" went past the edge and the whole screen
   could be dragged sideways (audit 4, area 7, Н1). */
.cal-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin-bottom: 12px; }
.cal-head h3 { margin: 0; font-size: 15px; font-weight: 600; min-width: 190px; }
.cal-head .seg, .cal-head .btn { flex: none; }
@media (max-width: 520px) {
  .cal-head h3 { min-width: 0; flex: 1 1 auto; }
}
/* A table wider than a phone scrolls by itself, not the whole screen (a goal's projects). */
.wide-scroll { overflow-x: auto; max-width: 100%; }
.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; }
.cal-dow {
  padding: 8px; text-align: center; font-size: 11.5px; color: var(--fg-muted);
  background: var(--bg-muted); border-bottom: 1px solid var(--border); font-weight: 600;
}
.cal-cell {
  min-height: 108px; min-width: 0; border-left: 1px solid var(--border); border-top: 1px solid var(--border);
  padding: 6px; background: var(--surface);
}
.cal-cell:nth-child(7n + 1) { border-left: 0; }
.cal-cell.other { background: var(--bg-muted); }
.cal-cell .num { font-size: 12px; color: var(--fg-muted); margin-bottom: 5px; }
.cal-cell.today .num {
  background: var(--accent); color: #fff; border-radius: 50%;
  width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;
  font-weight: 600;
}
.cal-task {
  display: block; width: 100%; text-align: left; border: 0; border-radius: 5px;
  padding: 3px 6px; margin-bottom: 3px; font-size: 11.5px; cursor: pointer;
  background: var(--chip-bg, #eceefb); color: var(--chip-fg, #414bb2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-task.done { opacity: .6; }

/* -------------------------------------------------------------- панель --- */

.dash { padding: 18px 32px 40px; display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.dash .panel {
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  background: var(--surface); padding: 16px 18px;
}
.dash .panel h3 { margin: 0 0 14px; font-size: 13.5px; font-weight: 600; }
.dash .panel.wide { grid-column: 1 / -1; }
.dash-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 12px; }

.bar-list { display: flex; flex-direction: column; gap: 11px; }
.bar-list .item { display: grid; grid-template-columns: minmax(0, 160px) minmax(0, 1fr) 42px; gap: 10px; align-items: center; font-size: 12.5px; }
.bar-list .item .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-muted); }
.bar-list .item .track { height: 8px; border-radius: 4px; background: var(--bg-muted); overflow: hidden; }
.bar-list .item .track i { display: block; height: 100%; border-radius: 4px; background: var(--series, #5b5fc7); }
.bar-list .item .val { text-align: right; color: var(--fg-muted); font-variant-numeric: tabular-nums; }

.donut-row { display: flex; align-items: center; gap: 20px; }
.donut-legend { display: flex; flex-direction: column; gap: 8px; font-size: 12.5px; }
.donut-legend .row { display: flex; align-items: center; gap: 8px; }
.donut-legend .sw { width: 10px; height: 10px; border-radius: 3px; background: var(--series); }
.donut-legend .val { color: var(--fg-muted); margin-left: auto; font-variant-numeric: tabular-nums; }

/* ---------------------------------------------- панель задачи (правая) --- */

/* Ширину задаёт переменная (см. --drawer-w): её же занимает место списка. */
.drawer { width: var(--drawer-w); }
/*
 * One left edge for the whole value column of the card: every kind of value
 * (a .cell-btn, the project's square, "+ Проект", a dependency, the fields
 * table) used to bring its own inset, and the first marks stood 2 to 11px
 * apart. --props-k is the label column, --value-inset the gap from the
 * value column's edge to a value's first mark.
 */
.drawer { --props-k: 148px; --value-inset: 9px; }
.drawer-head .complete-btn {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--border-strong); background: var(--surface); border-radius: 6px;
  padding: 6px 12px; font-size: 13px; color: var(--fg-muted);
}
.drawer-head .complete-btn:hover { background: var(--bg-muted); }
.drawer-head .complete-btn.on { background: var(--green-soft); border-color: var(--green-line); color: var(--green-fg); font-weight: 500; }
/* У согласования на кнопке читается решение, поэтому цвет — по решению,
   а не «выполнена»: отказ зелёной плашкой сбивал бы с толку. */
.drawer-head .complete-btn.approval.changes {
  background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber-fg);
}
.drawer-head .complete-btn.approval.rejected {
  background: var(--red-soft); border-color: var(--red-line); color: var(--red-fg);
}
.drawer-head .complete-btn.approval.pending { color: var(--amber-fg); border-color: var(--amber-line); }

/*
 * Свойства задачи — две колонки, как в Asana: слева подпись, справа
 * значение. Колонка подписи шире и спокойнее по цвету, строки с ровным
 * шагом: раньше подпись была вровень со значением по размеру, и глазу не
 * за что было зацепиться.
 */
/* A label stands at the top of its value: next to a value of several lines (projects, dependencies) it was centred among them. */
.props-grid { display: grid; grid-template-columns: var(--props-k) 1fr; gap: 0 12px; align-items: start; }
.props-grid .k {
  color: var(--fg-muted); font-size: 13px; min-height: 38px;
  display: flex; align-items: center; gap: 7px;
}
.props-grid .k .icon { color: var(--fg-faint); flex: none; }
.props-grid .v { min-width: 0; min-height: 38px; display: flex; align-items: center; }
.props-grid .v > * { min-width: 0; }
/* В карточке поле живёт крупнее, чем в таблице, но нажимается по значению,
   а не по всей колонке — см. .fields-rows ниже. */
.props-grid .cell-btn.wide { font-size: 13.5px; min-height: 30px; padding: 4px calc(var(--value-inset) - 1px); }
.props-grid > .v > .cell-btn { width: auto; max-width: 100%; }
.props-grid .cell-btn .cell-tags { flex-wrap: wrap; }
/*
 * A value that is a list (projects, dependencies) starts at the top, and
 * its first row is 32px tall: a .proj-row is the 28px .cell-btn of its
 * section plus 2px above and below, a .dep the 30px row plus its 1px
 * margins. Centred in the 38px of a one-line value, the label stood 3.5px
 * below that first row's text; in a 32px band of its own it shares its
 * middle and its baseline. Whoever changes those heights re-checks this.
 */
.props-grid > .k.lines { min-height: 32px; }
/* A row inherited from the parent has no section button to give it that height. */
.props-grid .proj-row { min-height: 32px; }
.props-grid > .v.lines { align-items: flex-start; }
.props-grid .dep-list { display: flex; flex-direction: column; }
.props-grid .dep-list > .chip-add { align-self: flex-start; }
/* Only "+ Проект" or "+ Зависимость" left: it takes the same band. */
.props-grid :is(.proj-list, .dep-list) > .chip-add:only-child { min-height: 32px; }
/*
 * Поля проекта — своя таблица под его именем, как в Asana: видно, чьи это
 * поля и откуда они возьмутся в другой задаче. Строки разделены линиями —
 * иначе набор из десяти полей читается как сплошная простыня.
 */
.fields-table {
  margin-top: 14px; border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden; background: var(--surface);
}
.fields-head {
  display: flex; align-items: center; gap: 7px; padding: 9px 12px;
  border-bottom: 1px solid var(--border); background: var(--bg-soft); min-width: 0;
}
.fields-head .dot {
  width: 9px; height: 9px; border-radius: 3px; flex: none;
  background: var(--project-color, var(--fg-faint));
}
.fields-head .fields-project { font-weight: 600; font-size: 13.5px; color: var(--fg); }
.fields-head a.fields-project:hover { color: var(--accent); text-decoration: underline; }
.fields-head .sep { color: var(--fg-faint); }
.fields-head .fields-section {
  color: var(--fg-muted); font-size: 13px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
/*
 * Свёрнутый набор — одна строка: у задачи из пяти проектов пять таблиц
 * подряд отодвигали описание за край экрана. Нижней границы у него нет,
 * иначе пустая полоса читалась бы как «тут что-то должно быть».
 */
.fields-table.folded .fields-head { border-bottom: 0; }
.fields-fold {
  display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; flex: none; padding: 0;
  color: var(--fg-faint); border: 0; background: transparent;
  border-radius: var(--radius);
}
.fields-fold:hover { background: var(--bg-muted); color: var(--fg); }
.fields-fold.on svg { transform: rotate(90deg); }
/* Сколько полей внутри: без этого непонятно, стоит ли разворачивать. */
.fields-count {
  margin-left: auto; padding: 0 6px; border-radius: 999px;
  background: var(--bg-muted); color: var(--fg-faint);
  font-size: 11.5px; font-weight: 600;
}

/* Its values stand on the card's value edge too: the 11px is the 12px gap of
   .props-grid less the table's 1px border. Without it they started 11px left
   of the assignee and the date. */
.fields-rows { display: grid; grid-template-columns: calc(var(--props-k) + 11px) 1fr; align-items: stretch; }
.fields-rows > .k, .fields-rows > .v {
  min-height: 42px; display: flex; align-items: center;
  border-top: 1px solid var(--border-soft, var(--border));
}
.fields-rows > .k:first-child, .fields-rows > .k:first-child + .v { border-top: none; }
.fields-rows > .k {
  gap: 8px; padding: 6px 0 6px 12px; color: var(--fg-dim); font-size: 13px;
  align-items: flex-start; padding-top: 12px;
}
.fields-rows > .k .icon { color: var(--fg-faint); flex: none; }
.fields-rows > .v { padding: 4px 12px 4px 0; min-width: 0; }
.fields-rows > .v > * { min-width: 0; }
.fields-rows .cell-btn.wide { font-size: 13.5px; min-height: 30px; padding: 4px calc(var(--value-inset) - 1px); }
/* Нажимается само значение, а не вся строка. Кнопка во всю ширину ловила
   щелчки, которыми закрывают предыдущее — всплывашку, поле ввода, меню, — и
   вместо закрытия открывала редактор следующего поля. */
.fields-rows > .v > .cell-btn { width: auto; max-width: 100%; }
/* A subfield sits under its parent, a step in, with no line between them:
   it is a detail of that value, not a field of its own. */
.fields-rows > .k.sub { padding-left: 28px; border-top: none; font-size: 12.5px; }
.fields-rows > .v.sub { border-top: none; }
.fields-rows > .v.sub .cell-btn.empty .label { color: var(--fg-faint); font-style: italic; }

.props-grid .project-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
.props-grid .project-row .sep { color: var(--fg-faint); }
.props-grid .project-row .cell-btn { width: auto; }

.tag-row { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 4px 0; }

/* Поля проекта списком: вид, название, порядок — как панель полей в Asana. */
.field-list { display: flex; flex-direction: column; gap: 2px; }
.field-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 6px;
  border-radius: 7px; font-size: 13px; color: var(--fg);
}
.field-row:hover { background: var(--bg-muted); }
.field-row.dragging { opacity: .5; }
.field-row .grip { color: var(--fg-faint); cursor: grab; display: flex; }
.field-row .grip:active { cursor: grabbing; }
.field-row > .icon { color: var(--fg-faint); flex: none; }
/* The name wins the room: "Несколько из списка" and the row's buttons left a
   field called "Версия" a single letter. The type gives way first. */
.field-row .field-name { flex: 1; min-width: 64px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.field-row .field-type { color: var(--fg-faint); font-size: 11.5px; white-space: nowrap; flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.field-row.sub { padding-left: 30px; }
/* The name comes first; the "under which value" note is what gets cut short. */
.field-row.sub .field-name { flex: 0 1 auto; min-width: 56px; }
.field-row .field-when { flex: 1 1 0; min-width: 0; color: var(--fg-muted); font-size: 11.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.opt-sub { display: flex; flex-direction: column; gap: 2px; }
.opt-sub .field-types { margin-top: 6px; }
.opt-bounds { display: flex; gap: 6px; margin-top: 6px; }
/* A number's format: labelled rows like the subfield settings, the chips and inputs under their labels. */
.opt-format { display: flex; flex-direction: column; gap: 2px; }
.opt-format .opt-label { margin-bottom: 8px; }
.opt-format .field-types, .opt-format .opt-type, .opt-format .opt-label > .opt-new { margin-top: 6px; }
.opt-format .opt-label > .opt-new { display: block; width: 100%; box-sizing: border-box; }
.opt-example { display: block; margin-top: 4px; font-size: 13px; color: var(--fg); font-variant-numeric: tabular-nums; }
.opt-currency-pick { padding: 6px; display: flex; flex-direction: column; gap: 4px; }
.opt-currencies .menu-hint { margin-left: auto; color: var(--fg-faint); font-size: 11.5px; }
/* A row's remove button: a bare icon, as by files and dependencies. Without
   it the browser drew a grey button box around the cross and the lock. */
.f-del {
  border: 0; background: transparent; color: var(--fg-faint); padding: 3px;
  border-radius: 4px; display: inline-flex; align-items: center; justify-content: center;
}
.f-del:hover { background: var(--red-soft); color: var(--red); }
.field-row .f-del { opacity: 0; }
.field-row:hover .f-del { opacity: 1; }
.field-add-row { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
/* In place of the add buttons for a member, who may only read the list. */
.field-owner-note { margin: 8px 0 0; }
/* Добавление поля — внутри панели: всплывашка поверх неё закрыла бы её саму. */
.field-add {
  margin-top: 8px; padding: 10px; border: 1px solid var(--border);
  border-radius: 9px; background: var(--bg-soft); display: flex; flex-direction: column; gap: 8px;
}
.field-add-head { display: flex; align-items: center; justify-content: space-between; font-size: 13px; }
.field-add-head .f-del { opacity: 1; }
.field-types { display: flex; flex-wrap: wrap; gap: 4px; }
.field-types .chip { cursor: pointer; display: inline-flex; align-items: center; gap: 4px; }
.field-types .chip.on { outline: 2px solid var(--focus); outline-offset: 1px; }
.field-add .opt-new {
  width: 100%; border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px;
  font: inherit; font-size: 13px; background: var(--bg); color: var(--fg); outline: none;
}
.field-add .opt-new:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }
.field-add-actions { display: flex; gap: 6px; justify-content: flex-end; }
.field-lib { max-height: 220px; overflow-y: auto; display: flex; flex-direction: column; }
.field-lib .menu-item { width: 100%; }
.field-lib .menu-hint { color: var(--fg-faint); font-size: 11.5px; margin-left: auto; }
.tag-add, .chip-add {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px dashed var(--border-strong); background: transparent; color: var(--fg-faint);
  border-radius: 11px; padding: 3px 9px; font-size: 11.5px;
}
.tag-add:hover, .chip-add:hover { border-color: var(--accent); color: var(--accent); }
/*
 * In the card the same buttons have no dashed frame: in Asana "Add
 * dependencies" is a quiet label, and a frame across the row drew the eye to
 * the empty place instead of the fields. Its plus stands on the card's value
 * edge (--value-inset), under the avatar and the date's calendar.
 */
.props-grid .chip-add {
  border: none; padding: 3px 8px 3px var(--value-inset); font-size: 13px; color: var(--fg-muted); border-radius: 6px;
}
.props-grid .chip-add:hover { background: var(--bg-muted); color: var(--fg); }
/*
 * A line icon leading a value (the date's calendar, the empty assignee, the
 * plus of "+ Проект" and "+ Зависимость") has 2px of blank drawing on its
 * left. Its ink stood 2px right of the avatar, the project square and the
 * dependency pills, and the value column read as two edges against a ruler.
 * Moved by its blank, not laid out anew: the label after it stays put.
 */
.props-grid > .v :is(.cell-btn, .chip-add) > svg.icon:first-child { position: relative; left: -2px; }

/*
 * История задачи. Строка читается как строка ленты: аватар, кто и что
 * сделал, время — и уже под этим подробности изменения. Прежняя точка вместо
 * аватара не говорила ни кто это, ни куда нажать.
 */
.activity-item {
  display: flex; gap: 10px; padding: 9px 0; font-size: 12.5px; color: var(--fg-muted);
  border-top: 1px solid var(--border-soft);
}
.activity-item:first-child { border-top: 0; }
.activity-item .act-body { flex: 1; min-width: 0; }
.activity-item .act-head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.activity-item .act-verb { color: var(--fg-muted); }
/*
 * A rule's record and comment (iteration 18, item 2): a bolt in a circle
 * the size of a picture, where the author's picture stood, and the rule's
 * name where the author's name stood. The name opens nothing (the author
 * is only in its hover), so it has the plain pointer, not a link's.
 */
.rule-badge {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--bg-muted); color: var(--fg-muted);
}
:is(.activity-item, .c-head) .rule-name { font-weight: 600; color: var(--fg); cursor: default; }
.activity-item .act-place { color: var(--fg); }
.fv-tip.rule-tip { flex-direction: row; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg); max-width: 360px; }
.rule-tip-label { color: var(--fg-muted); white-space: nowrap; }
/* Added or taken off, in a series of changes shown as one entry (history-groups.js). */
.act-op { color: var(--fg-muted); font-weight: 600; }
.activity-item time { color: var(--fg-faint); margin-left: auto; white-space: nowrap; }
/* A person is a link to the profile (personLink in user-card.js): the base link colour and underline do not apply. */
.activity-item .who-link {
  border: 0; background: transparent; padding: 0; font: inherit; font-weight: 600;
  color: var(--fg); cursor: pointer; text-decoration: none;
}
.activity-item .who-link:hover { text-decoration: underline; }
.activity-item .pill.project { cursor: pointer; border-style: solid; }
/* The project is a link now (projectPill): one piece, as the button was, never broken across lines. */
.activity-item a.pill.project { display: inline-block; }
.activity-item .pill.project:hover { border-color: var(--accent); color: var(--accent); }

.seg { display: inline-flex; border: 1px solid var(--border); border-radius: 7px; overflow: hidden; }
.seg button { border: 0; background: var(--surface); padding: 5px 11px; font-size: 12.5px; color: var(--fg-muted); }
.seg button.on { background: var(--bg-muted); color: var(--fg); font-weight: 500; }

/* ---------------------------------------------------------- загрузка --- */

.wl-toolbar { flex-wrap: wrap; }
.wl-norm { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--fg-muted); }
/* The two unit notes in one cell, the other unit's hidden (workload.js): its width never changes. */
.wl-unit-note { display: inline-grid; align-items: center; }
.wl-unit-note > * { grid-area: 1 / 1; }
.wl-unit-note > .off { visibility: hidden; }
.wl-per-day { width: 52px; padding: 4px 6px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 12.5px; }
.wl-project { padding: 5px 8px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 12.5px; background: var(--surface); color: var(--fg); max-width: 220px; }
.wl-range { font-size: 12.5px; color: var(--fg-muted); min-width: 150px; text-align: center; }
.wl-scroll { overflow: auto; padding: 0 32px 40px; }
.wl { min-width: 100%; width: max-content; font-size: 12.5px; }
.wl-row {
  display: grid; grid-template-columns: 240px repeat(var(--wl-cols), minmax(46px, 1fr)) 72px;
  align-items: stretch; border-bottom: 1px solid var(--border);
}
.wl-head { position: sticky; top: 0; background: var(--bg-muted); z-index: 1; }
.wl-head .wl-person, .wl-head .wl-total { padding: 9px 12px; font-size: 12px; color: var(--fg-muted); font-weight: 600; align-self: center; }
.wl-col { text-align: center; padding: 7px 2px; border-left: 1px solid var(--border); display: flex; flex-direction: column; gap: 1px; }
.wl-col b { font-size: 11px; color: var(--fg-muted); font-weight: 600; text-transform: uppercase; }
.wl-col span { font-size: 11px; color: var(--fg-faint); }
.wl-col.today b, .wl-col.today span { color: var(--accent); }
.wl-col.weekend { background: var(--bg); }
.wl-person { display: flex; align-items: center; gap: 9px; padding: 8px 12px; min-width: 0; }
.wl-who { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wl-name { font-weight: 500; color: var(--fg); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wl-name:hover { text-decoration: underline; }
.wl-meta { display: flex; gap: 8px; align-items: center; font-size: 11.5px; }
.wl-capacity { border: 0; background: none; padding: 0; color: var(--fg-muted); font: inherit; font-size: 11.5px; cursor: pointer; border-bottom: 1px dashed var(--border-strong); }
.wl-capacity.plain { cursor: default; border-bottom-color: transparent; }
.wl-cell {
  border: 0; border-left: 1px solid var(--border); background: transparent; font: inherit; font-size: 12px;
  color: var(--fg-muted); min-height: 46px; cursor: default; padding: 0 2px;
}
.wl-cell:not(:disabled) { cursor: pointer; }
.wl-cell:not(:disabled):hover { outline: 2px solid var(--accent); outline-offset: -2px; }
.wl-cell.weekend { background: var(--bg); }
.wl-cell.today { box-shadow: inset 0 3px 0 var(--accent); }
.wl-cell.low  { background: #e3f2ea; color: #14613f; }
.wl-cell.mid  { background: #bfe3cf; color: #0f4d31; }
.wl-cell.high { background: var(--amber-soft); color: var(--amber-fg); font-weight: 600; }
.wl-cell.over { background: var(--red-soft); color: var(--red-fg); font-weight: 700; }
.wl-cell.guess { color: var(--fg-faint); font-style: italic; }
.wl-total { padding: 0 12px; display: flex; align-items: center; justify-content: flex-end; color: var(--fg-muted); border-left: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.wl-total.over { color: var(--red-fg); font-weight: 700; }
.wl-row.unassigned .wl-name { color: var(--fg-faint); font-style: italic; }
.wl-pop { padding: 8px 0 2px; }
.wl-pop-head { display: flex; justify-content: space-between; gap: 10px; padding: 2px 12px 8px; border-bottom: 1px solid var(--border); font-size: 12.5px; }
.wl-item { display: flex; align-items: center; gap: 10px; white-space: normal; }
.wl-item-main { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 3px; }
.wl-item-title { font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wl-item-sub { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.wl-share { color: var(--fg-faint); font-size: 11.5px; flex: 0 0 auto; }
:root[data-theme="dark"] .wl-cell.low  { background: #163527; color: #7fd4a6; }
:root[data-theme="dark"] .wl-cell.mid  { background: #1e4a35; color: #9fe2bd; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .wl-cell.low  { background: #163527; color: #7fd4a6; }
  :root:not([data-theme="light"]) .wl-cell.mid  { background: #1e4a35; color: #9fe2bd; }
}
@media (max-width: 720px) {
  .wl-scroll { padding: 0 16px 32px; }
  .wl-row { grid-template-columns: 160px repeat(var(--wl-cols), minmax(40px, 1fr)) 60px; }
}

/* ------------------------------------------------ портфели и цели --- */

.crumb-link { font-size: 12.5px; color: var(--fg-muted); text-decoration: none; }
.crumb-link:hover { color: var(--fg); text-decoration: underline; }
.pf-people { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 10px; font-size: 12.5px; }
.goal-follow { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }
.goal-follow .avatar-stack { vertical-align: middle; }
.pf-statuses { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--bg-muted); margin-top: 12px; }
.pf-statuses i { display: block; height: 100%; background: var(--border-strong); }
.pf-statuses i.on_track { background: var(--green); }
.pf-statuses i.at_risk { background: var(--amber); }
.pf-statuses i.off_track { background: var(--red); }
.pf-statuses i.on_hold { background: #6b8bc0; }
.pf-statuses i.complete { background: #7a6bd0; }
.pf-project { display: inline-flex; align-items: center; gap: 8px; color: var(--fg); text-decoration: none; font-weight: 500; }
.pf-project:hover { text-decoration: underline; }
.pf-progress, .goal-progress { display: inline-flex; align-items: center; gap: 8px; min-width: 120px; }
.pf-progress .bar, .goal-progress .bar { flex: 1; height: 6px; border-radius: 3px; background: var(--bg-muted); overflow: hidden; min-width: 70px; }
.pf-progress .bar i, .goal-progress .bar i { display: block; height: 100%; background: var(--green); }
.goal-progress .bar i.at_risk { background: var(--amber); }
.goal-progress .bar i.off_track, .goal-progress .bar i.missed { background: var(--red); }
.goal-progress .bar i.dropped { background: var(--border-strong); }
.goal-progress.wide { width: 100%; }
.goal-progress.wide .bar { height: 10px; border-radius: 5px; }
.pf-progress .pct, .goal-progress .pct { font-variant-numeric: tabular-nums; font-size: 12px; color: var(--fg-muted); min-width: 34px; text-align: right; }
.pf-owner { display: inline-flex; align-items: center; gap: 7px; }
.pf-overdue { color: var(--red-fg); font-weight: 600; }
.row-x { border: 0; background: none; color: var(--fg-faint); cursor: pointer; padding: 4px; border-radius: 4px; }
.row-x:hover { color: var(--red); background: var(--bg-muted); }
.goals-table tr.closed td { color: var(--fg-faint); }
.goal-name { display: inline-flex; align-items: center; gap: 7px; color: var(--fg); text-decoration: none; font-weight: 500; }
.goal-name:hover { text-decoration: underline; }
.goal-branch { color: var(--fg-faint); }
.goal-metric { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 12px; font-size: 13px; }
.goal-metric-input { width: 110px; padding: 6px 8px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; }
.goal-notes { cursor: text; }
.project-dates { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin-top: 12px; font-size: 12.5px; }
.project-dates input { padding: 4px 6px; border: 1px solid var(--border); border-radius: 6px; font: inherit; font-size: 12.5px; }
.status-pill.achieved { color: var(--green-fg); background: var(--green-soft); border-color: var(--green-line); }
.status-pill.partial  { color: var(--amber-fg); background: var(--amber-soft); border-color: var(--amber-line); }
.status-pill.missed   { color: var(--red-fg); background: var(--red-soft); border-color: var(--red-line); }
.status-pill.dropped  { color: var(--fg-faint); background: var(--bg-muted); }
.status-editor button:disabled { cursor: default; }

/* ------------------------------------------------------------ прочее --- */

.toolbar .filter-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid transparent; background: transparent; color: var(--fg-muted);
  padding: 5px 9px; border-radius: 6px; font-size: 13px;
}
.toolbar .filter-btn:hover { background: var(--bg-muted); }
.toolbar .filter-btn.on { border-color: var(--border-strong); background: var(--surface); color: var(--fg); }

.task-row .grip { color: transparent; flex: 0 0 10px; cursor: grab; }
.task-row:hover .grip { color: var(--fg-faint); }

.section-add {
  display: flex; align-items: center; gap: 8px; padding: 14px 32px 28px;
  color: var(--fg-faint);
}
.section-add button {
  border: 1px dashed var(--border-strong); background: transparent; color: var(--fg-muted);
  border-radius: 6px; padding: 7px 14px; font-size: 13px;
}
.section-add button:hover { border-color: var(--accent); color: var(--accent); }

.empty-sm { padding: 18px 0; color: var(--fg-faint); font-size: 13px; text-align: center; }

/* ==========================================================================
   Всплывающие панели, меню, уведомления
   ========================================================================== */

.popover {
  position: fixed; z-index: 40; min-width: 200px; max-width: 340px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: 0 8px 28px rgba(0, 0, 0, .14);
  /* Never taller than the window: a panel that grows while in use (the
     field settings adding a subfield form) was cut off at the bottom with
     no way to reach its buttons. */
  max-height: calc(100vh - 16px); overflow: hidden auto; overscroll-behavior: contain;
  animation: pop-in .1s ease-out;
}
@keyframes pop-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* A panel above its element comes up from it, as one below comes down from it. */
.popover.above { animation-name: pop-in-up; }
@keyframes pop-in-up { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
/*
 * A search menu above its element has its field at the bottom, next to the
 * element, and the rows over it: the field stays where it is while the
 * list shrinks with each letter (popover.js, placePanel), and the eye does
 * not travel over the list to it (iteration 16, item 8). The rows keep
 * their order, the best match on top and lit. Only the drawing moves; the
 * order in the document, and so Tab and the arrows, stays.
 */
.popover.above .search-menu { display: flex; flex-direction: column; }
.popover.above .search-menu > .menu { order: 1; }
.popover.above .search-menu > .menu-foot { order: 2; }
.popover.above .search-menu > .menu-search { order: 3; border-bottom: 0; border-top: 1px solid var(--border); }

.menu { padding: 5px; display: flex; flex-direction: column; }
.menu.scroll { max-height: 280px; overflow-y: auto; }
.menu-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 6px 9px; border: 0; background: transparent; border-radius: 6px;
  text-align: left; font-size: 13.5px; color: var(--fg); line-height: 1.2;
}
.menu-item:hover { background: var(--bg-muted); }
/* The row Enter picks in a search menu (openSearchMenu), moved by the arrows. */
.menu-item.active { background: var(--bg-muted); }
.menu-item.checked { font-weight: 500; }
.menu-item.danger { color: var(--red); }
.menu-item .icon { flex: 0 0 auto; color: var(--fg-muted); }
/* The line is tall enough for g, y and p: at the item's 1.2 the hidden overflow cut their tails off. */
.menu-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1.4; }
.menu-hint { color: var(--fg-faint); font-size: 11.5px; }
.menu-gap { width: 15px; flex: 0 0 15px; }
.menu-sep { height: 1px; background: var(--border); margin: 5px 0; }
.menu-head {
  padding: 6px 9px 3px; font-size: 11px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--fg-faint);
}
.menu-empty { padding: 12px; text-align: center; color: var(--fg-faint); font-size: 13px; }
.menu-search {
  width: 100%; border: 0; border-bottom: 1px solid var(--border);
  padding: 10px 12px; outline: none; font-size: 13.5px;
}

.toasts {
  position: fixed; left: 20px; bottom: 20px; z-index: 60;
  display: flex; flex-direction: column; gap: 8px;
}
/*
 * On a phone a toast spans the bottom of the screen, where a search menu
 * opened above its button keeps its field: «Участники» stays open for the
 * next name, and the note about the one just added covered the field the
 * next name is typed into. While such a field is open the toasts go up.
 */
@media (max-width: 720px) {
  body:has(.popover.above .menu-search) .toasts { top: 20px; bottom: auto; }
}
.toast {
  display: flex; align-items: center; gap: 10px;
  background: var(--toast-bg); color: var(--toast-fg); border-radius: 8px;
  padding: 11px 12px 11px 14px; font-size: 13.5px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .25);
  animation: toast-in .16s ease-out; max-width: 420px;
}
@keyframes toast-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.toast.leaving { opacity: 0; transform: translateY(8px); transition: all .18s; }
.toast.error { background: #a5302a; }
.toast .icon { flex: 0 0 auto; opacity: .9; }
.toast-text { flex: 1; }
.toast-action {
  border: 0; background: transparent; color: #ffb4ae; font-weight: 600;
  padding: 2px 6px; border-radius: 5px; font-size: 13px;
}
.toast-action:hover { background: rgba(255, 255, 255, .12); }
.toast-close { border: 0; background: transparent; color: rgba(255, 255, 255, .55); padding: 2px; }
.toast-close:hover { color: var(--toast-fg); }

/* ==========================================================================
   Список задач как таблица
   ========================================================================== */

.tv { --cols: minmax(240px, 1fr) 186px 132px 128px 190px; --row-h: 45px; padding-bottom: 40px; }

.tv-head, .tv-row, .tv-add {
  display: grid; grid-template-columns: var(--cols); align-items: center;
}

.tv-head {
  position: sticky; top: calc(var(--head-h, 0px) + var(--bar-h, 0px)); z-index: 3;
  background: var(--surface); border-bottom: 1px solid var(--border-strong);
  padding: 0 32px;
}
.tv-head .th {
  font-size: 11.5px; color: var(--fg-muted); font-weight: 600;
  padding: 9px 5px; border-left: 1px solid var(--border);
  display: flex; align-items: center; gap: 6px; min-width: 0;
}
/* A head wider than its column ends in an ellipsis instead of running into the next one. */
.tv-head .th-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-head .th-sort { min-width: 0; max-width: 100%; overflow: hidden; }
.tv-head .th:first-child { border-left: 0; padding-left: 0; }
/* The filter before the first head. */
.tv-head .th:first-child > .tool-btn { margin: -4px 2px -4px -6px; }
/* A column that sorts shows its arrows on hover, the name cut shorter to make
   room for them; the sorted one keeps them lit. */
.tv-head .th-sort { display: inline-flex; align-items: center; gap: 5px; }
.tv-head .th-sort .icon { display: none; flex: none; color: var(--fg-faint); }
.tv-head .th:hover .th-sort .icon { display: inline-flex; }
.tv-head .th-sort.on { color: var(--fg); }
.tv-head .th-sort.on .icon { display: inline-flex; color: var(--accent); }
.tv-head .th-sort.desc .icon { transform: scaleY(-1); }
/* The columns' "+" after the last head, in the row's right padding. */
.tv-head.with-tail { position: sticky; }
.th-tail { position: absolute; right: 4px; top: 50%; transform: translateY(-50%); }
.th-add {
  width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--border-strong);
  background: var(--surface); color: var(--fg-muted); padding: 0;
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.th-add:hover { color: var(--fg); border-color: var(--fg-faint); background: var(--bg-muted); }
.tv-head .th button {
  border: 0; background: transparent; color: inherit; font: inherit;
  padding: 2px 4px; border-radius: 4px; display: inline-flex; align-items: center; gap: 5px;
}
.tv-head .th button:hover { background: var(--bg-muted); color: var(--fg); }

.tv-section { border-bottom: 1px solid var(--border); }
.tv-section-head {
  display: flex; align-items: center; gap: 8px;
  /* Заголовок теперь берут, чтобы тащить секцию, а сворачивает отдельная
     кнопка — поэтому и курсор здесь про перетаскивание, а не про нажатие. */
  cursor: grab; user-select: none;
  /* A band of its own, so a section reads as a heading and not as a row;
     the same 21px of padding as before, now even above and below the title. */
  padding: 10px 32px 11px; background: var(--section-fill);
}
.tv-section-head:active { cursor: grabbing; }
.sec-fold {
  display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; padding: 0; flex: none;
  border: 0; border-radius: 5px; background: transparent; cursor: pointer;
  color: var(--fg-faint);
}
.sec-fold:hover { background: var(--border-soft); color: var(--fg); }
/* Имя секции правится по нажатию, поэтому подсказываем это курсором и
   подчёркиванием при наведении — но только там, где имя и есть. */
.tv-section-head h3[title] { cursor: text; }
.tv-section-head h3[title]:hover { text-decoration: underline dotted; text-underline-offset: 3px; }
/*
 * Стрелка раздела поворачивается, как каретка у развёрнутой задачи. Прежние
 * правила целились в класс .caret, которого у значка нет вовсе: значок
 * рисуется одним и тем же, и свёрнутый раздел было не отличить от
 * развёрнутого.
 */
.tv-section-head .sec-fold .icon { transition: transform .15s; }
.tv-section.collapsed .sec-fold .icon { transform: rotate(-90deg); }
.tv-section.collapsed .tv-body { display: none; }
.tv-section-head h3 { margin: 0; font-size: 14px; font-weight: 600; }
.tv-section-head .n { color: var(--fg-faint); font-size: 12.5px; }
/* Номер строки в разделе: узкая колонка постоянной ширины, чтобы названия
   начинались по одной линии и при двузначных номерах. */
.tv-num {
  flex: none; width: 24px; text-align: right;
  color: var(--fg-faint); font-size: 11.5px;
  font-variant-numeric: tabular-nums; user-select: none;
}
/* «…» стоит сразу за «+», у самого названия раздела, как в Asana. У правого
   края таблицы её никто не находил: до неё через всю доску, да и видна она
   только при наведении. Оба значка появляются, когда мышь над разделом. */
.tv-section-head .sec-actions { margin-right: auto; display: flex; gap: 2px; }
/*
 * Прозрачность снимаем и с самой кнопки: внутри .sec-actions лежит кнопка
 * с классом .row-actions, а у него своё opacity: 0, которое снимается только
 * наведением на строку задачи. Из-за этого «…» у раздела не показывалась
 * вообще никогда — операций над разделом будто и не было.
 */
.tv-section-head .sec-actions, .tv-section-head .sec-actions .row-actions { opacity: 0; }
.tv-section-head:hover .sec-actions, .tv-section-head:focus-within .sec-actions,
.tv-section-head:hover .sec-actions .row-actions,
.tv-section-head:focus-within .sec-actions .row-actions { opacity: 1; }
.tv-section.dragover-section { background: var(--accent-soft); }
/* The band would hide the drop's accent behind the heading. */
.tv-section.dragover-section > .tv-section-head { background: transparent; }
/* "The subtasks end later" under the calendar, with the button that moves the date. */
.dp-late {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin: 6px 0 0; padding: 7px 9px; border-radius: 7px;
  background: var(--amber-soft); color: var(--amber-fg); font-size: 12px;
}
.cal-time { font-variant-numeric: tabular-nums; color: var(--fg-muted); margin-right: 4px; font-size: 11px; }
.tv-empty {
  padding: 10px 32px 12px 44px; color: var(--fg-faint); font-size: 12.5px;
  border: 1px dashed transparent; border-radius: 8px; margin: 4px 12px 8px;
}
.tv-section.dragover-section .tv-empty { border-color: var(--accent); }

.tv-row {
  padding: 0 32px; border-top: 1px solid var(--border-soft); cursor: pointer;
  background: var(--surface); position: relative;
}
.tv-row:hover { background: var(--bg-muted); }
/* Развёрнутые подзадачи сдвинуты целиком, вместе с номером: номер
   принадлежит строке, и оставлять его на месте, двигая одно название,
   значит разорвать строку пополам. */
.tv-row.sub .tv-cell:first-child { padding-left: calc(var(--depth, 1) * 26px); }
/* Поле новой подзадачи стоит с тем же отступом, что и строки вокруг него. */
.tv-add.sub .tv-cell:first-child { padding-left: calc(var(--depth, 1) * 26px); }
.tv-row.sub .tv-num { color: var(--fg-faint); }
/* Раздел внутри задачи в развёрнутой ветке: заголовок группы, не строка. */
.tv-row.tv-subsec { cursor: default; }
/* The section band, as under a list section's heading; no hover of a row. */
.tv-row.tv-subsec, .tv-row.tv-subsec:hover { background: var(--section-fill); }
.tv-row.tv-subsec .sec-title {
  font-size: 12.5px; font-weight: 600; color: var(--fg-muted);
  letter-spacing: .01em; cursor: text;
}
.tv-row.tv-subsec .sec-title:hover { text-decoration: underline dotted; text-underline-offset: 3px; }
/* Каретка разворота: узкая колонка постоянной ширины, чтобы названия
   начинались по одной линии и у задач без подзадач. */
.row-expand {
  flex: none; width: 16px; height: 16px; padding: 0; margin-right: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 4px; background: transparent; cursor: pointer;
  color: var(--fg-faint);
}
.row-expand.empty { cursor: default; }
.row-expand:not(.empty):hover { background: var(--border-soft); color: var(--fg); }
.row-expand svg { transition: transform .15s; }
.row-expand.on svg { transform: rotate(90deg); }
.tv-row.done .task-title { color: var(--title-done); font-weight: 400; }
/* Перетаскиваемая строка едет вместе с указателем и сразу встаёт на новое
   место, поэтому прятать её нельзя — только пометить, что она «в руке». */
.tv-row.dragging {
  opacity: .75; background: var(--accent-soft);
  outline: 1px solid var(--accent); outline-offset: -1px;
}

.tv-cell {
  padding: 6px 5px; border-left: 1px solid transparent; min-width: 0;
  display: flex; align-items: center; gap: 7px; min-height: 38px;
}
.tv-cell + .tv-cell { border-left-color: var(--border-soft); }
.tv-row:hover .tv-cell { border-left-color: var(--border); }
.tv-cell:first-child { border-left: 0 !important; padding-left: 0; }

.task-main { display: flex; align-items: center; gap: 9px; min-width: 0; flex: 1; }
/* The parent of a subtask in a section: faint, after the name, cut before it. */
.task-parent {
  flex: 0 100 auto; min-width: 0; max-width: 40%; border: 0; background: transparent; padding: 0;
  color: var(--fg-faint); font-size: 12.5px; cursor: pointer; overflow: hidden;
}
.task-parent > span { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-parent:hover { color: var(--fg-muted); text-decoration: underline; }
.card-parent { margin: -2px 0 6px 24px; color: var(--fg-faint); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.task-title {
  /* Растёт первым, сжимается последним: в строке важнее всего название. */
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13.5px;
  color: var(--title-open);
  font-weight: 450;
}
/*
 * В списке название занимает ровно свою ширину, а не всю строку. Иначе не
 * получается трёх мест, как в Asana: щелчок по названию правит его, левее
 * — выделяет, правее — открывает карточку. Растянутое на всю строку
 * название съедало бы третье место целиком.
 */
.tv-row .task-title { flex: 0 1 auto; cursor: text; }
/*
 * Держатель имени занимает ровно его ширину — на этом и держатся три места
 * щелчка. Под курсором текст в нём подменяется полем той же ширины, поэтому
 * ничего не сдвигается и зоны вокруг остаются на своих местах.
 */
.tv-row .task-title-host { flex: 0 1 auto; min-width: 0; display: flex; cursor: text; }
.tv-row .row-title-edit {
  flex: 0 1 auto; font-size: 13.5px; height: 20px;
  /* Длинное имя не должно распирать строку: поле ужимается вместе с ней. */
  max-width: 100%;
  /* A cut name keeps its «…» under the pointer until it is clicked into, as the text had it. */
  text-overflow: ellipsis;
}
/* Значки строки ужимаются раньше названия и прячутся за его краем. */
.row-meta {
  display: inline-flex; align-items: center; gap: 8px; flex: 0 1 auto; min-width: 0; overflow: hidden;
  padding-right: 14px; /* зона растворения приходится на отступ, а не на последний значок */
  /* Обрезанный значок растворяется, а не режется по живому. */
  mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent);
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 14px), transparent);
}
.row-meta:empty { display: none; }
.row-meta > * { flex: 0 0 auto; }
/*
 * Поле правки имени на месте — общее для карточки доски и подзадач.
 * Оно подменяет текст под курсором и потому обязано занимать ровно то же
 * место: тот же шрифт, та же высота строки, ни отступов, ни рамки. Иначе
 * строки и карточки дёргались бы от одного движения мышью.
 */
.title-edit {
  flex: 1; min-width: 0; width: 100%; display: block;
  font: inherit; line-height: inherit; color: inherit;
  margin: 0; padding: 0; border: 0; background: transparent;
  outline: none; resize: none; overflow: hidden;
}
/* Правку видно по самой строке или карточке, а не по рамке поля. */
.card:has(.title-edit:focus) { border-color: var(--focus); box-shadow: 0 0 0 2px var(--focus-soft); }
/*
 * No mark under a name being edited inside the card: the caret says it, as
 * in the project's list. The line under it read as a stray underline.
 */

/*
 * Ручка переноса. Тянут строку только за неё — за левый край до номера, как
 * в Asana; поэтому её область захвата продолжена влево на отступ строки:
 * целиться в двенадцать точек мышью невозможно, а промах утаскивал бы
 * строку оттуда, где её хотели просто выделить.
 */
.row-grip {
  width: 14px; flex: 0 0 14px; color: transparent; cursor: grab;
  display: flex; align-items: center; justify-content: center;
  position: relative;
}
.row-grip::before {
  content: ''; position: absolute; top: -14px; bottom: -14px; left: -32px; right: -2px;
}
.row-grip:active { cursor: grabbing; }
/* Ручку видно: бледнее фона она сливалась со строкой и читалась как мусор. */
.tv-row:hover .row-grip { color: var(--fg-faint); }
.tv-row .row-grip:hover { color: var(--fg-muted); }
.row-actions { display: flex; gap: 1px; opacity: 0; flex: 0 0 auto; }
/* Стрелка «открыть» смотрит вправо: карточка выезжает справа. */
.row-open svg { transform: rotate(-90deg); }
.tv-row:hover .row-actions { opacity: 1; }
.row-actions button {
  border: 0; background: transparent; color: var(--fg-faint);
  padding: 4px; border-radius: 5px; display: inline-flex;
}
.row-actions button:hover { background: var(--border); color: var(--fg); }

/* ячейка, которую можно править на месте */
.cell-btn {
  display: flex; align-items: center; gap: 7px; width: 100%;
  border: 1px solid transparent; background: transparent; border-radius: 6px;
  padding: 4px 7px; font-size: 12.5px; color: var(--fg-muted); min-width: 0;
  text-align: left; min-height: 28px;
}
.cell-btn:hover { background: var(--surface); border-color: var(--border-strong); }
.cell-btn.cell-open { background: var(--surface); border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.cell-btn .label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-btn.empty { color: var(--fg-faint); }
/* The assignee's cross: there on hover only, and never on the round picture-only button of a subtask. */
.cell-clear { display: none; align-items: center; justify-content: center; flex: none; width: 18px; height: 18px; margin-left: auto; border-radius: 4px; color: var(--fg-muted); }
.cell-btn:hover .cell-clear { display: inline-flex; }
.cell-clear:hover { background: var(--bg-muted); color: var(--fg); }
.subtask-who .cell-clear { display: none !important; }
/* Пустая ячейка в списке молчит. Раньше в каждой строке стояли слова
   «Назначить», «Срок», «Метки» — тринадцать задач давали тридцать девять
   подписей ни о чём, и глазу не за что было зацепиться. Теперь остаётся
   только значок, а подпись показывается, когда на строку наводят: там она
   и нужна — чтобы понять, куда нажимать. */
.tv-row .cell-btn.empty .label { display: none; }
/* The hint shows on the cell under the pointer only. Shown on every empty
   cell of a hovered or selected row, a selection of rows lit up a wall of
   «Задать» where nothing is set. */
.tv-row .cell-btn.empty:hover .label,
.tv-row .cell-btn.empty:focus-visible .label { display: block; }
.tv-row .cell-btn.empty > svg,
.tv-row .cell-btn.empty .avatar { opacity: .55; }
.tv-row:hover .cell-btn.empty > svg,
.tv-row:hover .cell-btn.empty .avatar { opacity: 1; }
.cell-tags { display: flex; gap: 4px; flex-wrap: nowrap; overflow: hidden; min-width: 0; }
/* Метка в ячейке не должна вылезать за колонку: обрезаем по многоточию. */
.cell-tags .chip { max-width: 92px; overflow: hidden; text-overflow: ellipsis; display: inline-block; }

.tv-add {
  padding: 0 32px; border-top: 1px solid var(--border-soft);
}
.tv-add .tv-cell { color: var(--fg-faint); }
/* A number or a text typed in place: as wide as the cell, drawn as the focused field. */
.cell-input {
  width: 100%; min-width: 0; box-sizing: border-box; padding: 5px 8px;
  border: 1px solid var(--accent); border-radius: 6px; background: var(--surface); color: var(--fg);
  font: inherit; font-size: 13px; outline: none;
}
.tv-add input {
  flex: 1; border: 0; outline: none; background: transparent; font-size: 13.5px; padding: 3px 0;
}
.tv-add:hover { background: var(--bg-muted); }
/* ------------------------------------------- поле с видимой разметкой --- */

/*
 * Ввод с разметкой на месте: жирный сразу жирный. Снаружи выглядит и ведёт
 * себя как поле ввода, внутри — обычные узлы, которые мы же и создаём.
 */
.rich-input {
  width: 100%; padding: 9px 11px; font: inherit; font-size: 14px; line-height: 1.5;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  background: var(--surface); color: var(--fg);
  outline: none; overflow-y: auto; max-height: 40vh; white-space: pre-wrap; word-break: break-word;
}
/* Рамка пишущего поля — спокойная, серая: та же, что у поля комментария.
   Красная читалась как ошибка ввода. */
.rich-input:focus { border-color: var(--fg-muted); box-shadow: 0 0 0 3px var(--bg-muted); }
.rich-input:empty::before {
  content: attr(data-placeholder); color: var(--fg-faint); pointer-events: none;
}
.rich-input p { margin: 0 0 6px; }
.rich-input p:last-child { margin-bottom: 0; }
.rich-input ul, .rich-input ol { margin: 4px 0 6px; padding-left: 22px; }
.rich-input li { margin: 2px 0; }
.rich-input blockquote {
  margin: 4px 0; padding-left: 10px; border-left: 3px solid var(--border-strong); color: var(--fg-muted);
}
.rich-input code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  background: var(--bg-muted); border-radius: 4px; padding: 3.5px 4px; white-space: break-spaces;
}
.rich-input a { color: var(--accent); }
/* Блок кода: моноширинный, с подложкой — и в поле ввода, и в показе. */
.rich pre, .rich-input pre {
  margin: 6px 0; padding: 8px 10px; border-radius: 8px;
  background: var(--bg-muted); border: 1px solid var(--border);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  white-space: pre-wrap; word-break: break-word;
}
/*
 * A spoiler (markup.js): open and framed where it is read, a left line and
 * a tint, as a quote but in the accent; folded away in a comment
 * (.c-folded, comment-fold.js). In the editor it is plainly marked: a
 * dashed frame and its name above it, so one sees what will be hidden.
 */
.rich .spoiler {
  margin: 6px 0; padding: 4px 0 4px 11px; border-left: 3px solid var(--accent);
  background: var(--accent-soft); border-radius: 0 6px 6px 0;
}
.rich-input .spoiler {
  position: relative; margin: 18px 0 6px; padding: 6px 10px;
  border: 1px dashed var(--accent); border-radius: 6px; background: var(--accent-soft);
}
.rich-input .spoiler::before {
  content: 'Спойлер'; position: absolute; left: 8px; top: -17px;
  font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--accent);
  pointer-events: none; user-select: none;
}
.rich-input .spoiler:first-child { margin-top: 16px; }
/*
 * A table (markup.js, iteration 18, item 5), read and in the editor alike:
 * thin rules, the first row the head on a light fill. A wide one scrolls
 * inside the text (display:block with overflow) instead of widening the
 * card; a picture in a cell keeps to the cell. Colours are the theme's
 * tokens, so the dark theme needs nothing of its own.
 */
:is(.rich, .rich-input) .rich-table {
  display: block; max-width: 100%; overflow-x: auto; margin: 6px 0;
  border-collapse: collapse; border-spacing: 0;
}
:is(.rich, .rich-input) .rich-table :is(th, td) {
  border: 1px solid var(--border); padding: 4px 8px; min-width: 3em;
  text-align: left; vertical-align: top;
  /*
   * Words whole: the text around breaks a long word anywhere, and in a
   * table that let a column shrink to two letters ("Ст/ол/бе/ц1"). A wide
   * table scrolls instead.
   */
  overflow-wrap: normal; word-break: normal;
}
:is(.rich, .rich-input) .rich-table th { background: var(--bg-muted); font-weight: 600; }
:is(.rich, .rich-input) .rich-table [data-align="center"] { text-align: center; }
:is(.rich, .rich-input) .rich-table [data-align="right"] { text-align: right; }
:is(.rich, .rich-input) .rich-table .rich-img { max-width: 100%; margin: 2px 0; }

/* Вид абзаца — кнопка с названием, а не значок: названия у видов разные. */
.markup-btn.block-pick {
  gap: 4px; padding: 3px 6px; width: auto; font-size: 12.5px;
  display: inline-flex; align-items: center;
}
.markup-btn.block-pick .block-name { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.markup-bar .block-pick + .markup-btn { margin-left: 6px; }
/* Разметка в поле ввода стоит там же, где панель пометок. */
.markup-bar + .rich-input { border-top-left-radius: 0; border-top-right-radius: 0; }

/* --- картинки в тексте: показ, перетаскивание, растягивание --- */
.rich-img {
  display: block; max-width: 100%; height: auto; margin: 6px 0;
  border-radius: 8px; border: 1px solid var(--border);
}
/*
 * A picture with no width of its own (saved before pictures were boxed on
 * insert, or written through /api/1.0, MCP or the Asana transfer) is held
 * to the box an inserted one gets, PICTURE_BOX in image.js (240x360 since
 * iteration 18, item 12), here on display only: nothing in the stored text
 * changes. Both limits as max-*, so the picture keeps its aspect ratio
 * whichever side the box stops. Keyed on the style attribute, not a class:
 * a handle dragged in the editor sets the width live, and the cap has to
 * let go at that moment.
 */
.rich-img:not([style*="width"]) { max-width: min(100%, 240px); max-height: 360px; }
.rich-img.loading { min-width: 120px; min-height: 80px; background: var(--bg-soft); }
/* A picture in a read text opens enlarged (viewer.js); one inside a link follows the link. */
.rich .rich-img { cursor: zoom-in; }
.rich a .rich-img { cursor: pointer; }
/* Поле, в которое сейчас тянут файл: видно, что его тут ждут. */
.rich-input.dropping { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.rich-input { position: relative; }
/* Полосы по бокам картинки — за них её и тянут. */
/*
 * Восемь ручек размера: четыре стороны и четыре угла. Стороны — полосы во
 * всю кромку: именно в них целятся, и квадратик в восемь пикселей был бы
 * плохой мишенью. Углы — квадраты поверх полос, они же и задают привычные
 * диагональные курсоры.
 *
 * Каждая ручка стоит ровно на своей точке кромки, а по центру её ставит
 * transform: так разметке не нужно знать её размер.
 */
.img-grip {
  position: absolute; transform: translate(-50%, -50%);
  border-radius: 3px; background: var(--accent); z-index: 2;
}
/*
 * Полосы вдоль кромок держим бледными: они лежат поверх самой картинки, и
 * восемь ярких полос читались бы как рамка, наглухо закрывающая её края.
 * Под указателем полоса проявляется — тогда и понятно, за что тянешь.
 */
.img-grip.w, .img-grip.e, .img-grip.n, .img-grip.s { opacity: .3; }
.img-grip:hover, .img-grip:active { opacity: 1; }
.img-grip.w, .img-grip.e { width: 6px; cursor: ew-resize; }
.img-grip.n, .img-grip.s { height: 6px; cursor: ns-resize; }
/* Углы — поверх полос, иначе до них не дотянуться на стыке. */
.img-grip.nw, .img-grip.ne, .img-grip.sw, .img-grip.se {
  width: 12px; height: 12px; border-radius: 3px; z-index: 3;
  border: 2px solid var(--surface);
}
.img-grip.nw, .img-grip.se { cursor: nwse-resize; }
.img-grip.ne, .img-grip.sw { cursor: nesw-resize; }
/* Пока тянут — курсор не должен меняться от того, что под ним оказалось. */
body.img-resizing { user-select: none; }

/* A field inserted between rows by "+" or Enter looks like the one in the
   card: no fill and no coloured line. The red band and border read as an
   error and made the list flicker on every Enter; the caret and the
   placeholder are enough to show where the task goes. */
.tv-add.inline { background: transparent; }
.tv-add.inline .tv-cell { color: var(--fg); }
.tv-add.inline input::placeholder { color: var(--fg-muted); }
/* «+» у названия раздела: появляется вместе с «…», когда мышь над разделом. */
.tv-section-head .sec-add, .column-head .col-add {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; padding: 0; flex: none;
  border: 0; border-radius: 6px; background: transparent; cursor: pointer;
  color: var(--fg-faint);
}
.tv-section-head .sec-add { opacity: 0; }
.tv-section-head:hover .sec-add, .tv-section-head:focus-within .sec-add { opacity: 1; }
.tv-section-head:hover .sec-add, .column-head:hover .col-add { color: var(--fg-muted); }
.tv-section-head .sec-add:hover, .column-head .col-add:hover { background: var(--border-soft); color: var(--fg); }

/* ==========================================================================
   Панели настроек списка, поиск, подсказка по клавишам
   ========================================================================== */

.tool-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid transparent; background: transparent; color: var(--fg-muted);
  padding: 6px 10px; border-radius: 6px; font-size: 13px;
}
.tool-btn:hover { background: var(--bg-muted); color: var(--fg); }
.tool-btn.on { border-color: var(--border-strong); background: var(--surface); color: var(--fg); }
.tool-btn.icon-only { padding: 6px 7px; position: relative; }
.tool-btn.icon-only .badge { position: absolute; top: -3px; right: -4px; }
.tool-btn .badge {
  background: var(--accent); color: #fff; border-radius: 9px;
  font-size: 10.5px; padding: 0 5px; line-height: 15px; min-width: 15px; text-align: center;
}

.filter-panel { padding: 12px; display: flex; flex-direction: column; gap: 12px; width: 268px; }
.filter-panel .group > label {
  display: block; font-size: 11.5px; color: var(--fg-muted); font-weight: 600; margin-bottom: 6px;
}
/* Not the checkbox: stretched to the full width, it stood alone in the
   middle of the panel with its words squeezed to the right edge. */
.filter-panel select, .filter-panel input:not([type="checkbox"]) {
  width: 100%; padding: 7px 9px; border: 1px solid var(--border-strong);
  border-radius: 6px; outline: none; font-size: 13px; background: var(--surface);
}
.filter-panel .row { display: flex; gap: 8px; }
.filter-check {
  display: flex; align-items: center; gap: 8px; padding: 2px 0;
  font-size: 13px; color: var(--fg); cursor: pointer; user-select: none;
}
.filter-check input { width: 15px; height: 15px; margin: 0; flex: 0 0 auto; accent-color: var(--accent); cursor: pointer; }
.filter-panel .row button { flex: 1; }

.search-results {
  /* Placed by the field's position (app.js): wider than the sidebar it lives in. */
  position: fixed; width: 460px; max-width: calc(100vw - 20px); z-index: 75;
  color: var(--fg);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .14); overflow: hidden; overflow-y: auto;
}
/* On a phone the field is a hundred pixels wide and so was the list under
   it: no title fitted. There it spans the screen under the top bar. */
@media (max-width: 720px) {
  .search-results { left: 8px !important; right: 8px; width: auto; }
}
.search-results .sr-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 9px 12px; border: 0; background: transparent; text-align: left; font-size: 13.5px;
}
.search-results .sr-item:hover, .search-results .sr-item.active { background: var(--bg-muted); }
.search-results .sr-title { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-results .sr-head {
  padding: 8px 12px 4px; font-size: 11px; color: var(--fg-muted);
  text-transform: uppercase; letter-spacing: .05em; font-weight: 600;
}
.search-results .sr-empty { padding: 16px; text-align: center; color: var(--fg-faint); font-size: 13px; }
/* The kind chips at the top, as under Asana's search. */
.sr-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 12px 6px; border-bottom: 1px solid var(--border-soft, var(--border)); }
.sr-chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--fg); font-size: 13px; cursor: pointer;
}
.sr-chip:hover { background: var(--bg-muted); }
.sr-chip.on { background: var(--accent-soft, var(--bg-muted)); border-color: var(--accent); color: var(--accent); }
.search-results .sr-sub { font-size: 12px; color: var(--fg-faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 45%; }
.search-results .sr-title.done { color: var(--fg-muted); }
/* "All results" and "Advanced search" close the drop-down, under a line. */
.search-results .sr-foot { border-top: 1px solid var(--border-soft, var(--border)); }
.search-results .sr-foot .sr-item { color: var(--fg-muted); }

/* ------------------------------------------------- the search screen --- */
/* The filters as removable phrases, under the bar. */
.search-chips { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 32px 10px; }
.search-chip {
  display: inline-flex; align-items: center; gap: 4px; max-width: 420px; padding: 3px 4px 3px 10px;
  border: 1px solid var(--border-strong); border-radius: 999px; background: var(--surface); font-size: 12.5px; color: var(--fg);
}
.search-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.search-chip .x { display: inline-flex; border: 0; background: transparent; color: var(--fg-muted); padding: 3px; border-radius: 50%; cursor: pointer; }
.search-chip .x:hover { background: var(--bg-muted); color: var(--fg); }
/* The advanced search, as Asana's form: a label and its control a row; it scrolls on its own when long. */
.adv-panel {
  margin: 0 32px 12px; padding: 14px 16px; display: flex; flex-direction: column; gap: 8px;
  border: 1px solid var(--border); border-radius: var(--radius-lg); background: var(--surface);
  max-height: 46vh; overflow-y: auto;
}
/* Rows keep their height: in a column of limited height flex items shrank and overlapped. */
.adv-panel > * { flex: none; }
.adv-row { display: flex; align-items: flex-start; gap: 12px; min-height: 30px; }
.adv-label { flex: 0 0 170px; padding-top: 6px; font-size: 13px; color: var(--fg-muted); }
.adv-controls { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.adv-stack { display: flex; flex-direction: column; gap: 6px; width: 100%; max-width: 560px; }
.adv-text, .adv-range input, .adv-field-value input {
  padding: 6px 9px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 13px;
}
.adv-text { width: 100%; box-sizing: border-box; }
.adv-picks { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding-top: 3px; }
.adv-picks .chip { font-size: 12.5px; padding: 5px 9px; }
.adv-range { display: inline-flex; align-items: center; gap: 6px; }
.adv-range.hidden { display: none; }
.adv-checks { display: flex; flex-wrap: wrap; gap: 4px 16px; padding-top: 4px; }
.adv-hint { font-size: 12.5px; color: var(--fg-faint); padding-top: 6px; }
.adv-fields { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; width: 100%; }
.adv-field { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.adv-field-name { min-width: 120px; font-size: 13px; }
.adv-actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 8px; border-top: 1px solid var(--border-soft, var(--border)); position: sticky; bottom: -14px; background: var(--surface); padding-bottom: 2px; }
/* Under the list: how many are shown, and the next page. */
.search-foot { display: flex; align-items: center; gap: 12px; padding: 12px 32px 28px; font-size: 13px; color: var(--fg-muted); }
@media (max-width: 720px) {
  .adv-panel, .search-chips { margin-left: 16px; margin-right: 16px; }
  .search-chips { padding: 0 0 10px; }
  .adv-row { flex-direction: column; gap: 4px; }
  .adv-label { flex: none; padding-top: 0; }
  .search-foot { padding: 12px 16px 24px; }
}

/* Окно сочетаний клавиш — как справка в Asana: шапка с крестиком, серая
   колонка с поиском и группами слева, прокручиваемые группы справа.
   Плоским списком в две колонки строки скакали по высоте: подпись вроде
   «Добавить раздел: в списке и на доске» ломалась на три строки и рвала
   сетку. Теперь контекст в заголовке группы, а не в каждой подписи. */
.keys-backdrop {
  position: fixed; inset: 0; background: rgba(30, 31, 33, .35); z-index: 70;
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.keys {
  background: var(--surface); border-radius: 12px; box-shadow: var(--shadow-md);
  width: 100%; max-width: 760px; height: min(640px, 100vh - 48px);
  display: flex; flex-direction: column; overflow: hidden;
}
.k-head {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 20px; border-bottom: 1px solid var(--border); flex: none;
}
.k-head h2 { margin: 0; font-size: 17px; flex: 1; }
.k-close {
  border: 0; background: transparent; color: var(--fg-muted);
  width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center;
}
.k-close:hover { background: var(--bg-muted); color: var(--fg); }

.k-main { display: flex; min-height: 0; flex: 1; }
.k-side {
  width: 210px; flex: none; background: var(--bg-muted); border-right: 1px solid var(--border);
  padding: 14px; display: flex; flex-direction: column; gap: 10px; overflow-y: auto;
}
.k-search {
  width: 100%; border: 1px solid var(--border-strong); border-radius: 18px;
  padding: 7px 12px; font: inherit; font-size: 13px; background: var(--surface); color: var(--fg);
}
.k-search:focus { outline: none; border-color: var(--fg-faint); }
.k-tabs { display: flex; flex-direction: column; gap: 2px; }
.k-tab {
  display: flex; align-items: center; gap: 8px; text-align: left;
  border: 0; background: transparent; color: var(--fg-muted);
  padding: 7px 9px; border-radius: 7px; font-size: 13px;
}
.k-tab svg { flex: none; opacity: .8; }
.k-tab:hover { background: var(--border); color: var(--fg); }
.k-tab.on { background: var(--surface); color: var(--fg); font-weight: 600; box-shadow: var(--shadow-sm); }

.k-body { flex: 1; min-width: 0; overflow-y: auto; padding: 18px 22px 24px; }
.k-group + .k-group { margin-top: 26px; }
.k-group h3 { display: flex; align-items: center; gap: 7px; margin: 0; font-size: 14px; }
.k-group h3 svg { color: var(--fg-muted); flex: none; }
.k-hint { margin: 3px 0 0; font-size: 12px; color: var(--fg-faint); }
.k-rows { margin-top: 8px; border: 1px solid var(--border); border-radius: 9px; overflow: hidden; }
.k-row {
  display: flex; align-items: center; gap: 16px; padding: 8px 12px;
  font-size: 13px; color: var(--fg);
}
.k-row + .k-row { border-top: 1px solid var(--border); }
.k-row:hover { background: var(--bg-muted); }
.k-what { flex: 1; min-width: 0; }
/* Клавиши прижаты вправо: так все kbd стоят в одну вертикаль и список
   читается столбцом, а не рваным краем. */
.k-keys { display: flex; align-items: center; gap: 5px; flex: none; }
.k-join { font-size: 11px; color: var(--fg-faint); }
.k-empty { color: var(--fg-faint); font-size: 13px; }
/* display: flex сильнее браузерного [hidden], и без этой строки поиск
   прятал только группы, а сами строки оставались на месте. */
.k-row[hidden], .k-tab[hidden], .k-group[hidden] { display: none; }
.keys kbd {
  background: var(--bg-muted); border: 1px solid var(--border-strong); border-bottom-width: 2px;
  border-radius: 5px; padding: 2px 7px; font-family: inherit; font-size: 11.5px;
  color: var(--fg); min-width: 22px; text-align: center;
}

/* Узкий экран: колонка превращается в ряд вкладок над списком. */
@media (max-width: 640px) {
  .keys { height: min(640px, 100vh - 32px); }
  .k-main { flex-direction: column; }
  .k-side {
    width: auto; border-right: 0; border-bottom: 1px solid var(--border);
    overflow-x: auto; overflow-y: hidden;
  }
  .k-tabs { flex-direction: row; }
  .k-tab { white-space: nowrap; }
  .k-body { padding: 16px; }
}

/* звезда избранного */
.star { border: 0; background: transparent; padding: 3px; border-radius: 5px; color: var(--sidebar-fg-dim); }
.star:hover { color: #f5c451; background: var(--sidebar-hover); }
.star.on { color: #f5c451; }
.project-head .star { color: var(--fg-faint); display: inline-flex; align-items: center; justify-content: center; }
.project-head .star:hover, .project-head .star.on { color: #e8a317; background: var(--bg-muted); }

/* отметка о выполнении: короткий отклик, как в привычных трекерах */
@keyframes pop-check { 0% { transform: scale(1); } 45% { transform: scale(1.35); } 100% { transform: scale(1); } }
.check.just-done { animation: pop-check .28s ease-out; }

/* Ячейка без собственного поведения: выравниваем как кнопочные поля. */
.cell-plain { display: flex; align-items: center; padding: 4px 7px; min-height: 28px; min-width: 0; }
.cell-plain .pill { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Место, куда встанет карточка при броске на доске. */
.card-placeholder {
  height: 3px; border-radius: 2px; background: var(--accent);
  margin: 4px 0; box-shadow: 0 0 0 3px var(--accent-soft);
}

/* ==========================================================================
   Вложения, зависимости, упоминания
   ========================================================================== */

.files { display: flex; flex-direction: column; gap: 2px; }
.files:empty { display: none; }
.file {
  display: flex; align-items: center; gap: 9px; padding: 6px 8px;
  border-radius: var(--radius); min-width: 0;
}
.file:hover { background: var(--bg-muted); }
.file.uploading { opacity: .6; }
.file .f-icon { color: var(--fg-faint); display: flex; }
.file .f-thumb {
  width: 30px; height: 30px; object-fit: cover; border-radius: 4px;
  border: 1px solid var(--border); flex: 0 0 30px; background: var(--bg-muted);
}
.file .f-name {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--fg); text-decoration: none; font-size: 13px;
}
a.f-name:hover { color: var(--accent); text-decoration: underline; }
.file .f-meta { color: var(--fg-faint); font-size: 11.5px; white-space: nowrap; }
.file .f-del, .dep .f-del {
  border: 0; background: transparent; color: var(--fg-faint); padding: 3px;
  border-radius: 4px; opacity: 0; display: flex;
}
.file:hover .f-del, .dep:hover .f-del { opacity: 1; }
.file .f-del:hover, .dep .f-del:hover { background: var(--red-soft); color: var(--red); }

/*
 * Where a file dragged in from outside may go (drawer.js): the description
 * and the files are outlined where they stand, and the comment field is
 * covered by a plate. The card stays in sight around them, and nothing
 * moves: an opaque cover of three boxes hid the description it offered to
 * drop into, and a plate that took room under the files moved the card
 * under the pointer. Every target shows at once (dashed, tinted); the one
 * under the pointer is lit (solid, filled). The colours are mixed from
 * the accent, so both themes follow it.
 */
.drop-plate { display: none; }
.drawer.file-drag .drop-plate { display: flex; }
/*
 * The label is plain text, lit or not: framed in a pill, and filled blue
 * when lit, it looked like a button (the owner, iteration 15). The 3px
 * padding keeps the 24px box the pill had, so the words stay on the
 * heading's baseline.
 */
.drop-label { display: inline-flex; align-items: center; padding: 3px 0; white-space: nowrap; color: var(--accent); font-size: 12.5px; line-height: 18px; }
.drop-label b { font-weight: 500; }

/*
 * The outline is drawn behind the block and past its edges, in the gaps
 * between sections, so it takes no room. It is the block's own (a
 * pseudo-element), so a file let go on the outline goes to the block as
 * well: drawn as an element of its own past the block's edges, the outline
 * refused the file it framed. An empty files section is a heading only:
 * its outline reaches further into the gaps, to be a target to aim at.
 */
.drawer.file-drag .drawer-section[data-zone] { position: relative; z-index: 0; }
.drawer.file-drag .drawer-section[data-zone]::before {
  content: ''; position: absolute; inset: -12px -10px; z-index: -1;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 60%, transparent); border-radius: var(--radius-lg);
  background: color-mix(in srgb, var(--accent) 4%, transparent);
  transition: background-color .12s, border-color .12s;
}
.drawer.file-drag .drawer-section[data-zone="files"]:has(.files:empty)::before { inset: -16px -10px; }
.drawer.file-drag .drawer-section[data-zone].over::before {
  border-style: solid; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
/*
 * The label stands at the right of the block's heading, where the
 * heading's buttons and the editor's toolbar give way to it (they do
 * nothing for a file in hand): never over the words. Kept at the top of a
 * long text scrolled, it covered a line of it.
 */
.drop-tag { position: absolute; top: -4px; right: 0; pointer-events: none; }
[data-zone="files"] > .drop-tag { top: 0; }
.drawer.file-drag :is(.desc .markup-bar, [data-zone="files"] .section-actions) { visibility: hidden; }
/*
 * An open editor in the outline looks like the text read: its own frame
 * stood just inside the outline, and two frames read as two targets. The
 * caret following the pointer shows where the file goes.
 */
.drawer.file-drag .desc .rich-input { border-color: transparent; background: transparent; box-shadow: none; }

/*
 * The comment field's plate covers the field: a file let go on it goes
 * where commentTarget() says, not to the field's own drop at the pointer.
 * Opaque, so the field's hint does not run into the label.
 */
.drawer-foot .comment-form { position: relative; }
.drop-plate.drop-cover {
  position: absolute; inset: 0; z-index: 2; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; border-radius: 12px;
  border: 1.5px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  background: color-mix(in srgb, var(--accent) 4%, var(--surface));
  transition: background-color .12s, border-color .12s;
}
[data-zone].over .drop-plate.drop-cover {
  border-style: solid; border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, var(--surface));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 16%, transparent);
}
/* The plate itself stays the target: no enter and leave between its own children to flicker the light. */
.drop-plate > * { pointer-events: none; }

/* A link that reads as words in a line («Открыть», «Отчёты» over a widget): an <a>, never a button. */
.link-btn {
  border: 0; background: transparent; padding: 0; color: var(--accent);
  font-size: inherit; text-align: left;
}
.link-btn:hover { text-decoration: underline; }

/*
 * A text button: an action drawn as words, without a button's plate at
 * rest («Показать изменения», «+ раздел», «Сбросить», «Показать больше»).
 * These were .link-btn, a link's look (underlined under the pointer) on
 * controls that open no page; the owner's rule is that a link opens a page
 * and a button acts (iteration 17, item 6). Under the pointer it takes a
 * button's soft plate and no underline. The plate's padding is given back
 * by an equal negative margin, so the words stay where they stood and no
 * row around them moves.
 */
.text-btn {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 6px; margin: -2px -6px; border: 0; border-radius: 6px;
  background: transparent; color: var(--fg-muted);
  font-size: inherit; line-height: inherit; text-align: left; text-decoration: none;
  transition: background-color .12s, color .12s;
}
.text-btn:hover { background: color-mix(in srgb, var(--fg) 7%, transparent); color: var(--fg); }
.text-btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 0; }
.text-btn:disabled { cursor: default; background: transparent; }
.text-btn.accent { color: var(--accent); }
.text-btn.accent:hover { color: var(--accent); background: color-mix(in srgb, var(--accent) 11%, transparent); }
.text-btn.subtle { color: var(--fg-faint); font-size: 12.5px; white-space: nowrap; }
.text-btn.subtle:hover { color: var(--fg-muted); }
/* After words in a line («описание: изменено Показать изменения») or a chip («Показать все» after the projects), the plate keeps off what stands before it. */
.activity-item .change .text-btn, .proj-chips .text-btn { margin-left: 0; }

/*
 * A blocked task's hourglass on a board card (blockChip), in the amber the
 * list row's completion circle has: by it one sees what not to take up
 * without opening the task. Only the icon, coloured; the pill around the
 * word «Заблокирована» filled the card's line.
 */
.block-chip { display: inline-flex; align-items: center; }
.block-chip.blocked { color: var(--amber-fg); }

.dep {
  display: flex; align-items: center; gap: 6px; padding: 3px 4px;
  border-radius: var(--radius); font-size: 13.5px; color: var(--fg-muted);
}
.dep:hover { background: var(--bg-muted); }
.dep.done { color: var(--fg-faint); }
.dep > svg { color: var(--fg-faint); flex: none; }
.dep.done > svg { color: var(--green); }

/*
 * Направление связи — плашкой перед ссылкой, как в Asana: «Ожидает»
 * янтарным, «Блокирует» красным. Это выпадающий список: нажатие меняет
 * направление, не снимая связь. Раньше направление задавалось заголовком
 * группы серым по серому и читалось в последнюю очередь.
 */
.dep-dir {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  padding: 2px 6px; border-radius: 999px; border: 1px solid transparent;
  font-size: 12px; font-weight: 600; line-height: 1.5; white-space: nowrap;
}
.dep-dir.blocked { background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber-fg); }
.dep-dir.blocking { background: var(--red-soft); border-color: var(--red-line); color: var(--red-fg); }
.dep-dir.settled { background: var(--bg-muted); border-color: var(--border); color: var(--fg-muted); }
.dep-dir:hover { filter: brightness(.97); }
.dep-dir svg:last-child { opacity: .6; }

/*
 * Тип связи — второй плашкой, кодом из двух букв: FS, SS, FF, SF. Цветом не
 * выделяется, цвет занят направлением; здесь важно только, что это тоже
 * выпадающий список, а не подпись.
 */
.dep-kind {
  display: inline-flex; align-items: center; gap: 2px; flex: none;
  padding: 2px 5px; border-radius: var(--radius);
  border: 1px solid var(--border); background: var(--surface);
  color: var(--fg-muted); font-size: 11.5px; font-weight: 600;
  letter-spacing: .02em; line-height: 1.5;
}
.dep-kind:hover { border-color: var(--border-strong); color: var(--fg); }
.dep-kind svg { opacity: .6; }
.dep .link-btn { color: var(--fg); }
.dep .link-btn:hover { color: var(--accent); }
.dep.done .link-btn { color: var(--fg-faint); }
.dep .f-del { margin-left: auto; }

/*
 * Упоминание — обычный текст на подложке, как в Asana: цветное имя посреди
 * реплики читается как ошибка или ссылка, а подложка говорит то же самое
 * спокойно.
 */
.mention {
  color: var(--fg); background: var(--mention-bg);
  border-radius: 4px; padding: 0 4px; font-weight: 500;
}
/* Упоминание ведёт в профиль, но остаётся плашкой: подчёркивание внутри
   текста комментария читалось бы ссылкой на задачу. */
/* Ссылка на профиль, но не ссылочного цвета: внутри текста комментария
   упоминание — имя на подложке (правило ниже общего «.rich a»). */
a.mention, .rich a.mention, .c-text a.mention, .rich-input a.mention { text-decoration: none; cursor: pointer; color: var(--fg); }
a.mention:hover, .rich a.mention:hover { background: var(--mention-line); color: var(--fg); text-decoration: none; }
.c-text { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
/*
 * A long comment folded to its first lines (comment-fold.js sets the
 * height): the last line kept fades out, the spoilers wait, and the blue
 * «Показать больше» under it opens all of it.
 */
.c-text > .rich.c-folded { overflow: hidden; }
.c-text > .rich.c-folded .spoiler { display: none; }
.c-text > .rich.c-cut {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 20px), transparent);
  mask-image: linear-gradient(to bottom, #000 calc(100% - 20px), transparent);
}
.c-fold { display: flex; width: fit-content; margin: 0 -6px -2px; font-size: 13px; font-weight: 500; }

/* Задача, которая кого-то ждёт: видно прямо в строке списка. */
.row-flags { display: inline-flex; align-items: center; gap: 5px; color: var(--fg-faint); }
.row-flags .blocked { color: var(--amber); }

/* Таблица шире экрана прокручивается вбок сама, не растягивая страницу. */
/*
 * Боком таблица прокручивается вместе с областью содержимого, а не внутри
 * своей обёртки. Обёртка со своим overflow-x становилась контейнером
 * прокрутки, и липкий заголовок колонок держался за неё — то есть уезжал
 * вверх вместе со страницей, потому что по вертикали она не прокручивается.
 * Без своего overflow заголовок липнет к области содержимого, как и
 * задумано, а вбок едет вся таблица целиком.
 */
.tv-scroll { overscroll-behavior-x: contain; }
.tv-scroll > .tv { min-width: min-content; }
/*
 * Шапка проекта и панель кончались по правому краю экрана, а таблица шире:
 * стоило отъехать вбок — и за ними открывалась дыра, сквозь которую были
 * видны строки. Закрепить их на месте нельзя: липкий блок двигается только
 * внутри своего родителя, а он ровно с экран шириной, — сдвигать некуда.
 * Поэтому они тянутся во всю ширину таблицы: область содержимого становится
 * сеткой в одну колонку, колонка берёт ширину самого широкого в ней —
 * таблицы, — и все блоки растягиваются до неё.
 */
.view:has(.tv-scroll) { display: grid; grid-template-columns: min-content; align-content: start; }
.view:has(.tv-scroll) > * { min-width: 100%; }
/*
 * Moving sideways, the head, the bar and the section names stay where they
 * are and so does the task's name, as in Asana: they are as wide as the
 * visible area and stick to its left edge; the grid keeps its width for
 * the table alone. The name cell reaches into the row's left padding, so
 * that stuck at the edge it keeps its indent and covers what slides under.
 */
@media (min-width: 861px) {
  .view:has(.tv-scroll) > .project-head,
  .view:has(.tv-scroll) .toolbar,
  .view:has(.tv-scroll) .tv-section-head {
    position: sticky; left: 0; width: var(--view-w, 100%); min-width: 0; box-sizing: border-box;
  }
  .view:has(.tv-scroll) .tv-section-head { z-index: 2; }
  .tv-head > .th:first-child,
  .tv-row > .tv-cell:first-child,
  .tv-add > .tv-cell:first-child {
    position: sticky; left: 0; z-index: 2; align-self: stretch;
    margin-left: -32px; padding-left: 32px; background: inherit;
  }
  .tv-head > .th:first-child { background: var(--surface); }
  .tv-row.sub > .tv-cell:first-child, .tv-add.sub > .tv-cell:first-child {
    padding-left: calc(32px + var(--depth, 1) * 26px);
  }
  .tv-row.picked > .tv-cell:first-child { box-shadow: inset 2px 0 0 var(--pick-line); }
  /*
   * Where the stuck name ends: once the columns slide under it, a line and
   * a soft shadow at its right edge. Without them the column under it ran
   * on as if nothing covered it, and one could not tell where the name ended.
   */
  .scrolled-x .tv-head > .th:first-child::after,
  .scrolled-x .tv-row > .tv-cell:first-child::after,
  .scrolled-x .tv-add > .tv-cell:first-child::after {
    content: ''; position: absolute; top: 0; bottom: 0; right: -1px; width: 1px;
    background: var(--border-strong); box-shadow: 3px 0 6px rgba(0, 0, 0, .18); pointer-events: none;
  }
}

/*
 * A list under a fixed head: the project's name and tabs (or the head and
 * bar of My tasks) stay put, and only the list scrolls, both ways, inside
 * .fill-scroll. The scrollbar starts where the list does, and a row dragged
 * to the top edge of the list scrolls it, not the edge of the page under the
 * head. scrollerOf() (dom.js) names the element that scrolls.
 */
.view:has(> .fill-scroll) { display: flex; flex-direction: column; overflow: hidden; }
.view:has(> .fill-scroll) > * { flex: none; min-width: 0; }
.view > .fill-scroll {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  display: grid; grid-template-columns: min-content; align-content: start;
}
.view > .fill-scroll > *:not(.bulk-bar) { min-width: 100%; }
/* The bulk bar is fixed to the bottom of the screen; stretched with the rest it lay full-width at the left. */
.fill-scroll .tv-head { top: 0; }

/* Проекты задачи: их может быть несколько, у каждого своя секция. */
.proj-list { display: flex; flex-direction: column; gap: 2px; }
.proj-row { display: flex; align-items: center; gap: 6px; min-width: 0; padding: 2px 0; }
.proj-row .pill.project { border: 0; cursor: pointer; }
.proj-row .pill.project:hover { text-decoration: underline; }
/*
 * The project's square with its icon, centred on the name. inline-flex is
 * safe only here, where the chip is a flex item of its row: elsewhere the
 * pill stands inline in text, and inline-flex would move its baseline.
 */
.proj-row .pill.project.marked { display: inline-flex; align-items: center; gap: 6px; padding-left: 2px; }
/* In the card the square stands on the value edge, under the assignee's avatar. */
.props-grid .proj-row .pill.project.marked { padding-left: var(--value-inset); }
.pill.project.marked::before { content: none; }
/*
 * A long project name gives way, cut with an ellipsis, in a narrow card (the
 * inbox leaves its list 540px): the chip kept its whole width, and the
 * section or «от родителя» beside it went past the card's edge.
 */
.proj-row .pill.project { min-width: 0; }
.proj-row .pill.project > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.proj-row :is(.sep, .proj-inherited, .home-mark, .f-del) { flex: none; }
.proj-row .sep { color: var(--fg-faint); }
.proj-row .section-pick { width: auto; max-width: 180px; }
.proj-row .home-mark { color: var(--fg-faint); display: flex; }
.proj-row .f-del { margin-left: auto; opacity: 0; }
.proj-row:hover .f-del { opacity: 1; }

/* Строки одинаковой высоты: иначе окно отрисовки не сможет считать позиции.
   Высота одна и та же для стилей и для расчёта окна — отсюда переменная. */
.tv-row { height: var(--row-h); box-sizing: border-box; }
/* Высота задана по числу задач, видимый кусок сдвигается внутри. */
.tv-virtual { position: relative; }
.tv-window { position: absolute; left: 0; right: 0; top: 0; }

/* ==========================================================================
   Узкий экран
   ========================================================================== */

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border: 0; background: transparent;
  color: var(--fg-muted); border-radius: var(--radius);
}
/* After .icon-btn: the menu button is one too, and its display won — the
   hamburger that did nothing on a wide screen. */
.menu-btn, .scrim { display: none; }
.icon-btn:hover { background: var(--bg-muted); color: var(--fg); }

@media (max-width: 860px) {
  /* Боковая панель выезжает поверх содержимого: постоянно держать её негде,
     а без неё не остаётся вообще никакой навигации. */
  .sidebar {
    position: fixed; z-index: 45; top: 0; bottom: 0; left: 0;
    transform: translateX(-100%); transition: transform .18s ease-out;
    box-shadow: 4px 0 24px rgba(0, 0, 0, .25);
  }
  body.sidebar-open .sidebar { transform: none; }
  body.sidebar-open .scrim {
    display: block; position: fixed; inset: 0; z-index: 44;
    background: rgba(0, 0, 0, .38);
  }
  /* Menus come up over the slid-out panel: "Create" and a project's menu
     opened from it lay under it, out of sight. Still below toasts and dialogs. */
  .popover { z-index: 46; }
  .menu-btn { display: inline-flex; }
  /* On a phone the bar stays: it holds the button that opens the panel. */
  :root { --topbar-h: 48px; }
  .topbar {
    position: static; height: var(--topbar-h); flex: 0 0 var(--topbar-h); padding: 0 10px; gap: 6px;
    border-bottom: 1px solid var(--border); background: var(--surface);
  }
  .topbar-brand { display: inline; font-weight: 600; }
  .sidebar-fold { display: none; }

  .page-head, .toolbar, .section-head, .board, .tiles, .cards-grid, .table-wrap {
    padding-left: 16px; padding-right: 16px;
  }
  /* Четыре плитки столбом занимали весь первый экран; парами — треть. */
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .tile .v { font-size: 22px; }
  .page-title h1 { font-size: 19px; }
  .project-head { padding: 14px 16px 0; }
  .project-head h1 { font-size: 17px; }
  /* Заголовок и кнопки не переносятся внутри себя: иначе шапка съедает
     треть экрана. Не поместившееся уезжает по горизонтали. */
  .project-head .row { flex-wrap: nowrap; overflow-x: auto; }
  .project-head .row > *, .project-title-line > * { flex: 0 0 auto; }
  /* Заголовку отдаём большую часть ширины, остальное уезжает вбок. */
  .project-head .row h1 { flex: 0 0 auto; max-width: 62vw; overflow: clip visible; min-width: 0;
    text-overflow: ellipsis; white-space: nowrap; }
  .tabs-row, .toolbar { overflow-x: auto; flex-wrap: nowrap; }
  .tabs-row::-webkit-scrollbar, .toolbar::-webkit-scrollbar { display: none; }
  .tabs-row .tab, .toolbar .tool-btn, .toolbar .btn { flex: 0 0 auto; white-space: nowrap; }
  .toolbar .spacer, .toolbar .saving { display: none; }
  /* Except a load that stopped: on a phone the notice goes in seconds, and this is the only way on left. */
  .toolbar .saving.load-stalled { display: inline-flex; }
  /*
   * In a project the same notice stands under the tabs: at the end of the
   * scrolling row it sat off the screen, and the list looked whole once
   * the toast had gone (setViewTools in views/project.js).
   */
  .view-notes:not(:empty) { display: flex; padding: 6px 0 2px; }
  .view-notes .load-stalled { display: inline-flex; align-items: baseline; gap: 6px; color: var(--fg-muted); white-space: normal; }
  .view-notes .load-stalled .text-btn { font-size: inherit; }
  .view-tools .load-stalled { display: none; }

  /* Таблица из шести колонок на телефоне нечитаема, поэтому строка
     превращается в карточку: название сверху, остальное — строкой под ним. */
  /* Базовое правило не даёт таблице сжиматься уже содержимого — на узком
     экране это как раз и уводит строку за край. */
  .tv-scroll { overflow-x: visible; }
  .tv-scroll > .tv { min-width: 0; }
  /* Таблица здесь в ширину экрана, тянуть шапку не за чем: сетка только
     заставила бы область содержимого ехать вбок вслед за строками.
     The grid is dropped on the scroller itself, not on the view: a view
     turned into a block kept its overflow: hidden, the list lost the
     height it scrolls within, and on a phone nothing past the first ten
     rows could be reached. */
  .view:has(.tv-scroll):not(:has(> .fill-scroll)) { display: block; }
  .view > .fill-scroll:has(.tv-scroll) { display: block; }
  .tv, .tv-section, .tv-body, .tv-virtual { min-width: 0; max-width: 100%; }
  .tv-head { display: none; }
  .view-tools .phone-only { display: inline-flex; }
  .tv { --row-h: 64px; }
  .tv-row {
    display: flex; flex-wrap: wrap; align-content: center; gap: 2px 8px;
    padding: 6px 16px; overflow: hidden;   /* высота фиксирована: её знает окно отрисовки */
  }
  .tv-row .tv-cell { border-left: 0; padding: 0; min-height: 0; }
  .tv-row .tv-cell:first-child { flex: 1 0 100%; min-width: 0; }
  /* Строка — ровно две полосы: название и под ним исполнитель со сроком.
     Остальные свойства увели бы её на третью полосу и сломали расчёт окна
     отрисовки; они никуда не деваются — их видно в карточке. */
  .tv-row .tv-cell:nth-child(n+4) { display: none; }
  /* The second band holds the second and third cells together. In "My
     tasks", the search and a profile the second is a project's pill, which
     took the whole band for a long name: the due date went to a third band
     the row has no height for, cut off and read as the next task's (audit
     4, area 7, С6). The pill gives way and ends in an ellipsis. */
  .tv-row .tv-cell:nth-child(2) { flex: 0 1 auto; min-width: 0; max-width: 55%; overflow: hidden; }
  .tv-row .tv-cell:nth-child(3) { flex: 0 0 auto; }
  .tv-row .cell-btn { width: auto; padding: 2px 6px; min-height: 26px; font-size: 12px; }
  .tv-row .cell-btn.empty { display: none; }   /* пустые свойства на телефоне только шумят */
  .tv-add { display: flex; padding: 8px 16px; }
  .tv-add .tv-cell:not(:first-child) { display: none; }
  .tv-section-head { padding-left: 16px; padding-right: 16px; }
  .row-actions, .row-grip { display: none; }   /* наводить нечем, действия — в карточке */

  /* Карточка задачи занимает весь экран: боковая панель шириной с телефон
     оставляет нечитаемые обрывки строк. */
  .drawer { width: 100%; max-width: none; }
  /* Under the bar, not over it: the bar holds the menu button, the only way
     to the panel and its search, and a card over it left no way there but
     closing the card first. */
  .drawer, .drawer-backdrop { top: var(--topbar-h); }
  .props-grid { grid-template-columns: 1fr; gap: 0 0; }
  /* Labels stand above their values here, so there is no value edge to meet. */
  .fields-rows { grid-template-columns: 148px 1fr; }
  .props-grid .k { margin-top: 10px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em; }
  /* Here a label stands above its value, not beside it: every label keeps the same band. */
  .props-grid > .k.lines { min-height: 38px; }

  .board { flex-direction: column; }
  .column { flex: 1 1 auto; width: 100%; }
}

/* ---------------------------------------------------- массовые действия --- */

/* Отметка выбора: не мозолит глаза, пока выбор не начат. */
/* Выделение. Это не предупреждение и не ошибка, поэтому оно синее, а не
   цвета акцента: красная заливка с рамкой у каждой строки читалась как сбой.
   Обводим ряд целиком — линия сверху у первой выделенной и снизу у последней, —
   поэтому несколько выделенных подряд выглядят одним блоком, а не лестницей. */
.tv-row.picked { background: var(--pick-soft); }
/* The task open on the card (drawer.js markOpenRow), as Asana marks it. */
.tv-row.open-in-card, .tv-row.open-in-card:hover { background: var(--pick-soft); }
.tv-row.open-in-card > .tv-cell:first-child { box-shadow: inset 3px 0 0 var(--accent); }
.card.open-in-card { background: var(--pick-soft); box-shadow: inset 3px 0 0 var(--accent); }
.card-sub.open-in-card { background: var(--pick-soft); }
.tv-row.picked:hover { background: var(--pick-hover); }
.tv-row.picked { box-shadow: inset 2px 0 0 var(--pick-line); }
.tv-row.picked { border-top-color: var(--pick-line); }
.tv-row.picked + .tv-row.picked { border-top-color: var(--pick-soft); }
.tv-row.picked:last-child, .tv-row.picked:has(+ .tv-row:not(.picked)) {
  box-shadow: inset 2px 0 0 var(--pick-line), inset 0 -1px 0 var(--pick-line);
}

/* Панель выделенного. Раньше она была полосой поверх шапки таблицы и
   сдвигала список вниз, стоило выделить одну строку. Теперь это плавающая
   пилюля у нижнего края: она не трогает вёрстку и не спорит с заголовками
   колонок, а руки всё равно рядом — выделяют мышью, а не с клавиатуры. */
.bulk-bar {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
  /* Пилюля не должна закрывать список, ради которого её открыли: редкие
     действия ушли под «…», а ширина ограничена узкой полосой посередине. */
  z-index: 40; max-width: min(620px, calc(100vw - 48px));
  display: flex; align-items: center; gap: 1px; flex-wrap: nowrap;
  /*
   * Полоска, а не брусок: в Asana она тонкая и не спорит со строками списка.
   * Высота задана числом, а не сложена из отступов: так её не может раздуть
   * ни выросший значок, ни чужая строчная высота, ни длинное название поля,
   * ушедшее на вторую строку.
   */
  height: 36px; box-sizing: border-box;
  padding: 0 6px 0 14px; border-radius: 999px;
  background: #22242a; color: #fff;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .24), 0 1px 3px rgba(0, 0, 0, .16);
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
}
/* Ничто внутри не тянется и не сжимается: панель — ряд, а не сетка. */
.bulk-bar > * { flex: 0 0 auto; align-self: center; }
.bulk-bar::-webkit-scrollbar { display: none; }
/* Пока карточка открыта, пилюля держится над списком, а не под карточкой. */
body.drawer-open .bulk-bar { left: calc((100vw - var(--drawer-w)) / 2); }
@media (max-width: 1100px) {
  body.drawer-open .bulk-bar { left: 50%; }
}
/*
 * The bar over subtasks picked in the task card (iteration 17, item 8)
 * stands over the card, just above its foot whatever the foot's height:
 * at the screen's bottom it covered the comment field. The screen's own
 * bar gives way to it meanwhile, so one bar shows at a time.
 */
.drawer-foot { position: relative; }
.drawer-foot > .bulk-bar.for-card {
  position: absolute; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%);
  max-width: calc(100% - 24px);
}
body:has(.drawer.open .bulk-bar.for-card) .bulk-bar:not(.for-card) { display: none; }
.bulk-bar .count { font-weight: 600; font-size: 12.5px; line-height: 1; white-space: nowrap; }
/* Сумма оценок: рядом со счётчиком, но тише его — это справка, а не заголовок. */
.bulk-bar .count.sum {
  font-weight: 500; font-size: 12px; line-height: 1; color: rgba(255, 255, 255, .62);
  font-variant-numeric: tabular-nums; cursor: default; margin-left: 4px;
  /* Длинное название поля не должно распирать пилюлю на весь экран. */
  max-width: 190px; overflow: hidden; text-overflow: ellipsis;
}
.bulk-bar .count.sum.empty { opacity: .55; }
.bulk-bar .sep { width: 1px; height: 16px; background: rgba(255, 255, 255, .22); margin: 0 5px; }
.bulk-bar .spacer { flex: 1; }
.bulk-btn {
  display: inline-flex; align-items: center; gap: 6px;
  border: 0; background: transparent; color: #fff;
  padding: 5px 10px; border-radius: 999px; font-size: 12.5px; line-height: 1; white-space: nowrap;
}
.bulk-btn:hover { background: rgba(255, 255, 255, .16); }
/* Значок без подписи: действия в пилюле — как в Asana, подпись в подсказке. */
.bulk-btn.icon {
  width: 28px; height: 28px; padding: 0; justify-content: center;
  border-radius: 50%; color: rgba(255, 255, 255, .85);
}
.bulk-btn.icon:hover { color: #fff; }
.bulk-btn.danger:hover { background: var(--red); border-color: var(--red); }

@media (max-width: 860px) {
  .bulk-bar { padding: 0 6px 0 14px; overflow-x: auto; flex-wrap: nowrap; }
  .bulk-bar .bulk-btn { flex: 0 0 auto; }
  }

/* ------------------------------------------------------------ разметка --- */

.rich { line-height: 1.55; }
.rich p { margin: 0 0 8px; }
.rich p:last-child { margin-bottom: 0; }
/* Headings of text in tasks and comments: modest, in three steps. The
   same in the editor, so a heading does not change size on edit. */
.rich :is(h1, h2, h3, h4), .rich-input :is(h1, h2, h3, h4) { margin: 12px 0 6px; font-weight: 600; line-height: 1.35; }
.rich h1, .rich-input h1 { font-size: 16px; }
.rich h2, .rich-input h2 { font-size: 15px; }
.rich :is(h3, h4), .rich-input :is(h3, h4) { font-size: 14px; }
.rich :is(h1, h2, h3, h4):first-child, .rich-input :is(h1, h2, h3, h4):first-child { margin-top: 0; }
.rich ul, .rich ol { margin: 0 0 8px; padding-left: 22px; }
.rich li { margin: 2px 0; }
/*
 * Levels of a list, read and edited alike. A nested list sits tight under
 * its item: with the outer list's bottom margin it left a gap before the
 * next outer item. A numbered level goes 1. a. i. and round again, as in
 * Google Docs (the owner's choice); the level counts every list above,
 * bulleted ones too. Only the look: the text keeps "1." on every level,
 * and the letters (richToHtml in server/mail-templates.js) say the same.
 * Bullets stay the browser's ● ○ ■.
 */
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol), .desc .rich-input :is(ul, ol) :is(ul, ol) { margin: 0; }
:is(.rich, .rich-input) :is(ul, ol) ol { list-style-type: lower-alpha; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) ol { list-style-type: lower-roman; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: decimal; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: lower-alpha; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: lower-roman; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: decimal; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: lower-alpha; }
:is(.rich, .rich-input) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) :is(ul, ol) ol { list-style-type: lower-roman; }
/*
 * Inline code that wraps keeps one background: no frame (a frame is drawn
 * at the top and bottom of every line it wraps to) and padding tall enough
 * for the line pitch, so the pieces of its lines meet. With 1px padding and
 * a frame each line was a box of its own, 3px apart: a long log line read
 * as a striped vest (iteration 17, item 15, image19). Spaces wrap rather
 * than hang past the box's end.
 */
.rich code {
  background: var(--bg-muted); border-radius: 4px; padding: 3.5px 4px; font-size: 12.5px;
  white-space: break-spaces;
}
.rich blockquote {
  margin: 0 0 8px; padding: 2px 0 2px 11px;
  border-left: 3px solid var(--border-strong); color: var(--fg-muted);
}
/* Ссылка в тексте — синяя, как ссылки везде: красный здесь читался тревогой. */
.rich a { color: var(--blue); }
.rich a:hover { text-decoration: underline; }

/* Описание правится по щелчку: пока не трогают — это текст, а не поле ввода. */
.rich-view {
  padding: 9px 11px; border: 1px solid transparent; border-radius: var(--radius);
  cursor: text; min-height: 40px;
}
.rich-view:hover { border-color: var(--border); }
/*
 * The description's editor stands exactly where its text did: the same
 * spacing as the text read, no height limit of its own, and the toolbar
 * over the heading line instead of above the text. A click into the text
 * pushed it down by the toolbar's height, away from the pointer.
 */
.desc { position: relative; }
/* Two versions of the description, the person's own in the editor below
   and a colleague's saved meanwhile (drawer.js, С2 of audit 4, area 7). */
.desc-clash {
  margin-bottom: 10px; padding: 10px 12px; border: 1px solid var(--amber-line);
  background: var(--amber-soft); border-radius: var(--radius); font-size: 13px;
}
.desc-clash-head { color: var(--amber-fg); font-weight: 500; margin-bottom: 8px; }
.desc-clash-label { color: var(--fg-muted); font-size: 12px; margin-bottom: 4px; }
.desc-clash-text {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 8px 10px; max-height: 220px; overflow: auto;
}
.desc-clash-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.desc .rich-input { line-height: 1.55; max-height: none; }
/*
 * An empty description is two lines to click into and nothing else, read or
 * edited alike: two lines of 1.55, 9px of padding and a 1px border on each
 * side. The hint that stood there was 76px of grey text.
 */
.desc :is(.rich-view.empty, .rich-input:empty) { min-height: calc(3.1em + 20px); }
.desc .rich-input p { margin: 0 0 8px; }
.desc .rich-input p:last-child { margin-bottom: 0; }
/* Lists and quotes with the spacing of the text read (.rich): the editor's
   own put a list 2px lower, and a click into it made the text jump. */
.desc .rich-input ul, .desc .rich-input ol { margin: 0 0 8px; }
.desc .rich-input blockquote { margin: 0 0 8px; padding: 2px 0 2px 11px; }
/*
 * The text read keeps what the editor keeps: spaces as typed and long links
 * broken anywhere, and inline code drawn one way in both. Read, two spaces
 * folded into one and a link wrapped at another place, so a click into the
 * text moved its words sideways.
 */
.desc .rich-view .rich { white-space: pre-wrap; word-break: break-word; }
.desc .rich code, .desc .rich-input code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  background: var(--bg-muted); border-radius: 4px; padding: 3.5px 4px; white-space: break-spaces;
}
.desc .markup-bar {
  position: absolute; bottom: 100%; right: 0; margin: 0 0 4px;
  background: var(--surface); padding-left: 8px; flex-wrap: wrap; justify-content: flex-end; max-width: calc(100% - 90px);
}
.desc .markup-bar + .rich-input { border-radius: var(--radius); }

.markup-bar { display: flex; gap: 2px; margin-bottom: 6px; }
.markup-btn {
  min-width: 28px; height: 26px; border: 1px solid var(--border);
  background: var(--surface); border-radius: 5px; color: var(--fg-muted); font-size: 12.5px;
  /* Centred as a box, so the link's icon sits where the letters do. */
  display: inline-flex; align-items: center; justify-content: center;
}
.markup-btn:hover { border-color: var(--border-strong); color: var(--fg); background: var(--bg-muted); }
/* Включённая пометка подсвечена: видно, чем пишется то, что набирают. */
.markup-btn.on {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.markup-btn.on:hover { background: var(--accent-soft); color: var(--accent); }
/* «@» стоит чуть особняком: это не пометка текста, а вызов списка людей. */
.mention-btn { margin-left: 6px; font-weight: 600; }
/* Задача, упомянутая по названию: читается как текст, ведёт как ссылка. */
.task-link { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; }
.task-link:hover { text-decoration-thickness: 2px; }

/* Вехи, повторения и связи на хронологии. */
.tl-links { position: absolute; inset: 0; pointer-events: none; color: var(--fg-faint); }
.tl-links svg { position: absolute; left: 0; top: 0; overflow: visible; }
.tl-links path { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-dasharray: 3 3; }
.tl-milestone {
  position: absolute; top: 50%; width: 13px; height: 13px;
  transform: translate(-50%, -50%) rotate(45deg);
  background: var(--violet); border-radius: 2px; cursor: pointer;
}
.tl-milestone.done { opacity: .45; }
.tl-milestone.overdue { background: var(--red); }
.timeline { position: relative; }

/* ------------------------------------------------- время отрисовки --- */

.perf { position: relative; }
.perf-badge {
  display: inline-flex; align-items: center; gap: 5px;
  border: 1px solid var(--border); background: var(--surface); color: var(--fg-faint);
  border-radius: 20px; padding: 4px 10px; font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
.perf-badge:hover { border-color: var(--border-strong); color: var(--fg); }
.perf-badge.slow { color: var(--red); border-color: var(--red); }

.perf-panel {
  position: fixed; right: 16px; top: 60px; z-index: 50;
  width: 420px; max-height: 70vh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .16); padding: 12px;
}
.perf-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.perf-page { border-top: 1px solid var(--border); padding: 9px 0; }
.perf-page:first-of-type { border-top: 0; padding-top: 0; }
.perf-page.current .perf-route { color: var(--fg); font-weight: 600; }
.perf-route { font-size: 12.5px; color: var(--fg-muted); margin-bottom: 6px; word-break: break-all; }
.perf-bars { display: flex; flex-direction: column; gap: 3px; }
.perf-bar { display: grid; grid-template-columns: 68px 1fr 62px; gap: 7px; align-items: center; font-size: 11.5px; }
.perf-bar .n { color: var(--fg-muted); }
.perf-bar .track { height: 6px; background: var(--bg-muted); border-radius: 3px; overflow: hidden; }
.perf-bar .track i { display: block; height: 100%; background: var(--accent); }
.perf-bar .v { text-align: right; color: var(--fg-faint); font-variant-numeric: tabular-nums; }
.perf-sum { font-size: 11.5px; color: var(--fg-faint); margin-top: 5px; }
.perf-expand {
  border: 0; background: transparent; color: var(--accent); font-size: 11.5px;
  padding: 3px 0; display: inline-flex; align-items: center; gap: 4px;
}
.perf-expand:hover { text-decoration: underline; }
.perf-req {
  display: grid; grid-template-columns: 46px 1fr 62px; gap: 6px; width: 100%;
  border: 0; background: transparent; text-align: left; font-size: 11.5px;
  padding: 3px 0; color: var(--fg-muted);
}
.perf-req:hover { color: var(--fg); }
.perf-req .m { color: var(--fg-faint); }
.perf-req .p { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.perf-req .v { text-align: right; font-variant-numeric: tabular-nums; }
.perf-req-sub { font-size: 11px; color: var(--fg-faint); padding-left: 52px; }
.perf-req-sub.hint { font-style: italic; }
.perf-query { display: flex; gap: 7px; padding: 3px 0 3px 52px; font-size: 11px; align-items: baseline; }
.perf-query .v { color: var(--fg-faint); flex: 0 0 52px; text-align: right; font-variant-numeric: tabular-nums; }
.perf-query code { color: var(--fg-muted); word-break: break-all; font-size: 10.5px; }

@media (max-width: 860px) {
  .perf-badge span { display: none; }
  .perf-panel { width: calc(100vw - 24px); right: -8px; }
}

/* ------------------------------------------------------------ профиль --- */

.profile-head { display: flex; align-items: center; gap: 16px; padding: 22px 32px 6px; }
.profile-head .who h1 { margin: 0 0 3px; font-size: 22px; font-weight: 600; letter-spacing: -.3px; }
.profile-head .mail { margin: 0 0 7px; color: var(--fg-muted); font-size: 13px; }
.profile-head .roles { display: flex; gap: 6px; }
.avatar.xl { width: 62px; height: 62px; font-size: 22px; }

.profile-section { padding: 18px 32px 0; }
/* The profile's tabs, under the name as a project's are under its title. */
.profile-tabs { padding: 0 32px; margin-top: 10px; }
.tab-count { margin-left: 4px; padding: 0 6px; border-radius: 9px; background: var(--bg-muted); color: var(--fg-muted); font-size: 11.5px; font-weight: 500; }
.profile-more { margin-left: auto; font-size: 12.5px; font-weight: 400; }
.profile-tiles { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }
.tile-link { text-align: left; font: inherit; color: inherit; cursor: pointer; }
.tile-link:hover { border-color: var(--border-strong); }
.settings-head.embedded { padding-top: 18px; }
.settings-head.embedded h2 { margin: 0 0 6px; font-size: 16px; font-weight: 600; }
@media (max-width: 860px) { .profile-tabs { padding: 0 16px; } }
.profile-section > h3 {
  margin: 0 0 10px; font-size: 14px; font-weight: 600;
  display: flex; align-items: center; gap: 8px;
}
.profile-section > h3 .n { color: var(--fg-faint); font-weight: 400; font-size: 12.5px; }
.profile-section .tv-scroll { margin: 0 -32px; }
/* Заголовок раздела уже стоит выше — внутренний в таблице его дублирует. */
.profile-section .tv-section-head { display: none; }
.profile-section .tv { padding-bottom: 0; }

.proj-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.proj-chips .pill.project { border: 0; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.proj-chips .pill.project:hover { text-decoration: underline; }
.proj-chips .pill .n {
  background: rgba(0, 0, 0, .12); border-radius: 9px; padding: 0 5px; font-size: 10.5px;
}

.tile .v.red { color: var(--red); }
.tile .v.green { color: var(--green); }

.props-grid .assignee-row { display: flex; align-items: center; gap: 4px; }
/* "Open the profile" had no style of its own and showed as the browser's grey
   default button. */
.props-grid .assignee-row .f-del {
  flex: none; display: inline-grid; place-items: center; width: 26px; height: 26px; padding: 0;
  border: 0; border-radius: 6px; background: transparent; color: var(--fg-faint); cursor: pointer;
}
.props-grid .assignee-row .f-del:hover { background: var(--bg-muted); color: var(--fg); }
/* Кнопка по ширине имени, а не всей колонки: нажимают по исполнителю. */
.props-grid .assignee-row .cell-btn { flex: 0 1 auto; min-width: 0; }

/* Поля ввода без своих цветов берут системные — в тёмной теме это белые
   прямоугольники с чёрным текстом. */
input, textarea, select, button { color: inherit; }
input[type=text], input[type=search], input[type=password], input[type=date],
input[type=number], input[type=email], textarea, select {
  background: var(--surface); color: var(--fg);
}
/*
 * A name's in-place field is the name itself, not a box: the rule above
 * outweighs .title-edit, and painted a white (dark, in the dark theme) box
 * in the plain text colour over the row under the pointer, the hover
 * "highlight" of a completed task (iteration 17, item 20). Its colour and
 * weight come from the name it replaces (inline-title.js).
 */
input.title-edit, textarea.title-edit { background: transparent; color: inherit; }

/* ==========================================================================
   Тёмная тема
   ==========================================================================
   Значения переопределяются на тех же токенах, поэтому правила ниже по файлу
   трогать не нужно. Белый текст на цветном (кнопка, отметка, аватар) остаётся
   белым намеренно: фон под ним не меняется.

   Включается двумя путями: выбором в приложении (data-theme) и системной
   настройкой. Системную перекрывает явный выбор — иначе человек не смог бы
   остаться в светлой теме на тёмной системе. */

:root[data-theme="dark"] {
  --sidebar-bg: #17181a;
  --sidebar-fg: #b9bcc1;
  --sidebar-fg-dim: #7d8188;
  --sidebar-hover: #232528;
  --sidebar-active: #2d3034;
  --sidebar-line: #232528;
  --sidebar-thumb: #3a3d42;

  --bg: #131416;
  --surface: #1b1c1f;
  --bg-muted: #232528;
  --section-fill: #202124;
  --border: #2c2e33;
  --border-soft: #26282c;
  --border-strong: #3d4046;

  --fg: #e8e9eb;
  --fg-muted: #a0a4ab;
  --fg-faint: #767b83;
  --title-open: #f7f8fa;
  --title-done: #6a7079;

  --accent: var(--blue);
  --accent-hover: var(--primary-hover);
  --accent-soft: var(--blue-soft);

  --green: #3cb98a;
  --amber: #e0a94a;
  --red: #e4645a;
  --red-soft: #3b2220;
  --red-fg: #f09a93;
  --red-line: #5a2f2b;
  --violet: #9d86dd;
  --blue: #6d9aeb;
  /* Deeper than --blue: white text on the light dark-theme blue is unreadable. */
  --primary: #3b6cd0;
  --primary-hover: #4a7ade;
  --pick-soft: #1e2a3d;
  --pick-hover: #24334a;
  --pick-line: #3f5f93;
  --blue-soft: #1e2733;
  --blue-line: #2f3f55;
  --mention-bg: #2f4467;
  --mention-line: #3f5a89;
  --amber-soft: #332818;
  --amber-fg: #e5b878;
  --amber-line: #4a3a1f;
  --green-soft: #1b2e26;
  --green-fg: #74d3aa;
  --green-line: #2d4c3d;

  --scroll-thumb: #3a3d42;
  --scroll-thumb-hover: #4a4e55;
  --toast-bg: #2c2e33;
  --toast-fg: #e8e9eb;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-md: 0 4px 14px rgba(0, 0, 0, .5);
  --shadow-lg: -8px 0 32px rgba(0, 0, 0, .6);

  color-scheme: dark;
}

/* Метки и плашки: на тёмном фоне светлые заливки слепят. */
:root[data-theme="dark"] .t-indigo { --chip-bg: #2e3152; --chip-fg: #aab1f2; }
:root[data-theme="dark"] .t-teal   { --chip-bg: #1b3e3c; --chip-fg: #6ed6d0; }
:root[data-theme="dark"] .t-amber  { --chip-bg: #43331d; --chip-fg: #efc484; }
:root[data-theme="dark"] .t-rose   { --chip-bg: #4d2b28; --chip-fg: #f5a89f; }
:root[data-theme="dark"] .t-violet { --chip-bg: #372a52; --chip-fg: #c3a9f0; }
:root[data-theme="dark"] .t-lime   { --chip-bg: #2b3d21; --chip-fg: #a2d477; }
:root[data-theme="dark"] .t-sky    { --chip-bg: #243448; --chip-fg: #8dbcef; }
:root[data-theme="dark"] .t-slate  { --chip-bg: #31353a; --chip-fg: #aeb6c0; }
:root[data-theme="dark"] .t-gold { --chip-bg: #45351a; --chip-fg: #f2c070; }
:root[data-theme="dark"] .t-yellow { --chip-bg: #3f3b17; --chip-fg: #e8da6a; }
:root[data-theme="dark"] .t-olive { --chip-bg: #353d1a; --chip-fg: #c5d66a; }
:root[data-theme="dark"] .t-emerald { --chip-bg: #1c3d30; --chip-fg: #6fd6a8; }
:root[data-theme="dark"] .t-magenta { --chip-bg: #452a45; --chip-fg: #e7a2e4; }
:root[data-theme="dark"] .t-fuchsia { --chip-bg: #4d2537; --chip-fg: #f59cc4; }
:root[data-theme="dark"] .t-pink { --chip-bg: #4a3036; --chip-fg: #f2b7c4; }

/* Системная настройка действует, пока человек не выбрал тему сам. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-theme="dark"]) {
    --sidebar-bg: #17181a;
    --sidebar-fg: #b9bcc1;
    --sidebar-fg-dim: #7d8188;
    --sidebar-hover: #232528;
    --sidebar-active: #2d3034;
    --sidebar-line: #232528;
    --sidebar-thumb: #3a3d42;
  
    --bg: #131416;
    --surface: #1b1c1f;
    --bg-muted: #232528;
    --section-fill: #202124;
    --border: #2c2e33;
    --border-soft: #26282c;
    --border-strong: #3d4046;
  
    --fg: #e8e9eb;
    --fg-muted: #a0a4ab;
    --fg-faint: #767b83;
    --title-open: #f7f8fa;
    --title-done: #6a7079;
  
    --accent: var(--blue);
    --accent-hover: var(--primary-hover);
    --accent-soft: var(--blue-soft);
  
    --green: #3cb98a;
    --amber: #e0a94a;
    --red: #e4645a;
    --red-soft: #3b2220;
    --red-fg: #f09a93;
    --red-line: #5a2f2b;
    --violet: #9d86dd;
    --blue: #6d9aeb;
  /* Deeper than --blue: white text on the light dark-theme blue is unreadable. */
  --primary: #3b6cd0;
  --primary-hover: #4a7ade;
  --pick-soft: #1e2a3d;
  --pick-hover: #24334a;
  --pick-line: #3f5f93;
    --blue-soft: #1e2733;
    --blue-line: #2f3f55;
    --mention-bg: #2f4467;
    --mention-line: #3f5a89;
    --amber-soft: #332818;
    --amber-fg: #e5b878;
    --amber-line: #4a3a1f;
    --green-soft: #1b2e26;
    --green-fg: #74d3aa;
    --green-line: #2d4c3d;
  
    --scroll-thumb: #3a3d42;
    --scroll-thumb-hover: #4a4e55;
    --toast-bg: #2c2e33;
    --toast-fg: #e8e9eb;
  
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, .4);
    --shadow-md: 0 4px 14px rgba(0, 0, 0, .5);
    --shadow-lg: -8px 0 32px rgba(0, 0, 0, .6);
  
    color-scheme: dark;
  }
  /* Метки и плашки: на тёмном фоне светлые заливки слепят. */
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-indigo { --chip-bg: #24263f; --chip-fg: #9ba3ee; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-teal   { --chip-bg: #16302f; --chip-fg: #5cc9c4; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-amber  { --chip-bg: #332818; --chip-fg: #e5b878; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-rose   { --chip-bg: #3b2220; --chip-fg: #f09a93; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-violet { --chip-bg: #2b2140; --chip-fg: #b79aea; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-lime   { --chip-bg: #212f1a; --chip-fg: #94c86a; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-sky    { --chip-bg: #1c2836; --chip-fg: #7fb0e5; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-slate  { --chip-bg: #26292d; --chip-fg: #a3abb5; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-gold { --chip-bg: #352914; --chip-fg: #e8b462; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-yellow { --chip-bg: #302d12; --chip-fg: #dccf5e; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-olive { --chip-bg: #282f14; --chip-fg: #b8ca5e; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-emerald { --chip-bg: #152f25; --chip-fg: #62c99b; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-magenta { --chip-bg: #342034; --chip-fg: #da95d7; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-fuchsia { --chip-bg: #3a1c2a; --chip-fg: #ea8fb8; }
  :root:not([data-theme="light"]):not([data-theme="dark"]) .t-pink { --chip-bg: #372428; --chip-fg: #e6aab7; }
}

/* --------------------------------------------- свёрнутая боковая панель --- */

/* Folded to icons: the top sections, analytics and favourites stay; the
   names, counts and the full list of projects go. */
@media (min-width: 861px) {
  body.sidebar-folded .sidebar { width: var(--rail-w); flex: 0 0 var(--rail-w); }
  body.sidebar-folded .sidebar-fold { left: calc(var(--rail-w) - 12px); }
  body.sidebar-folded .sidebar-fold .icon { transform: rotate(-90deg); }
  body.sidebar-folded .sidebar .nav-label,
  body.sidebar-folded .sidebar .count,
  body.sidebar-folded .sidebar .nav-projects,
  body.sidebar-folded .sidebar .search-key,
  body.sidebar-folded .sidebar .create-btn .create-label,
  body.sidebar-folded .sidebar .create-btn .icon:last-child { display: none; }
  /* A group's heading keeps its height and turns into a divider: the rows
     under it stay where they were. */
  body.sidebar-folded .sidebar .nav-group > * { visibility: hidden; }
  body.sidebar-folded .sidebar .nav-group:not(.nav-group-toggle) { pointer-events: none; }
  body.sidebar-folded .sidebar .nav-group:not(.nav-group-toggle)::after {
    content: ''; position: absolute; left: 10px; right: 10px; top: 24px; border-top: 1px solid var(--sidebar-line);
  }
  /* A group that folds stays as folded as in the wide panel; its heading
     keeps only the caret, in the column of the icons, to open it here too. */
  body.sidebar-folded .sidebar .nav-group-toggle > .icon {
    visibility: visible; position: absolute; left: 12px; top: 18px;
  }
  body.sidebar-folded .sidebar .nav-group-toggle:hover { background: var(--sidebar-hover); border-radius: var(--radius); }
  body.sidebar-folded .sidebar .nav-item { position: relative; }
  /* Unread becomes a dot on the icon. */
  body.sidebar-folded .sidebar .unread-badge:not(.hidden) {
    display: block; position: absolute; top: 6px; right: 10px; width: 8px; height: 8px; padding: 0;
    font-size: 0; border-radius: 50%; background: var(--accent);
  }
  body.sidebar-folded .sidebar .create-btn { overflow: hidden; }
  /*
   * The search is its magnifier; pressed, the field opens over the page in
   * the very place it has in the wide panel, as wide as there. Only the
   * field leaves the flow: its place in the column stays, so the icons
   * under it do not move up, and the magnifier stays where it was.
   */
  body.sidebar-folded .sidebar-search { border-radius: var(--radius); cursor: pointer; }
  body.sidebar-folded .sidebar-search:hover { background: var(--sidebar-hover); }
  body.sidebar-folded .sidebar-search input { opacity: 0; width: 100%; padding: 0; cursor: pointer; }
  body.sidebar-folded .sidebar-search:focus-within input {
    position: fixed; z-index: 70; top: var(--sidebar-head-h); left: 8px; width: calc(var(--sidebar-w) - 16px);
    opacity: 1; padding: 0 12px 0 34px; cursor: text; box-shadow: var(--shadow-md);
  }
  body.sidebar-folded .sidebar-search:focus-within > .icon { z-index: 71; color: var(--fg-faint); }
}

/* Карточка во весь экран: на широком мониторе боковая панель оставляет
   описание и обсуждение в узкой колонке. */
/* --- карточка в стиле панели Asana: значки под названием, подвал, участники --- */
.drawer-head { gap: 4px; padding: 10px 14px; }
.drawer-head .complete-btn { margin-right: 6px; }
.drawer-head .saving { margin-right: 6px; }
.drawer-head .like-btn { margin-right: 2px; }
/*
 * Where the card reaches the sidebar's edge (a wide card on a window under
 * ~1372px, see --drawer-max), the fold button, centred on that edge, lies
 * 12px into the card and stood 3px from «Отметить выполненной». There the
 * head moves its buttons right until 8px part them from the fold button:
 * its right edge, less the card's left edge, its 1px border and the head's
 * 14px, plus the 8px. Anywhere else the clamp() is 0 and the head keeps
 * its 14px. --card-w is the width the card is given (.drawer and
 * .drawer.wide).
 */
@media (min-width: 901px) {
  .drawer { --card-w: min(var(--drawer-w), var(--drawer-max)); }
  .drawer.wide { --card-w: var(--drawer-wide-w); }
  .drawer-head {
    padding-left: calc(14px + clamp(0px, var(--side-now) + 12px - (100vw - var(--card-w)) - 1px - 14px + 8px, 12px));
  }
}
/* On a phone the row ran past the edge and took "More" and "Close" with it.
   What goes is what the phone does not need: the card is full screen
   already, and the subtask field is right there in the card. */
@media (max-width: 480px) {
  .drawer-head .head-spare { display: none; }
  .drawer-head { gap: 2px; padding: 10px 8px; }
  .drawer-head .complete-btn { margin-right: 2px; }
}
/* Follows the title's 8px bottom margin: the row sits close under the name and keeps 10px from the fields. */
.task-badges { display: flex; flex-wrap: wrap; gap: 6px; margin: -2px 0 10px; }
.task-badge {
  display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border-radius: 999px;
  font-size: 12px; font-weight: 500; background: var(--bg-muted); color: var(--fg-muted); border: 1px solid var(--border);
}
/* Баг: плашка спокойная — тревожность и так задаёт значок в названии. */
.task-badge.bug { background: var(--bg-muted); }
.task-badge.milestone { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
/* Дубль: плашка-ссылка на первоисточник. Подчёркивания нет — в ряду
   значков оно читалось бы как ошибка правописания. */
a.task-badge.dup { text-decoration: none; max-width: 100%; }
a.task-badge.dup span, a.task-badge.dup { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.task-badge.dup:hover { border-color: var(--accent); color: var(--accent); }
.task-badge.approval.pending { background: var(--amber-soft); border-color: var(--amber-line); color: var(--amber-fg); }
.task-badge.approval.approved { background: var(--green-soft); border-color: var(--green-line); color: var(--green-fg); }
.task-badge.approval.rejected, .task-badge.approval.changes { background: var(--red-soft); border-color: var(--red-line); color: var(--red-fg); }
:root[data-theme="dark"] .task-badge.approval.approved { background: #163527; border-color: #1f6b46; color: #7fd4a6; }
.due-row { display: flex; align-items: center; gap: 10px; min-width: 0; }
.due-row .cell-btn { flex: 1; }
.link-btn.subtle { color: var(--fg-faint); font-size: 12.5px; white-space: nowrap; }
.link-btn.subtle:hover { color: var(--fg-muted); }
.props-grid .dep { margin: 1px 0; padding-left: var(--value-inset); }
.feed-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.feed-head h4 { margin: 0; }
.drawer-foot {
  border-top: 1px solid var(--border); background: var(--surface); padding: 12px 18px 10px;
  display: flex; flex-direction: column; gap: 8px; box-shadow: 0 -6px 16px -12px rgba(0, 0, 0, .25);
}
.drawer-foot .comment-form { margin-top: 0; }
.drawer-foot .c-compose textarea { min-height: 44px; }
.collab-row { display: flex; align-items: center; gap: 10px; padding-top: 4px; border-top: 1px solid var(--border-soft); }
.collab-row .spacer { flex: 1; }
.collab-label { font-size: 12px; color: var(--fg-faint); white-space: nowrap; }
/* Leave or join: a button, as low as the avatars beside it, so the footer keeps its height. */
.collab-join { gap: 6px; padding: 2px 8px; font-size: 12.5px; white-space: nowrap; color: var(--fg-muted); }
.collab-join:hover { color: var(--fg); }
.collab-stack { display: inline-flex; align-items: center; }
.collab-stack .collab-avatar { margin-left: -6px; border: 2px solid var(--surface); cursor: pointer; transition: transform .1s; }
.collab-stack .collab-avatar:first-child { margin-left: 0; }
.collab-stack .collab-avatar:hover { transform: translateY(-2px); z-index: 1; position: relative; }
.collab-add {
  width: 26px; height: 26px; border-radius: 50%; margin-left: 4px; border: 1.5px dashed var(--border-strong);
  background: transparent; color: var(--fg-faint); display: grid; place-items: center; cursor: pointer;
}
.collab-add:hover { color: var(--fg); border-style: solid; }

/* Печать карточки: только панель, на всю страницу. */
@media print {
  body.print-task > *:not(.drawer) { display: none !important; }
  body.print-task .drawer { position: static; width: auto; max-width: none; transform: none; box-shadow: none; border: 0; height: auto; }
  body.print-task .drawer-head, body.print-task .drawer-foot, body.print-task .drop-plate, body.print-task .subtask-add { display: none !important; }
  body.print-task .section-actions { display: none !important; }
  body.print-task .drawer-body { overflow: visible; padding: 0; }
  /* On paper there is no «Показать больше» to press: every comment in full. */
  body.print-task .c-text > .rich { max-height: none !important; -webkit-mask-image: none; mask-image: none; }
  body.print-task .c-text > .rich .spoiler { display: block; }
  /* Paper has no scroll: a wide table is laid out on the page, its words broken where they must. */
  body.print-task .rich-table { display: table; overflow: visible; }
  body.print-task .rich-table :is(th, td) { overflow-wrap: anywhere; }
  body.print-task .c-fold { display: none; }
  body.print-task .drawer-title { border: 0; }
}

.drawer.wide { width: var(--drawer-wide-w); max-width: none; }
.drawer.wide .drawer-body { padding: 16px 40px 40px; }
.drawer.wide { --props-k: 150px; }
.drawer.wide .props-grid { grid-template-columns: var(--props-k) minmax(0, 560px); }
.drawer.wide .drawer-title { font-size: 24px; }

/* ------------------------------------------------------ колонки и секции --- */

.tv-head .th { position: relative; }
.col-resize {
  position: absolute; right: -3px; top: 6px; bottom: 6px; width: 7px;
  cursor: col-resize; z-index: 2;
}
.col-resize::after {
  content: ''; position: absolute; left: 3px; top: 0; bottom: 0; width: 1px; background: transparent;
}
.col-resize:hover::after { background: var(--accent); }
body.col-resizing { cursor: col-resize; user-select: none; }

/*
 * Заголовок колонки тянут мышью, чтобы переставить её. Курсор «схватить»
 * только у тех, кого можно двигать: «Название задачи» всегда первое.
 */
.tv-head .th[draggable="true"] { cursor: grab; }
.tv-head .th[draggable="true"]:active { cursor: grabbing; }
/* Кнопке сортировки курсор нужен свой: по ней кликают, а не тянут. */
.tv-head .th[draggable="true"] button { cursor: pointer; }
.tv-head .th.th-dragging { opacity: .45; }

.col-list { display: flex; flex-direction: column; gap: 1px; max-height: 300px; overflow-y: auto; }
.col-row {
  display: flex; align-items: center; gap: 8px; padding: 5px 6px;
  border-radius: var(--radius); font-size: 13px; cursor: pointer;
}
.col-row:hover { background: var(--bg-muted); }
.col-row .n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.col-row .move { display: flex; gap: 1px; opacity: 0; }
.col-row:hover .move { opacity: 1; }
.col-row .move button {
  border: 0; background: transparent; color: var(--fg-faint); padding: 2px; border-radius: 3px;
  display: flex;
}
.col-row .move button:hover { background: var(--border); color: var(--fg); }
.col-row .move button:not(.down) svg { transform: rotate(180deg); }

/* Перетаскивание секции: метка вставки над или под соседом. */
.tv-section-head[draggable=true] { cursor: grab; }
/* Раздел «в руке»: как и строка, он уже стоит на новом месте, поэтому его
   не прячут, а помечают. Меток «сюда» больше нет — место видно по самому
   разделу. */
.tv-section.dragging { opacity: .75; }
.tv-section.dragging > .tv-section-head {
  background: var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent);
}

/* ------------------------------------- перетаскивание сроков и дат --- */

.cal-task { cursor: grab; }
.cal-cell.drop-day { background: var(--accent-soft); outline: 2px solid var(--accent); outline-offset: -2px; }

.tl-bar { cursor: grab; }
.tl-milestone { cursor: grab; }
body.tl-dragging, body.tl-dragging * { cursor: grabbing !important; user-select: none; }
.tl-grip {
  position: absolute; top: 0; bottom: 0; width: 8px; cursor: col-resize;
}
.tl-grip.left { left: 0; }
.tl-grip.right { right: 0; }
.tl-bar:hover .tl-grip::after {
  content: ''; position: absolute; top: 3px; bottom: 3px; width: 2px;
  background: rgba(255, 255, 255, .7); border-radius: 1px;
}
.tl-grip.left::after { left: 2px; }
.tl-grip.right::after { right: 2px; }

/* Свёрнутая колонка доски: полоса с повёрнутым названием. */
.column.folded { flex: 0 0 44px; }
.column.folded .column-body, .column.folded .column-add { display: none; }
.column.folded .column-head .col-add { display: none; }
.column.folded .column-head {
  writing-mode: vertical-rl; height: 100%; justify-content: flex-start;
  padding: 12px 0; gap: 8px;
}
.column-head { cursor: pointer; }
.column-head:hover { background: var(--border); border-radius: var(--radius); }

/* Выбор значка проекта. */
.icon-picker { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; }
.icon-own { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.icon-own .project-icon { width: 28px; height: 28px; flex: 0 0 28px; border-radius: 7px; }
.icon-picker .pick {
  display: flex; align-items: center; justify-content: center; height: 34px;
  border: 1px solid var(--border); background: var(--surface); border-radius: var(--radius);
  color: var(--fg-muted);
}
.icon-picker .pick:hover { border-color: var(--border-strong); color: var(--fg); }
.icon-picker .pick.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

/* Картинка профиля: под ней — кнопки смены, видны только тому, кто вправе. */
.avatar-edit { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.avatar-edit .avatar.xl { margin: 0; }
.avatar-edit button {
  border: 0; background: transparent; color: var(--fg-muted);
  font: inherit; font-size: 11.5px; padding: 1px 6px; border-radius: 5px; cursor: pointer;
}
.avatar-edit button:hover { background: var(--bg-muted); color: var(--fg); }
.avatar-edit .avatar-clear { color: var(--fg-faint); }

/* Комментарии: ветка ответов, закрепление и правка на месте. */
.c-earlier { display: flex; width: fit-content; margin: 4px -6px 12px; padding: 6px; font-size: 13px; }
.c-earlier:disabled { color: var(--fg-faint); }
.c-pin-label {
  display: flex; align-items: center; gap: 5px;
  font-size: 11.5px; color: var(--fg-faint); margin: 2px 0 6px;
}
.c-thread { margin-bottom: 4px; }
.c-thread.pinned {
  background: var(--blue-soft); border-radius: var(--radius);
  padding: 6px 8px; margin: 0 -8px 8px;
}
.c-replies { margin-left: 30px; }
.comment.reply { padding-top: 4px; }
.comment.reply .c-body { font-size: 13px; }
.c-text { min-width: 0; }
.c-edited { font-size: 11px; color: var(--fg-faint); }

/*
 * Arrived while one was reading: the light and the mark go out by themselves,
 * slowly, so that it does not read as a blink. Blue, not the accent: news,
 * not an alarm.
 *
 * The light is drawn under the entry, 12px past its sides, and never changes
 * its box: it used to widen a comment by margins while lit and to sit on
 * the avatar of a history entry with none, so the entries moved as it came
 * and went. It fades by its opacity alone.
 */
.comment, .activity-item { position: relative; isolation: isolate; }
.comment::before, .activity-item::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: -12px; right: -12px; z-index: -1;
  border-radius: 6px; background: color-mix(in srgb, var(--blue) 9%, transparent);
  box-shadow: inset 2px 0 0 var(--blue); opacity: 0; transition: opacity .8s ease; pointer-events: none;
}
.comment.c-new::before, .activity-item.c-new::before { opacity: 1; }
/* The comment a link or an inbox entry opened the card on: the same light,
   for a few seconds (drawer.js, revealComment). */
.comment.c-target::before { opacity: 1; }
/* News since the last read, on a card opened from the inbox: a blue line with
   its word on the right, as Asana's "New activity". It stays; the light on
   the entries below it goes out after a few seconds. */
.news-line {
  display: flex; align-items: center; gap: 8px; margin: 10px 0 6px;
  color: var(--blue); font-size: 11.5px; font-weight: 600;
}
.news-line::before { content: ''; flex: 1; height: 1px; background: var(--blue); }
.c-fresh {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--blue);
  background: color-mix(in srgb, var(--blue) 16%, transparent);
  border-radius: 999px;
  padding: 1px 6px;
}
.c-pinned { color: var(--blue); display: inline-flex; }
.c-reply-btn, .c-more {
  border: 0; background: transparent; color: var(--fg-faint);
  padding: 2px 5px; border-radius: 5px; cursor: pointer;
  display: inline-flex; align-items: center; gap: 3px; font: inherit; font-size: 11.5px;
}
.c-reply-btn:hover, .c-more:hover { background: var(--bg-muted); color: var(--fg); }
/* Ответ под веткой — то же поле, что под лентой, только уже: аватар слева,
   рамка со всем содержимым справа. */
.c-answer { display: flex; align-items: flex-start; gap: 7px; margin: 6px 0 2px 30px; }
.c-answer.hidden { display: none; }
.c-answer .c-compose { padding: 6px 9px; }
.c-answer .c-send { width: 28px; height: 28px; }
.c-edit-actions { display: flex; gap: 6px; margin-top: 6px; }
/* While a comment is edited its hover tools would sit over the field's markup bar. */
.comment:has(.c-edit) > .c-tools { display: none; }
.c-edit { margin-top: 4px; }

/* Карточка: цепочка родителей, согласование, время, шаблоны, ссылка-карточка. */
/* The parents share the card's width and are cut to it, not to a fixed length. */
.crumbs { display: flex; flex-wrap: nowrap; align-items: center; gap: 4px; margin-bottom: 8px; font-size: 12.5px; min-width: 0; }
.crumbs .sep { flex: none; color: var(--fg-faint); }
.crumb {
  border: 0; background: transparent; color: var(--fg-muted); font: inherit; font-size: 12.5px;
  padding: 2px 6px; border-radius: 5px; cursor: pointer; flex: 0 1 auto; min-width: 3em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.crumb:hover { background: var(--bg-muted); color: var(--fg); }
.crumb.done { color: var(--fg-faint); }
/*
 * Something the reader may not open, named only as private: a parent, a
 * linked task, a project in the history or the card's fields.
 */
.crumb.muted { color: var(--fg-faint); cursor: default; font-style: italic; }
.crumb.muted:hover { background: transparent; color: var(--fg-faint); }
.fields-project.muted, .activity-item span.muted, .dep .muted { color: var(--fg-muted); font-style: italic; }
.dep.hidden-dep .dep-dir, .dep.hidden-dep .dep-kind { cursor: default; }

.approval-btn.pending .label { color: var(--amber, #b7791f); }
.approval-btn.approved .label { color: var(--green); }
.approval-btn.changes .label { color: var(--red); }
.approval-btn.rejected .label { color: var(--red); }

/* Шаблоны задач */
.split-btn { display: inline-flex; }
.split-btn .btn:first-child { border-top-right-radius: 0; border-bottom-right-radius: 0; }
.split-btn .split-caret { border-top-left-radius: 0; border-bottom-left-radius: 0; padding: 5px 6px; border-left: 1px solid rgba(255, 255, 255, .35); }
.tpl-modal { max-width: 620px; }
.tpl-head { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.tpl-head h2 { margin: 0; }
.tpl-head .spacer { flex: 1; }
.tpl-hint { margin: 0 0 12px; font-size: 13px; color: var(--fg-muted); }
.tpl-list { max-height: 55vh; overflow-y: auto; border-top: 1px solid var(--border); }
.tpl-row { display: flex; align-items: center; gap: 10px; padding: 10px 4px; border-bottom: 1px solid var(--border-soft); }
.tpl-row .tpl-icon { width: 32px; height: 32px; border-radius: 8px; background: var(--bg-muted); color: var(--fg-muted); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 32px; }
.tpl-row .tpl-name { flex: 1; min-width: 0; font-size: 13.5px; }
.tpl-row .tpl-name b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tpl-sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px; color: var(--fg-muted); margin-top: 2px; }
.tpl-sub .pill { font-size: 11px; padding: 1px 7px; }
.tpl-author { display: inline-flex; align-items: center; gap: 5px; }
.tpl-row .btn.danger:hover { background: var(--red-soft); color: var(--red); }
.tpl-empty { display: flex; flex-direction: column; gap: 6px; padding: 22px 8px; color: var(--fg-muted); font-size: 13px; }
.tpl-empty b { color: var(--fg); }
/* Заголовок части окна: обычным регистром, как ступени правила. Капслок
   с разрядкой читается медленнее и в диалоге выглядит служебной надписью. */
.tpl-section {
  font-size: 13px; font-weight: 600; color: var(--fg-muted);
  margin: 4px 0 10px; padding-top: 12px; border-top: 1px solid var(--border-soft);
}
.tpl-grid { display: grid; grid-template-columns: 1fr 1fr 1.4fr; gap: 12px; align-items: end; }
.tpl-check { display: flex; align-items: center; gap: 8px; font-size: 13px; padding-bottom: 9px; }
.tpl-check input { width: 16px; height: 16px; margin: 0; }
.tpl-modal .markup-bar { margin-bottom: 4px; }
@media (max-width: 720px) { .tpl-grid { grid-template-columns: 1fr; } }
.popover-head { font-weight: 600; font-size: 13px; padding: 4px 4px 8px; }

.cell-btn.formula { cursor: default; opacity: .9; }
.cell-btn.formula .label::before { content: '= '; color: var(--fg-faint); }

/* Изменение: «было → стало» одной строкой, значения — плашками и аватарами. */
.activity-item .change {
  display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
  font-size: 12.5px; color: var(--fg-muted); margin-top: 4px;
}
.activity-item .change .what { color: var(--fg-faint); }
.activity-item .change .arrow { color: var(--fg-faint); }
.activity-item .change .v-none { color: var(--fg-faint); font-style: italic; }
.activity-item .change .v-who { display: inline-flex; align-items: center; gap: 5px; }
/* A person in a change leads to the profile, as the author does (iteration 17, item 5.2): the value's colour, underlined on hover. */
.activity-item .change a.person-link { color: inherit; text-decoration: none; border-radius: 4px; }
.activity-item .change a.person-link:hover .person-name { text-decoration: underline; }
.activity-item .change a.person-link:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.activity-item .change .v-people { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px 10px; }
.activity-item .change .fv-chip { text-decoration: none; }
/* Зачёркивание осталось только там, где оно значит «было → стало». Названия
   выполненных задач им не перечёркиваются: список закрытых задач читают не
   реже открытых, а перечёркнутый текст читается плохо. Что задача закрыта,
   видно по галочке и приглушённому цвету. */
.activity-item .change .from { color: var(--fg-faint); }
/* Зачёркнуто само прежнее значение, а не вся строка с плашкой внутри. */
.activity-item .change .from > span:not(.fv-chip):not(.v-who):not(.v-people) { text-decoration: line-through; }
.activity-item .change .from .v-who { opacity: .6; }
.activity-item .change .from .fv-chip { opacity: .6; }
.activity-item .change .to { color: var(--fg); }

.task-chip {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 1px 8px 1px 5px; border: 1px solid var(--border-strong); border-radius: 14px;
  color: var(--fg); text-decoration: none; font-size: 12.5px; vertical-align: middle;
  background: var(--surface);
}
.task-chip:hover { background: var(--bg-muted); }
.task-chip .mark { width: 12px; height: 12px; border-radius: 50%; border: 1.5px solid var(--fg-faint); flex: 0 0 auto; }
.task-chip.done .mark { background: var(--green); border-color: var(--green); }
.task-chip.done .name { color: var(--fg-faint); }
.task-chip .name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A link to one comment: the task chip with a speech bubble for its mark. */
.comment-chip .icon { flex: none; color: var(--fg-faint); }
/* A file named in a text: a grey chip with a paperclip, as Asana draws it. */
.file-chip {
  display: inline-flex; align-items: center; gap: 5px; max-width: 100%;
  padding: 0 7px 0 5px; border-radius: 5px; background: var(--bg-muted);
  color: var(--fg); text-decoration: none; font-size: 12.5px; line-height: 1.6; vertical-align: baseline;
}
.file-chip:hover { background: var(--border); }
/* Dropped into a text and still uploading: it has no address yet. */
.file-chip.loading { opacity: .6; }
.rich a.file-chip, .c-text a.file-chip, .rich-input a.file-chip, .rich a.file-chip:hover, .c-text a.file-chip:hover { color: var(--fg); text-decoration: none; }
.file-chip .icon { flex: none; color: var(--fg-muted); }
.file-chip span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Комментарии: реакции, файлы, панель ввода. */
.c-reactions { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; }
.reaction {
  display: inline-flex; align-items: center; gap: 4px; border: 1px solid var(--border);
  background: var(--surface); color: var(--fg-muted); border-radius: 12px; padding: 1px 7px;
  font: inherit; font-size: 12px; cursor: pointer; line-height: 1.5;
}
.reaction:hover { border-color: var(--border-strong); color: var(--fg); }
.reaction.mine { border-color: var(--blue-line); background: var(--blue-soft); color: var(--fg); }
/* Пустой ряд не занимает места: под комментарием стоят только поставленные
   реакции, а ставят новые с панельки, всплывающей над комментарием. */
.c-reactions:empty { display: none; }

/*
 * Панелька действий над комментарием — как в Asana: три ходовых значка, весь
 * набор, «ответить» и «ещё». Видна, пока на комментарии курсор; открытое из
 * неё меню или набор значков держат её на месте (класс open), иначе она
 * исчезала бы вместе с уходом курсора к самому меню.
 */
.comment { position: relative; }
.c-tools {
  position: absolute; top: 2px; right: 0; z-index: 2;
  display: flex; align-items: center; gap: 2px;
  padding: 3px; border: 1px solid var(--border); border-radius: 999px;
  background: var(--surface); box-shadow: var(--shadow-md);
  opacity: 0; pointer-events: none; transition: opacity .12s ease;
}
.comment:hover > .c-tools,
.comment:focus-within > .c-tools,
.c-tools.open { opacity: 1; pointer-events: auto; }
@media (hover: none) { .c-tools { opacity: 1; pointer-events: auto; } }
.c-tool {
  border: 0; background: transparent; color: var(--fg-muted); cursor: pointer;
  font: inherit; font-size: 14px; line-height: 1;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 6px; border-radius: 999px;
}
.c-tool:hover { background: var(--bg-muted); color: var(--fg); }
.c-tool.mine { background: var(--blue-soft); }
.c-tools .c-reply-btn, .c-tools .c-more { font-size: 12px; padding: 4px 8px; }
.c-tools .c-more { padding: 4px 6px; }
/* Сколько ответов в ветке: остаётся в шапке, панелька для этого не нужна. */
.c-replies-n {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11.5px; color: var(--fg-faint);
}

/*
 * Карточка человека над упоминанием — как в Asana. Ширину задаёт попап,
 * поэтому здесь только внутреннее устройство.
 */
.user-card { padding: 12px; display: grid; gap: 10px; }
.uc-head { display: flex; gap: 10px; align-items: center; }
.uc-who { min-width: 0; display: grid; gap: 1px; }
.uc-who b { font-size: 14px; }
.uc-role { font-size: 12px; color: var(--fg-muted); }
.uc-mail { font-size: 12.5px; color: var(--blue); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uc-away { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--amber); }
.uc-stats {
  font-size: 12.5px; color: var(--fg-muted);
  border-top: 1px solid var(--border); padding-top: 9px;
}
.uc-actions { display: flex; gap: 6px; }
.uc-actions .btn { flex: 1; justify-content: center; white-space: nowrap; padding: 5px 8px; }
/*
 * Who one is, at the head of one's avatar menu (app.js): the card's head.
 * The address is text there, to read and select, not a link: muted, not
 * link-blue.
 */
.menu .menu-me { padding: 8px 9px 6px; cursor: default; }
.menu-me .uc-mail { color: var(--fg-muted); user-select: text; }

/* Весь набор значков — сеткой, шесть в ряд, как в Asana за «плюсом». */
.react-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px; padding: 4px; }
.react-pick {
  border: 0; background: transparent; cursor: pointer; font-size: 18px; line-height: 1;
  padding: 6px; border-radius: 8px;
}
.react-pick:hover { background: var(--bg-muted); }
.c-files { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.c-file {
  display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px;
  border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; color: var(--fg); text-decoration: none;
  max-width: 260px;
}
.c-file span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.c-file:hover { background: var(--bg-muted); }
.c-file-img img { max-width: 220px; max-height: 160px; border-radius: 6px; border: 1px solid var(--border); display: block; }
/*
 * The comment plate is one frame around everything, as in Asana: the text,
 * the files attached and one row of buttons at the bottom, the markup
 * buttons among them. It keeps one height at rest and while writing: a
 * toolbar appearing above the text on focus made the card jump under the
 * pointer and grew a scrollbar in a card that fitted (iteration 17, item 7).
 * At rest the frame is quiet so the conversation above does not compete
 * with it; writing gives it a focus ring.
 */
.c-compose {
  flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border-strong); border-radius: 12px;
  background: var(--surface); padding: 8px 10px;
  transition: border-color .12s ease, box-shadow .12s ease;
}
/* Рамка пишущего поля — спокойная, серая: в Asana она белая и не кричит,
   а красная здесь читалась как ошибка ввода. */
.c-compose:focus-within { border-color: var(--fg-muted); box-shadow: 0 0 0 3px var(--bg-muted); }
/* A file held over a reply or a comment being edited: the field has no frame of its own here, the box shows it takes the file. */
.c-compose:has(> .rich-input.dropping) { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.c-compose textarea { width: 100%; }
/* Внутри общей рамки у поля своей рамки нет — иначе их видно две. */
.c-compose .rich-input {
  border: 0; background: transparent; padding: 2px 0; max-height: 30vh;
}
.c-compose .rich-input:focus { box-shadow: none; }
.c-compose-actions { display: flex; align-items: center; gap: 2px; min-width: 0; }
.c-compose-gap { flex: 1; min-width: 4px; }
/*
 * The markup buttons stand in the bottom row, after @, and only fade in
 * while one writes: shown and hidden by their look, never by display, so
 * the plate keeps its height on a click into the field and out of it
 * (composer.js, iteration 17, item 7). A narrow card scrolls them sideways
 * rather than wrapping them onto a second row.
 */
.c-compose-actions .markup-bar {
  margin: 0 0 0 4px; padding-left: 6px; border-left: 1px solid var(--border);
  flex: 0 1 auto; min-width: 0; overflow-x: auto; scrollbar-width: none;
  visibility: hidden; opacity: 0; transition: opacity .12s ease, visibility 0s linear .12s;
}
.c-compose-actions .markup-bar::-webkit-scrollbar { display: none; }
.c-compose:focus-within .c-compose-actions .markup-bar, .c-compose.filled .c-compose-actions .markup-bar {
  visibility: visible; opacity: 1; transition: opacity .12s ease;
}
/* In the row the markup buttons look like its other buttons: no frame of their own, the paragraph kind by its icon. */
.c-compose-actions .markup-btn { flex: none; border-color: transparent; background: transparent; border-radius: 8px; }
.c-compose-actions .markup-btn:hover { background: var(--bg-muted); }
.c-compose-actions .markup-btn.on { background: var(--accent-soft); border-color: transparent; }
.c-compose-actions .markup-btn.block-pick .block-name { display: none; }
.c-compose-actions .markup-bar .block-pick + .markup-btn { margin-left: 2px; }
/* On a phone the row is tighter, so all the markup buttons, the spoiler last, fit beside the send button. */
@media (max-width: 520px) {
  .c-compose-actions .c-tool-btn { padding: 3px; }
  .c-compose-actions .markup-bar { margin-left: 2px; padding-left: 4px; }
  .c-compose-actions .markup-btn { min-width: 24px; padding: 0; }
  .c-compose-actions .markup-btn.block-pick { padding: 0 4px; }
  .c-compose-actions .markup-btn.block-pick svg:last-child { display: none; }
}
/* A comment being edited is in the feed, not in the plate: its bar stays above it, shown, so leaving the field moves nothing. */
.c-edit > .markup-bar { display: flex; flex-wrap: wrap; margin: 0 0 2px; padding-bottom: 6px; border-bottom: 1px solid var(--border); }
/* Кнопки содержимого: файл, значок, упоминание — слева, как в Asana. */
.c-tool-btn {
  border: 0; background: transparent; color: var(--fg-faint); cursor: pointer;
  padding: 5px; border-radius: 8px; display: inline-flex; align-items: center;
}
.c-tool-btn:hover { background: var(--bg-muted); color: var(--fg); }
/*
 * Отправка — круглая кнопка у правого края. Пока писать нечего, она
 * приглушена: нажимать её в пустом поле незачем, и об этом лучше сказать
 * видом, чем отказом после нажатия.
 */
.c-send {
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--fg-faint);
  width: 32px; height: 32px; border-radius: 50%; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
/* Отправка — синяя, как в Asana: это обычное действие, а не опасное. */
.c-send.ready { background: var(--blue); border-color: var(--blue); color: #fff; }
.c-send:hover { border-color: var(--blue); color: var(--blue); }
.c-send.ready:hover { color: #fff; opacity: .92; }
.c-send:disabled { opacity: .5; cursor: default; }
.c-queued { display: flex; flex-wrap: wrap; gap: 4px; }
.c-queued:empty { display: none; }
.mention-task-mark {
  width: 22px; height: 22px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center;
  background: var(--accent-soft); color: var(--accent); flex: 0 0 auto;
}

/* Входящие: непрочитанное и значок в панели. */
.inbox-row { position: relative; }
.inbox-dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; background: transparent; }
.inbox-row.unread .inbox-dot { background: var(--accent); }
.inbox-row.unread .title { font-weight: 600; }
.inbox-row .done-text { color: var(--fg-faint); }
.unread-badge { background: var(--accent); color: #fff; }
.unread-badge.hidden { display: none; }
.tool-btn.on { background: var(--accent-soft); color: var(--accent); }

/* Хронология: масштаб и крупные шкалы. */
.tl-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.tl-head .days { flex: 0 0 auto; }
.tl-head .day.week, .tl-head .day.month { flex: 0 0 auto; text-align: left; padding-left: 6px; white-space: nowrap; overflow: hidden; }
.tl-row .track.coarse { background: repeating-linear-gradient(90deg, transparent 0, transparent calc(100% - 1px)); }
.tl-row .track .today-line { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--accent); opacity: .5; flex: none; border: 0; }

/* Повторение: дни недели. */
.repeat-days { display: flex; gap: 4px; margin-top: 8px; }
.repeat-days .day {
  flex: 1; border: 1px solid var(--border); background: var(--surface); color: var(--fg-muted);
  border-radius: 6px; padding: 4px 0; font: inherit; font-size: 11.5px; cursor: pointer;
}
.repeat-days .day.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.repeat-last { display: flex; align-items: center; gap: 6px; margin-top: 8px; font-size: 12.5px; color: var(--fg-muted); }

/* Календарь по неделям. */
.cal-week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; min-height: 420px; }
.cal-week-col { border-left: 1px solid var(--border); padding: 8px; display: flex; flex-direction: column; gap: 4px; }
.cal-week-col:first-child { border-left: 0; }
.cal-week-col.today { background: var(--accent-soft); }
.cal-week-col.drop-day { outline: 2px dashed var(--accent); outline-offset: -3px; }
.cal-week-head { display: flex; align-items: baseline; gap: 6px; margin-bottom: 6px; font-size: 12px; color: var(--fg-muted); }
.cal-week-head .num { font-weight: 600; color: var(--fg); }
.cal-week-head .count { margin-left: auto; font-size: 11px; color: var(--fg-faint); }
.cal-week .cal-task { width: 100%; text-align: left; white-space: normal; }
@media (max-width: 720px) {
  .cal-week { grid-template-columns: 1fr; min-height: 0; }
  .cal-week-col { border-left: 0; border-top: 1px solid var(--border); }
}
.toast-action.primary { background: var(--accent); color: #fff; }
.toast-action.primary:hover { filter: brightness(1.1); }

/* ============================================ проход по интерфейсам Asana */

/* Шапка: «Создать» слева, поиск по центру */
.create-btn { gap: 6px; padding-right: 8px; }
.create-btn .icon:last-child { opacity: .7; }

/* Боковая панель: сворачиваемые группы */
.nav-group-toggle { width: 100%; border: 0; background: transparent; cursor: pointer; color: var(--sidebar-fg-dim); }
.nav-group-toggle .icon { transition: transform .15s; transform: rotate(-90deg); }
.nav-group-toggle.open .icon { transform: rotate(0); }
.nav-group-toggle:hover { color: var(--sidebar-fg); }
/* "All projects" under the five most used, "Collapse" under the full list. */
.nav-more { color: var(--sidebar-fg-dim); font-size: 12.5px; }
/* The 12 px caret keeps the 16 px slot of the icons above it without stretching its box. */
.nav-more .icon { flex: 0 0 12px; margin: 0 2px; }
.nav-more:hover { color: var(--sidebar-fg); }
.nav-projects.all .nav-more .icon { transform: rotate(180deg); }
.nav-group-body { display: flex; flex-direction: column; }

/* Главная */
.home-head { display: flex; align-items: flex-end; gap: 16px; flex-wrap: wrap; padding: 26px 32px 6px; max-width: 1180px; margin: 0 auto; }
.home-head .home-title { flex: 1; min-width: 200px; }
.home-date { font-size: 13px; color: var(--fg-muted); }
.home-head h1 { margin: 4px 0 0; font-size: 26px; font-weight: 600; letter-spacing: -.4px; }
.home-week { display: inline-flex; align-items: center; gap: 16px; flex-wrap: wrap; padding: 7px 16px; border: 1px solid var(--border); border-radius: 999px; background: var(--surface); font-size: 13px; color: var(--fg-muted); }
.home-week-label { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); }
.home-stat b { color: var(--fg); font-size: 15px; margin-right: 2px; }
.home-stat.warn b { color: var(--red); }
.home-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; padding: 18px 32px 40px; max-width: 1180px; margin: 0 auto; }
.home-widget { border: 1px solid var(--border); border-radius: 14px; background: var(--surface); padding: 16px 18px 14px; min-width: 0; }
.home-widget.wide { grid-column: 1 / -1; }
.home-widget-head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 12px; margin-bottom: 10px; }
.home-widget-head h2 { margin: 0; font-size: 16px; font-weight: 600; white-space: nowrap; }
.home-widget-head .spacer { flex: 1; }
.home-tabs { display: flex; gap: 2px; }
.home-tabs button { border: 0; background: transparent; padding: 5px 9px; border-radius: 6px; font-size: 12.5px; color: var(--fg-muted); display: inline-flex; align-items: center; gap: 5px; }
.home-tabs button:hover { background: var(--bg-muted); }
.home-tabs button.on { background: var(--bg-muted); color: var(--fg); font-weight: 500; }
.home-tabs .n { font-size: 11px; color: var(--fg-faint); }
.home-widget-body { display: flex; flex-direction: column; }
/*
 * All tabs of My tasks lie in one grid cell and only the chosen one is
 * visible, so the widget keeps the height of its fullest tab. The track is
 * minmax(0, 1fr) and the panes min-width: 0, or the unbroken titles of a
 * hidden tab would widen the widget past the page.
 */
.home-panes { display: grid; grid-template-columns: minmax(0, 1fr); }
.home-pane { grid-area: 1 / 1; display: flex; flex-direction: column; min-width: 0; }
.home-pane:not(.on) { visibility: hidden; }
/*
 * The home page grows and shrinks with its lists. Where scrollbars take
 * room, the bar coming and going shifted the whole page sideways, so its
 * place is kept. Only here: list and board views do not scroll the .view
 * and would get an empty strip.
 */
.view:has(> .home-grid) { scrollbar-gutter: stable; }
.home-add { display: flex; align-items: center; gap: 8px; border: 0; background: transparent; padding: 8px 4px; color: var(--fg-muted); font-size: 13.5px; text-align: left; border-bottom: 1px solid var(--border-soft); }
.home-add:hover { color: var(--fg); }
.home-task { display: flex; align-items: center; gap: 10px; padding: 8px 4px; border-bottom: 1px solid var(--border-soft); cursor: pointer; }
.home-task:hover { background: var(--bg-muted); }
.home-task .title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; }
.home-task .title.done { color: var(--fg-faint); }
.home-task .meta { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.home-empty { padding: 14px 4px; color: var(--fg-muted); font-size: 13px; }
.home-more { display: block; padding: 10px 4px 2px; font-size: 12.5px; color: var(--fg-muted); text-decoration: none; }
.home-more:hover { color: var(--fg); }
.home-projects { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.home-project { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid transparent; border-radius: 10px; background: transparent; text-align: left; color: var(--fg); min-width: 0; }
.home-project:hover { background: var(--bg-muted); border-color: var(--border); }
.home-project .mark { width: 40px; height: 40px; border-radius: 10px; flex: 0 0 40px; display: inline-flex; align-items: center; justify-content: center; }
.home-project.new .mark { border: 1.5px dashed var(--border-strong); color: var(--fg-faint); }
.home-project.new .name { color: var(--fg-muted); }
.home-project .text { display: flex; flex-direction: column; min-width: 0; gap: 2px; }
.home-project .name { font-weight: 500; font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.home-project .sub { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fg-muted); }
.home-project .sub .pill.status { font-size: 11px; padding: 1px 7px; }
.home-people { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.home-person { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 6px; border: 1px solid transparent; border-radius: 10px; background: transparent; color: var(--fg); }
.home-person:hover { background: var(--bg-muted); border-color: var(--border); }
.home-person .name { font-size: 12.5px; text-align: center; line-height: 1.3; }
.home-person .avatar.lg { width: 44px; height: 44px; font-size: 15px; }
.avatar.dashed { background: transparent; border: 1.5px dashed var(--border-strong); color: var(--fg-faint); display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; }
@media (max-width: 960px) { .home-grid { grid-template-columns: 1fr; } }
/* The same by the view's own width, which the task card takes from (app.js). */
.view-narrow .home-grid { grid-template-columns: 1fr; }
.view-slim .home-head, .view-slim .home-grid { padding-left: 16px; padding-right: 16px; }
.view-slim .home-projects { grid-template-columns: 1fr; }
@media (max-width: 720px) { .home-head, .home-grid { padding-left: 16px; padding-right: 16px; } .home-projects { grid-template-columns: 1fr; } }

/* Входящие */
/* The whole row opens its task; the one open on the right is marked. */
.inbox-item { cursor: pointer; }
/* --row-bg: the row's own background, which the badge on a picture rings itself with (.inbox-badge). */
.inbox-item { --row-bg: var(--surface); }
.inbox-item.current { box-shadow: inset 3px 0 0 var(--accent); background: var(--accent-soft); --row-bg: var(--accent-soft); }
.inbox-tabs .n { font-size: 11px; margin-left: 5px; color: var(--fg-faint); }
/* The count keeps room for two digits and stays when it is 0 (inbox.js,
   drawTabs): the tabs after it moved as threads were read. */
.inbox-tabs .n { display: inline-block; min-width: 2ch; text-align: left; font-variant-numeric: tabular-nums; }
.inbox-list { padding: 0 32px 40px; }
.inbox-item { position: relative; display: flex; gap: 12px; padding: 12px 14px 12px 22px; border-radius: 10px; align-items: flex-start; }
.inbox-item:hover { background: var(--bg-muted); --row-bg: var(--bg-muted); }
/* Ветка: одна задача — одна строка. Лица участников слева стопкой, как
   в Asana; событий внутри может быть сколько угодно. */
.inbox-faces { display: flex; margin-top: 2px; }
.inbox-faces .avatar + .avatar { margin-left: -7px; box-shadow: 0 0 0 2px var(--surface); }
.inbox-item:hover .inbox-faces .avatar + .avatar { box-shadow: 0 0 0 2px var(--bg-muted); }
/* The thread's head: the task's circle, its name, the assignee and the due date (its place is the line above, .inbox-crumbs). A click on it opens the task. */
.inbox-head {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  min-width: 0; margin-bottom: 2px;
}
.inbox-head .inbox-task { flex: 0 1 auto; min-width: 3em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A read title keeps the width of its bold (unread) face, as .tab-label does:
   opening a thread moved the project chip, assignee and due pill after it. */
.inbox-head .inbox-task::after {
  content: attr(data-label); display: block; height: 0; overflow: hidden;
  visibility: hidden; font-weight: 600; user-select: none; pointer-events: none;
}
.inbox-head:hover .inbox-task { text-decoration: underline; text-underline-offset: 2px; }
.inbox-body .inbox-line time { color: var(--fg-faint); font-size: 12px; margin-left: 6px; }
.inbox-item .avatar { margin-top: 2px; }
.inbox-body { flex: 1; min-width: 0; }
.inbox-line { font-size: 13.5px; line-height: 1.45; }
/*
 * A row of a thread (iteration 18, items 16.4 and 16.7), as Asana's: the
 * author's picture in a column of its own with a badge of what the entry
 * tells on its corner, the words beside it, the text under the words. The
 * picture stood in the text at 18 px, too small for a badge, and the text
 * ran under it.
 */
.inbox-line { display: grid; grid-template-columns: 22px minmax(0, 1fr); column-gap: 10px; align-items: start; }
.inbox-actor { grid-row: 1 / span 2; position: relative; width: 22px; height: 22px; }
.inbox-item .inbox-actor > .avatar { margin-top: 0; }
.inbox-said { grid-column: 2; min-width: 0; padding-top: 1px; }
/* Under the words, cut at two lines: as a block in the text (display: block) it outweighed the clamp's -webkit-box, and a long comment ran on. */
.inbox-body .inbox-line .inbox-snippet { grid-column: 2; }
/*
 * The badge: a 12 px glyph on a 16 px disc, ringed with the row's own
 * background so it reads as laid on the picture. Grey says what happened;
 * green, amber and red are an outcome (done, an approval's decision).
 * Centred by flex, not a translate (AGENTS.md, 7).
 */
.inbox-badge {
  position: absolute; right: -7px; bottom: -6px; width: 16px; height: 16px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--fg-muted); color: var(--surface); box-shadow: 0 0 0 2px var(--row-bg);
}
.inbox-badge.tone-green { background: var(--green); color: #fff; }
.inbox-badge.tone-amber { background: var(--amber); color: #fff; }
.inbox-badge.tone-red { background: var(--red); color: #fff; }
/* A rule's row (iteration 18, item 2): its name as a person's name is set, the author only in its hover. */
.inbox-line .rule-name { font-weight: 600; color: var(--fg); cursor: default; }
/* The bolt's disc is the hovered row's own grey: a hairline keeps it a picture there. */
.inbox-actor > .rule-badge { box-shadow: inset 0 0 0 1px var(--border-strong); }
.inbox-where { color: var(--fg); }
/* A list field's and the Priority's values as chips (iteration 18, item 16.8), small enough to keep the two lines. */
.inbox-snippet .fv-chip { font-size: 12px; line-height: 16px; padding: 1px 7px; border-radius: 5px; }
.inbox-snippet .fv-chip + .fv-chip { margin-left: 4px; }
/*
 * One weight read or unread: opening a thread made its line read, the name
 * went from bold to 500 and the time after it moved ~5 px left (more on
 * Windows, whose Segoe UI has no 500 face). Unread shows in the colour and
 * the bold title, which keeps its width (.inbox-task::after).
 */
.inbox-line b { font-weight: 600; }
.inbox-task { color: var(--fg); }
.inbox-item.unread .inbox-task { font-weight: 600; }
.inbox-snippet { margin-top: 3px; font-size: 13px; color: var(--fg-muted); line-height: 1.45; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.inbox-meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; font-size: 12px; color: var(--fg-faint); }
.inbox-actions { display: flex; gap: 2px; opacity: 0; transition: opacity .1s; }
.inbox-item:hover .inbox-actions, .inbox-item:focus-within .inbox-actions { opacity: 1; }
/* `.hover` is the row under the pointer as the script knows it (inbox.js,
   `hovered`): a row drawn anew is not :hover for a few frames, and its
   buttons faded out and back in on every click. */
.inbox-item.hover { background: var(--bg-muted); --row-bg: var(--bg-muted); }
.inbox-item.hover .inbox-actions { opacity: 1; }
.inbox-item .inbox-dot { position: absolute; left: 8px; top: 18px; width: 8px; height: 8px; border-radius: 50%; background: transparent; }
.inbox-item.unread .inbox-dot { background: var(--accent); }
/* With the place line above (iteration 18, item 16.4) the dot stays by the name, as Asana's. */
.inbox-item.with-crumbs .inbox-dot { top: 35px; }
/* On a phone the four buttons took most of the row and left the event a
   word wide; they get a line of their own under it. */
@media (max-width: 720px) {
  .inbox-list { padding: 0 8px 40px; }
  .inbox-actions { opacity: 1; flex-basis: 100%; order: 3; margin: 2px 0 -4px -6px; }
}
/* A screen with no pointer to hover (a tablet wider than a phone) never lit
   the buttons: invisible, they still took a tap by the row's right edge
   and archived the thread (audit 4, area 2, Н8). */
@media (hover: none) {
  .inbox-actions { opacity: 1; }
}

/* ------------------------------------------------- «Входящие» как в Asana */

/* Промежуток времени: «Сегодня», «Вчера», «За последние 7 дней». */
.inbox-period {
  font-size: 12.5px; font-weight: 600; color: var(--fg-muted);
  padding: 18px 6px 8px; border-top: 1px solid var(--border);
  margin-top: 12px;
}
.inbox-list > .inbox-period:first-child { border-top: 0; margin-top: 0; padding-top: 8px; }

/* Threads of a period in one frame, as Asana's cards. */
.inbox-rows { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.inbox-rows > .inbox-item { border-radius: 0; padding: 9px 12px 9px 20px; }
.inbox-rows > .inbox-item + .inbox-item { border-top: 1px solid var(--border-soft); }
.inbox-rows .inbox-head { gap: 9px; }
/*
 * A task's row is worked from here: its circle completes it, and the
 * buttons on the right (⋯, like, bookmark, archive) are Asana's. The circle
 * is the lists' own (iteration 18, item 16.7): open, done in green, a
 * milestone, a blocked task, an approval's decision. Its border and fill
 * were taken off here, and an open task, a done one and a blocked one all
 * read as a faint tick.
 */
.inbox-head .check { flex: none; }
.inbox-head .inbox-task { cursor: pointer; }
.inbox-head .due-pill, .inbox-head .avatar { flex: none; }
/* The task's files, as a list row counts them (row-flags): the paperclip of Asana's inbox (iteration 18, item 16.7). */
.inbox-head .inbox-files { flex: none; display: inline-flex; align-items: center; gap: 3px; color: var(--fg-faint); font-size: 12px; }
/* The like is a grey thumb like the other actions, lit only once given. */
.inbox-actions .btn.like { color: var(--fg-muted); line-height: 1; }
.inbox-actions .btn.like.on { color: var(--accent); }
.inbox-actions .btn.like.on svg { fill: currentColor; }
.inbox-actions .btn .n { margin-left: 4px; font-size: 11.5px; color: var(--fg-muted); }
.inbox-actions .btn.on .n { color: inherit; }
/* The like and the empty slot of a thread with nothing to like are one fixed
   width: its count appearing (0 -> 1 -> 12) or a row without it moved the
   buttons beside it out of their columns. The thumb stays on the left. */
.inbox-actions .btn.like, .inbox-like-slot { flex: none; width: 56px; justify-content: flex-start; padding-right: 0; }
.inbox-item { flex-wrap: wrap; }
.inbox-rows .inbox-head .check { flex: 0 0 18px; }
/*
 * A thread: its kind (or the task's circle), its name and place on the first
 * line, the latest entry under it, the earlier ones folded.
 */
.inbox-kind { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 18px; color: var(--fg-muted); }
.inbox-kind.k-message { color: var(--accent); }
.inbox-kind.k-goal { color: var(--green-fg); }
/*
 * The task's place above its name (iteration 18, item 16.4): the project,
 * then the parent task, each a link, each cut short on its own. The title
 * line keeps the whole width, so a long project name no longer squeezes it
 * (audit 4, area 7, Н3).
 */
.inbox-crumbs { display: flex; align-items: center; gap: 4px; min-width: 0; font-size: 12px; line-height: 16px; color: var(--fg-muted); margin-bottom: 3px; }
.inbox-crumb { display: inline-flex; align-items: center; gap: 5px; flex: 0 1 auto; min-width: 0; color: inherit; border-radius: 4px; }
.inbox-crumb-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.inbox-crumb:hover { color: var(--fg); }
a.inbox-crumb:hover .inbox-crumb-name { text-decoration: underline; text-underline-offset: 2px; }
a.inbox-crumb:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.inbox-crumb-sep, .inbox-crumb-task { flex: none; display: inline-flex; color: var(--fg-faint); }
.inbox-crumb-task.done { color: var(--green); }
.inbox-crumb.closed { color: var(--fg-faint); }
.inbox-head .status-pill { flex: none; }
.inbox-thread:not(.unread) .inbox-line { color: var(--fg-muted); }
.inbox-thread:not(.unread) .inbox-line :is(b, .rule-name) { color: var(--fg); }
.inbox-fold { margin-top: 2px; }
.inbox-load-more { margin: 14px 6px 0; }
/*
 * A block of new tasks (iteration 18, item 16.6), as Asana's: the project
 * or the parent task on top, who added them, then one short row per task
 * in a frame of its own. The block itself is not a row: it does not grey
 * on hover (its rows do), and its buttons stand in the rows' columns.
 */
.inbox-rows > .inbox-block { position: relative; display: flex; flex-wrap: wrap; align-items: flex-start; gap: 12px; padding: 10px 12px 12px 20px; }
.inbox-rows > :is(.inbox-item, .inbox-block) + .inbox-block,
.inbox-rows > .inbox-block + .inbox-item { border-top: 1px solid var(--border-soft); }
.inbox-block-head { display: flex; align-items: center; min-width: 0; font-weight: 600; }
.inbox-block-place { display: inline-flex; align-items: center; gap: 8px; min-width: 0; color: var(--fg); border-radius: 4px; }
.inbox-block-place .inbox-crumb-task { color: var(--fg-faint); }
.inbox-block-place .inbox-crumb-task.done { color: var(--green); }
.inbox-block-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
a.inbox-block-place:hover .inbox-block-name { text-decoration: underline; text-underline-offset: 2px; }
a.inbox-block-place:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
.inbox-block-place.closed { color: var(--fg-faint); font-weight: 400; }
.inbox-block-said { margin: 4px 0 8px; font-size: 13px; line-height: 1.45; color: var(--fg-muted); }
.inbox-block-said time { color: var(--fg-faint); font-size: 12px; margin-left: 6px; }
.inbox-block-rows { border: 1px solid var(--border); border-radius: 8px; overflow: hidden; background: var(--surface); }
/* The block's buttons stand over its head, where a thread's stand, so the rows below take the whole width. */
.inbox-block > .inbox-actions { opacity: 0; position: absolute; top: 6px; right: 12px; }
.inbox-block > .inbox-body > .inbox-block-head { padding-right: 200px; min-height: 22px; }
.inbox-block:hover > .inbox-actions, .inbox-block.hover > .inbox-actions, .inbox-block:focus-within > .inbox-actions { opacity: 1; }
.inbox-block .inbox-fold { margin-top: 6px; }
/* A row of a block: one line, opened by a click like any thread. */
.inbox-arrival {
  --row-bg: var(--surface);
  position: relative; display: flex; align-items: center; gap: 8px; min-width: 0; min-height: 38px;
  padding: 4px 6px 4px 22px; cursor: pointer; background: var(--row-bg);
}
.inbox-arrival + .inbox-arrival { border-top: 1px solid var(--border-soft); }
.inbox-arrival:hover, .inbox-arrival.hover { --row-bg: var(--bg-muted); }
.inbox-arrival.current { --row-bg: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
.inbox-arrival .inbox-dot { position: absolute; left: 8px; top: calc(50% - 4px); }
.inbox-arrival.unread .inbox-dot { background: var(--accent); }
.inbox-arrival .check { flex: 0 0 18px; }
.inbox-arrival .inbox-task { flex: 0 1 auto; min-width: 3em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-arrival.unread .inbox-task { font-weight: 600; }
/* A read title keeps the room of its bold face, as the thread's head does. */
.inbox-arrival .inbox-task::after {
  content: attr(data-label); display: block; height: 0; overflow: hidden;
  visibility: hidden; font-weight: 600; user-select: none; pointer-events: none;
}
.inbox-arrival:hover .inbox-task { text-decoration: underline; text-underline-offset: 2px; }
.inbox-arrival-end { display: flex; align-items: center; gap: 6px; flex: none; margin-left: auto; }
.inbox-arrival-end .due-pill, .inbox-arrival-end .avatar { flex: none; }
.inbox-arrival-end .btn.like { flex: none; width: 48px; justify-content: flex-start; padding-right: 0; color: var(--fg-muted); line-height: 1; }
.inbox-arrival-end .btn.like.on { color: var(--accent); }
.inbox-arrival-end .btn.like.on svg { fill: currentColor; }
.inbox-arrival-end .btn .n, .inbox-talk .n { margin-left: 4px; font-size: 11.5px; color: var(--fg-muted); }
.inbox-arrival-end .btn.like.on .n { color: inherit; }
.inbox-arrival-end .inbox-like-slot { width: 48px; }
.inbox-talk { display: inline-flex; align-items: center; flex: none; width: 40px; color: var(--fg-faint); }
.inbox-arrival-archive { opacity: 0; transition: opacity .1s; }
.inbox-arrival:hover .inbox-arrival-archive, .inbox-arrival.hover .inbox-arrival-archive, .inbox-arrival:focus-within .inbox-arrival-archive { opacity: 1; }
@media (hover: none) {
  .inbox-block > .inbox-actions, .inbox-arrival-archive { opacity: 1; }
}
/* On a phone the likes and comment counts give way to the names; the buttons show, as a thread's do. */
@media (max-width: 720px) {
  .inbox-rows > .inbox-block { padding: 10px 8px 12px 12px; }
  .inbox-block > .inbox-actions, .inbox-arrival-archive { position: static; opacity: 1; }
  .inbox-block > .inbox-body > .inbox-block-head { padding-right: 0; }
  .inbox-arrival-end .btn.like, .inbox-arrival-end .inbox-like-slot, .inbox-talk { display: none; }
}
/* Tabs of one's own sit after the built-in ones; "+" adds one, as Asana's "Add tab". */
.inbox-tabs { flex-wrap: wrap; }
.inbox-add-tab { display: inline-flex; align-items: center; }
.inbox-filter-label { max-width: 380px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inbox-tab-name { padding: 7px 10px; font: inherit; font-size: 13.5px; border: 1px solid var(--border-strong); border-radius: 8px; background: var(--surface); color: var(--fg); outline: none; }
.inbox-tab-name:focus { border-color: var(--focus); }
.inbox-filter-foot { display: flex; justify-content: flex-end; gap: 8px; border-top: 1px solid var(--border-soft, var(--border)); padding-top: 10px; }

/* Панель «Входящих» живёт по тем же правилам, что и панель списка. */
.inbox-bar { position: sticky; top: 0; z-index: 4; background: var(--surface); }
.inbox-tabs { margin-top: 14px; }
/*
 * With no card open the rows run to the window's edge, less the page's
 * 32px, and the bar's "⋯" stands over their end (the toolbar's own 32px).
 * From audit 4 (area 2, Н13) the list kept the width the card would leave
 * even with the card closed, so that no row button moved as a thread
 * opened; the owner reversed that for the closed state (iteration 18, item
 * 16.2): rows stopping halfway across an empty window looked cut off.
 * Opening a thread narrows the rows to the card's edge, so the row buttons
 * move then. The frame's right border stays with the card, only unseen, so
 * the rows keep their width. Below 901px the card covers the list.
 */
@media (min-width: 901px) {
  body.drawer-open .inbox-list { padding-right: 0; }
  body.drawer-open .inbox-rows { border-right-color: transparent; border-radius: 10px 0 0 10px; }
  body.drawer-open .inbox-bar { padding-right: 12px; }
}
/*
 * On the inbox the card gives way to the list (audit 4, area 2, Н13; the
 * owner's decision). With the sidebar open and the card at 44% of the
 * window the list had its 520px only from a 1393px window, so on 1280 and
 * 1366, most laptops, an open thread left the rows a strip. Here the card
 * is cut to what leaves the list 540px: its 520 and 20 for a scrollbar of
 * the view, which 100vw does not count. Never under 400px: where even that
 * leaves the list short, the card keeps its own width. --drawer-own, the
 * person's dragged width included, stays wherever it fits. The cut is a
 * clamp() that is 0 under the floor and the whole overshoot above it, in
 * whole pixels where round() is known, as the default width is. The
 * sidebar's width is --side-now, folded or not (the card's block). The
 * wide card is not cut: it is asked for to read the task, not the list.
 */
@media (min-width: 901px) {
  body:not(.drawer-wide):has(.view > .inbox-list) {
    --inbox-fit: calc(100vw - var(--side-now) - 540px);
    --drawer-w: calc(var(--drawer-own) - clamp(0px, (var(--inbox-fit) - 399px) * 1000, max(0px, var(--drawer-own) - var(--inbox-fit))));
  }
}
/* To the nearest pixel, not down: 100vw comes out a hair under the window
   (1855.9999… at 1856), down made it a pixel less, and not in every place
   it is read, so the rows moved by 1px as the card opened. */
@supports (width: round(down, 10.5px, 1px)) {
  @media (min-width: 901px) {
    body:not(.drawer-wide):has(.view > .inbox-list) { --inbox-fit: round(nearest, 100vw - var(--side-now) - 540px, 1px); }
  }
}
.btn.btn-ghost.on { color: var(--accent); }

/* Шапка проекта */
.project-title-btn { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; padding: 4px 8px; margin-left: -8px; border-radius: 8px; color: var(--fg); min-width: 0; }
.project-title-btn:hover { background: var(--bg-muted); }
.project-title-btn .icon { color: var(--fg-faint); }
/* clip, not hidden: the h1 is trimmed to its capitals, and hidden would cut
   off Й, Ё and the descenders. clip does not let a flex item shrink, hence
   min-width. */
.project-title-btn h1 { overflow: clip visible; min-width: 0; text-overflow: ellipsis; white-space: nowrap; }
.status-set { display: inline-flex; align-items: center; border: 0; background: transparent; padding: 0; cursor: pointer; }
/* Выпадающий выбор состояния проекта прямо из шапки. */
.status-menu { padding: 6px; }
.status-menu-head { font-size: 11.5px; color: var(--fg-faint); text-transform: uppercase; letter-spacing: .04em; padding: 4px 8px 6px; }
.status-menu-item, .status-menu-more {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  border: 0; background: none; cursor: pointer; padding: 5px 8px; border-radius: 6px; font: inherit;
}
.status-menu-item:hover, .status-menu-more:hover { background: var(--bg-muted); }
.status-menu-item svg { margin-left: auto; color: var(--fg-muted); }
.status-menu-more { font-size: 13px; color: var(--fg-muted); }
.status-set.empty .pill.status.none { color: var(--fg-muted); border: 1px dashed var(--border-strong); background: transparent; }
.members-btn { display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--border-strong); background: var(--surface); border-radius: 8px; padding: 4px 10px 4px 6px; font-size: 13px; color: var(--fg); }
.members-btn:hover { background: var(--bg-muted); }
.avatar.more { background: var(--bg-muted); color: var(--fg-muted); font-size: 10px; }
.members-modal { max-width: 520px; }
.members-privacy { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 8px 0 4px; font-size: 13px; }
.members-privacy select { flex: 1; min-width: 200px; }
.members-list { display: flex; flex-direction: column; margin: 10px 0 6px; max-height: 50vh; overflow-y: auto; }
/* Teams as members: above the people, each a row like a person's, with its own head. */
.members-teams { display: flex; flex-direction: column; margin: 10px 0 0; }
.members-teams:empty { display: none; }
.members-teams-head { font-size: 12px; font-weight: 600; color: var(--fg-muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: 2px; }
.members-team-icon { width: 24px; height: 24px; border-radius: 6px; display: inline-flex; align-items: center; justify-content: center; background: var(--bg-muted); color: var(--fg-muted); flex: none; }
.members-team-add { align-self: flex-start; margin-top: 4px; }
.members-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-top: 1px solid var(--border-soft); }
.members-who { flex: 1; min-width: 0; display: flex; flex-direction: column; font-size: 13.5px; }
.members-who .saving { font-size: 12px; }
.members-modal .modal-actions { align-items: center; }
.members-modal .modal-actions .spacer { flex: 1; }
@media (max-width: 720px) { .members-share { display: none; } .members-btn { padding: 4px 6px; } }

/* Файлы проекта */
.files-page { padding: 18px 32px 40px; }
.files-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 12px; margin-top: 12px; }
.file-card { border: 1px solid var(--border); border-radius: 12px; padding: 10px; background: var(--surface); display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.file-thumb { display: block; height: 110px; border-radius: 8px; background: var(--bg-muted); overflow: hidden; display: flex; align-items: center; justify-content: center; color: var(--fg-faint); }
.file-thumb img { width: 100%; height: 100%; object-fit: cover; }
.file-name { font-size: 13px; font-weight: 500; color: var(--fg); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.file-meta { font-size: 12px; color: var(--fg-faint); }
.file-task { font-size: 12.5px; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-muted); }
.file-task:hover { color: var(--accent); text-decoration: underline; }
.file-task.done { color: var(--fg-faint); }

/* Обзор проекта */
.overview .roles { display: flex; flex-wrap: wrap; gap: 8px; }
.overview .role { display: flex; align-items: center; gap: 10px; padding: 8px 12px 8px 8px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); text-align: left; color: var(--fg); min-width: 200px; }
.overview .role:hover { background: var(--bg-muted); }
.overview .role-text { display: flex; flex-direction: column; font-size: 13px; line-height: 1.3; }
.overview .role-text span { color: var(--fg-muted); font-size: 12px; }
.overview .role.add { border-style: dashed; }
.overview-two { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 18px; }
.overview .empty-card { color: var(--fg-muted); font-size: 13px; }
.milestone-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; border-top: 1px solid var(--border-soft); }
.milestone-row:first-child { border-top: 0; }
.milestone-row .spacer { flex: 1; }
.milestone-title { color: var(--fg); text-align: left; }
.milestone-title:hover { color: var(--accent); text-decoration: underline; }
.milestone-row.done .milestone-title { color: var(--fg-faint); }
.links-card { display: flex; flex-direction: column; gap: 4px; }
.linked { display: flex; align-items: center; gap: 8px; border: 0; background: transparent; padding: 7px 6px; border-radius: 8px; text-align: left; color: var(--fg); font-size: 13.5px; }
.linked:hover { background: var(--bg-muted); }
.linked .saving { margin-left: auto; }
@media (max-width: 860px) { .overview-two { grid-template-columns: 1fr; } }

/* Доска и календарь «Моих задач» */
.my-head .tabs-row { margin-top: 12px; }
.column-hint { padding: 10px 12px 12px; font-size: 12.5px; color: var(--fg-faint); line-height: 1.45; }

/* ------------------------------------------------ просмотр вложений ---- */
.viewer { position: fixed; inset: 0; z-index: 80; background: rgba(20, 21, 23, .94); display: flex; flex-direction: column; color: #eee; }
body.viewer-open { overflow: hidden; }
.viewer-head { display: flex; align-items: center; gap: 8px; padding: 10px 14px; min-height: 52px; }
.viewer-head .spacer { flex: 1; }
.viewer-title { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; font-size: 14px; }
.viewer-title b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 50vw; font-weight: 500; }
.viewer-title .icon, .viewer-title svg { flex: 0 0 auto; opacity: .8; }
.viewer-meta { color: rgba(255, 255, 255, .55); font-size: 12.5px; white-space: nowrap; }
.viewer-counter { color: rgba(255, 255, 255, .6); font-size: 12.5px; margin-right: 6px; }
/* flex: none — on a phone the head squeezed the buttons to 24-28 px targets. */
.viewer-btn { flex: none; width: 36px; height: 36px; border: 0; border-radius: 8px; background: transparent; color: #eee; display: inline-flex; align-items: center; justify-content: center; text-decoration: none; }
.viewer-btn:hover { background: rgba(255, 255, 255, .12); }
.viewer-btn:disabled { opacity: .35; cursor: default; background: transparent; }
.viewer-btn.hidden { display: none; }
.viewer-zoom { display: inline-flex; align-items: center; }
.viewer-zoom-label { width: auto; min-width: 52px; padding: 0 6px; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.viewer-stage { flex: 1; min-height: 0; display: flex; align-items: center; justify-content: center; padding: 8px 64px 24px; overflow: auto; }
/*
 * A picture is centred by its auto margins, not by the stage: a centred flex
 * item larger than its box overflows to the left and top as well, where no
 * scrolling reaches, and at 100% the left 530 px of a wide screenshot could
 * never be seen. Auto margins centre a small picture and fall to zero for a
 * large one, which then starts at the stage's padding and scrolls whole.
 */
.viewer-stage[data-kind="image"] { justify-content: flex-start; align-items: flex-start; user-select: none; }
.viewer-img { flex: none; margin: auto; border-radius: 4px; box-shadow: 0 10px 40px rgba(0, 0, 0, .5); }
.viewer-img:not(.sized) { max-width: 100%; max-height: 100%; object-fit: contain; }
.viewer-img.can-zoom-in { cursor: zoom-in; }
.viewer-img.can-zoom-out { cursor: zoom-out; }
.viewer-img.can-pan { cursor: grab; }
.viewer-stage.panning, .viewer-stage.panning .viewer-img { cursor: grabbing; }
.viewer-video { max-width: 100%; max-height: 100%; border-radius: 6px; background: #000; outline: none; }
.viewer-pdf { width: min(1100px, 100%); height: 100%; border: 0; border-radius: 6px; background: #fff; }
.viewer-text-wrap { width: min(1000px, 100%); max-height: 100%; overflow: auto; background: #fff; color: #1e1f21; border-radius: 8px; box-shadow: 0 10px 40px rgba(0, 0, 0, .5); }
.viewer-text { margin: 0; padding: 18px 22px; font: 12.5px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; white-space: pre-wrap; overflow-wrap: anywhere; tab-size: 4; }
.viewer-text-wrap .viewer-note { color: var(--fg-muted); padding: 10px 22px 16px; border-top: 1px solid var(--border); }
.viewer-audio, .viewer-fallback { display: flex; flex-direction: column; align-items: center; gap: 14px; text-align: center; padding: 30px; max-width: 520px; }
.viewer-audio audio { width: min(480px, 80vw); }
.viewer-big-icon { width: 96px; height: 96px; border-radius: 24px; background: rgba(255, 255, 255, .08); display: flex; align-items: center; justify-content: center; color: #ddd; }
.viewer-file-name { font-size: 16px; font-weight: 500; overflow-wrap: anywhere; }
.viewer-note { color: rgba(255, 255, 255, .6); font-size: 13px; }
.viewer-fallback .btn-primary { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; }
.viewer-nav { position: absolute; top: 50%; transform: translateY(-50%); width: 44px; height: 44px; border: 0; border-radius: 50%; background: rgba(255, 255, 255, .12); color: #fff; display: inline-flex; align-items: center; justify-content: center; }
.viewer-nav:hover { background: rgba(255, 255, 255, .24); }
.viewer-nav:disabled { opacity: .25; cursor: default; }
.viewer-nav.prev { left: 12px; }
.viewer-nav.next { right: 12px; }
@media (max-width: 720px) {
  .viewer-stage { padding: 4px 8px 56px; } .viewer-nav { top: auto; bottom: 8px; transform: none; } .viewer-title b { max-width: 40vw; }
  /*
   * The head keeps one row of whole buttons: −, the percentage and + stay,
   * the new tab and the size line go, and the name gives way to them. The
   * percentage is the one way back to "fit" or 100% on a phone, which has
   * no keys 0 and 1: hidden, a picture zoomed in three steps went back one
   * press of − at a time (iteration 16 review).
   */
  .viewer-external, .viewer-meta { display: none; }
  .viewer-zoom-label { min-width: 44px; padding: 0 2px; }
}

/* Строки и плитки файлов: имя открывает просмотр, скачивание отдельно. */
.file .f-icon.can-view, .file .f-thumb { cursor: pointer; }
.file button.f-name { border: 0; background: transparent; text-align: left; padding: 0; font: inherit; }
.file .f-dl, .file .f-link { color: var(--fg-faint); display: flex; padding: 3px; border-radius: 4px; opacity: 0; }
.file .f-link { border: 0; background: transparent; }
.file:hover .f-dl, .file:hover .f-link, .file .f-link:focus-visible { opacity: 1; }
.file .f-dl:hover, .file .f-link:hover { background: var(--bg-muted); color: var(--fg); }
/* Without a pointer to hover with, the row's file actions would never show. */
@media (hover: none) { .file .f-dl, .file .f-link { opacity: 1; } }
.c-file-img, .c-file { border: 0; background: transparent; padding: 0; cursor: pointer; }
.c-file { border: 1px solid var(--border); padding: 3px 8px; }
.file-thumb { width: 100%; border: 0; padding: 0; cursor: pointer; flex-direction: column; gap: 4px; }
.file-thumb.can-view:hover { background: var(--border); }
.file-kind { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); }
.file-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.file-card button.file-name { flex: 1; min-width: 0; border: 0; background: transparent; padding: 0; text-align: left; font: inherit; font-size: 13px; font-weight: 500; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; }
.file-card button.file-name:hover { color: var(--accent); }
.file-dl { color: var(--fg-faint); display: flex; padding: 3px; border-radius: 4px; }
.file-dl:hover { background: var(--bg-muted); color: var(--fg); }

/* ------------------------------------------------- сообщения проекта ---- */
.messages-page { padding: 22px 32px 40px; max-width: 900px; }
.messages-head { display: flex; align-items: flex-start; gap: 16px; margin-bottom: 16px; }
.messages-head h2 { margin: 0 0 4px; font-size: 18px; }
.messages-head .page-desc { margin: 0; max-width: 560px; }
.messages-head .spacer { flex: 1; }
.messages-head .btn-primary { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.messages-list { display: flex; flex-direction: column; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); overflow: hidden; }
.message-row { display: flex; gap: 12px; align-items: flex-start; padding: 14px 16px; border: 0; border-bottom: 1px solid var(--border-soft); background: transparent; text-align: left; color: var(--fg); cursor: pointer; width: 100%; }
.message-row:last-child { border-bottom: 0; }
.message-row:hover { background: var(--bg-muted); }
.message-row.pinned { background: var(--accent-soft, #fff4f2); }
.message-row-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.message-row-title { display: flex; align-items: center; gap: 6px; font-size: 14.5px; color: var(--fg); }
.message-row-title b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.message-row-snippet { font-size: 13px; color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.message-row-meta { display: flex; align-items: center; gap: 10px; font-size: 12px; color: var(--fg-faint); }
.message-row-replies { display: inline-flex; align-items: center; gap: 3px; }
.messages-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 48px 20px; color: var(--fg-muted); text-align: center; border: 1px dashed var(--border-strong); border-radius: 12px; }
.messages-empty b { color: var(--fg); font-size: 15px; }
.messages-empty-icon { width: 56px; height: 56px; border-radius: 16px; background: var(--bg-muted); display: flex; align-items: center; justify-content: center; color: var(--fg-faint); }
.message-card { border: 1px solid var(--border); border-radius: 12px; background: var(--surface); padding: 22px 24px; }
.message-composer { margin-bottom: 16px; display: flex; flex-direction: column; gap: 8px; }
.message-composer.hidden { display: none; }
.message-composer h3 { margin: 0 0 4px; font-size: 15px; }
.message-title-input { width: 100%; padding: 9px 11px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 16px; font-weight: 600; outline: none; }
.message-title-input:focus { border-color: var(--focus); }
.message-card textarea, .message-editor textarea, .message-reply-compose textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 14px; line-height: 1.5; resize: vertical; outline: none; }
.message-card textarea:focus, .message-reply-compose textarea:focus { border-color: var(--focus); }
.message-editor-actions { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
.message-nav { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; }
.message-nav .spacer { flex: 1; }
.message-nav .btn { display: inline-flex; align-items: center; gap: 5px; }
.message-actions { display: flex; align-items: center; gap: 4px; }
.message-actions .btn.on { color: var(--accent); }
.message-actions .btn.danger:hover { background: var(--red-soft); color: var(--red); }
.message-pinned { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; text-transform: uppercase; letter-spacing: .05em; color: var(--accent); margin-bottom: 8px; }
.message-title { margin: 0 0 10px; font-size: 22px; font-weight: 600; letter-spacing: -.2px; line-height: 1.25; }
.message-meta { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--fg-muted); flex-wrap: wrap; }
.message-meta b { color: var(--fg); font-weight: 600; }
.message-meta .del { margin-left: auto; border: 0; background: none; color: var(--fg-faint); cursor: pointer; padding: 2px 6px; border-radius: 4px; opacity: 0; }
.message-reply:hover .message-meta .del { opacity: 1; }
.message-meta .del:hover { background: var(--red-soft); color: var(--red); }
.message-body { margin: 16px 0 8px; font-size: 14.5px; line-height: 1.6; }
.message-editor { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.message-replies { margin-top: 20px; border-top: 1px solid var(--border-soft); padding-top: 14px; display: flex; flex-direction: column; gap: 14px; }
.message-replies h3 { margin: 0; font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--fg-faint); }
.message-reply { display: flex; gap: 10px; align-items: flex-start; }
.message-reply-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; font-size: 14px; line-height: 1.55; }
.message-reply-form { display: flex; gap: 10px; align-items: flex-start; margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border-soft); }
.message-reply-compose { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
@media (max-width: 720px) { .messages-page { padding: 16px; } .messages-head { flex-direction: column; } .message-card { padding: 16px; } }

/* ------------------------------------------------------ формы приёма ---- */
.forms-list { border-top: 1px solid var(--border); max-height: 55vh; overflow-y: auto; }
.forms-row { display: flex; align-items: center; gap: 8px; padding: 10px 4px; border-bottom: 1px solid var(--border-soft); }
.forms-row.off .tpl-name b { color: var(--fg-muted); }
.forms-row .btn-outline { display: inline-flex; align-items: center; gap: 4px; text-decoration: none; }
.forms-row .btn.danger:hover { background: var(--red-soft); color: var(--red); }
.pill.ok { background: var(--green-soft, #e6f5ee); color: var(--green, #1f7a4d); }
.pill.warn { background: var(--amber-soft, #fff3d6); color: var(--amber, #9a6a00); }
.pill.muted { background: var(--bg-muted); color: var(--fg-muted); }
.form-editor { max-width: 760px; }
.tpl-grid.two { grid-template-columns: 1fr 1fr; }
.fb-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 8px; }
.fb-field { border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--bg-muted); display: flex; flex-direction: column; gap: 6px; }
.fb-row { display: flex; align-items: center; gap: 8px; }
.fb-row input[type=text], .fb-row select, .fb-row textarea { padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 13px; background: var(--surface); outline: none; }
.fb-row input[type=text] { flex: 1; min-width: 0; }
.fb-row textarea { flex: 1; resize: vertical; }
.fb-row.sub { padding-left: 30px; flex-wrap: wrap; }
.fb-row.sub input[type=text] { flex: 1; }
.fb-n { width: 22px; height: 22px; border-radius: 50%; background: var(--surface); border: 1px solid var(--border); font-size: 11px; display: inline-flex; align-items: center; justify-content: center; color: var(--fg-muted); flex: 0 0 22px; }
.fb-map { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-muted); flex: 1; }
.fb-map select { flex: 1; }
.fb-req { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--fg-muted); }
.fb-req input { width: 15px; height: 15px; margin: 0; }
.fb-add { align-self: flex-start; display: inline-flex; align-items: center; gap: 6px; }
.fb-public small { display: block; color: var(--fg-muted); font-weight: 400; margin-top: 2px; line-height: 1.4; }
.fb-public b { font-weight: 600; }
.fb-link { display: flex; align-items: center; gap: 10px; margin-top: 8px; padding: 8px 10px; border: 1px dashed var(--border-strong); border-radius: 8px; }
.fb-link code { flex: 1; min-width: 0; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-muted); }

/* страница заполнения */
.form-page { padding: 28px 32px 60px; display: flex; flex-direction: column; align-items: center; gap: 14px; }
.form-page.standalone { min-height: 100%; background: radial-gradient(900px 500px at 10% 0%, #fff1ef 0%, transparent 60%), radial-gradient(700px 500px at 100% 100%, #eef0ff 0%, transparent 60%), var(--bg-muted); justify-content: center; }
.form-brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 17px; color: var(--fg); }
.form-brand-dot { width: 14px; height: 14px; border-radius: 50%; background: var(--accent); display: inline-block; }
.form-card { width: 100%; max-width: 640px; background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 30px 34px; box-shadow: 0 12px 40px rgba(30, 31, 33, .08); }
.form-card h1 { margin: 0 0 6px; font-size: 24px; letter-spacing: -.3px; }
.form-desc { margin: 0; color: var(--fg-muted); line-height: 1.55; white-space: pre-wrap; }
.form-head { margin-bottom: 22px; padding-bottom: 16px; border-bottom: 1px solid var(--border-soft); }
.form-body { display: flex; flex-direction: column; gap: 16px; }
.form-field { display: flex; flex-direction: column; gap: 6px; }
.form-field > label { font-size: 13.5px; font-weight: 600; color: var(--fg); }
.form-field .req { color: var(--red); font-style: normal; }
.form-hint { color: var(--fg-muted); font-size: 12.5px; margin-top: -2px; }
.form-field input:not([type=checkbox]), .form-field select, .form-field textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--border-strong); border-radius: 8px; font: inherit; font-size: 14px; outline: none; background: var(--surface); }
.form-field input:focus, .form-field select:focus, .form-field textarea:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }
.form-check { flex-direction: row; align-items: flex-start; gap: 10px; cursor: pointer; }
.form-check input { width: 18px; height: 18px; margin: 2px 0 0; flex: 0 0 18px; }
.form-check b { font-weight: 600; font-size: 13.5px; }
.form-check small { display: block; color: var(--fg-muted); font-size: 12.5px; margin-top: 2px; }
.form-who { border-top: 1px solid var(--border-soft); padding-top: 14px; display: flex; flex-direction: column; gap: 8px; }
.form-who-title { font-size: 12px; text-transform: uppercase; letter-spacing: .05em; color: var(--fg-faint); }
.form-two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-actions { display: flex; align-items: center; gap: 12px; margin-top: 6px; flex-wrap: wrap; }
.form-actions .btn { display: inline-flex; align-items: center; text-decoration: none; }
.form-thanks { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 12px 0; }
.form-card-icon { width: 56px; height: 56px; border-radius: 16px; background: var(--bg-muted); color: var(--fg-muted); display: flex; align-items: center; justify-content: center; margin-bottom: 6px; }
.form-card-icon.ok { background: var(--green-soft, #e6f5ee); color: var(--green, #1f7a4d); }
.form-foot { color: var(--fg-muted); font-size: 13px; }
@media (max-width: 720px) { .form-page { padding: 16px; } .form-card { padding: 22px 18px; } .form-two, .tpl-grid.two { grid-template-columns: 1fr; } }

/* ------------------------------------------------ правила автоматизации -- */
.rules-modal, .rule-editor { max-width: 760px; }
.rules-list { border-top: 1px solid var(--border); max-height: 60vh; overflow-y: auto; }
.rule-row { display: flex; align-items: flex-start; gap: 8px; padding: 12px 4px; border-bottom: 1px solid var(--border-soft); }
.rule-row.off .rule-text > b, .rule-row.off .rule-when, .rule-row.off .rule-then { color: var(--fg-muted); }
.rule-row .btn { flex: 0 0 auto; }
.rule-row .btn.danger:hover { background: var(--red-soft); color: var(--red); }
.rule-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; font-size: 13px; }
.rule-text > b { font-size: 14px; }
.rule-when, .rule-then { display: flex; gap: 6px; align-items: baseline; flex-wrap: wrap; }
.rule-k { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--fg-faint); min-width: 58px; }
/* Ступень правила — карточка на белом, а не серая коробка с капслоком:
   три таких коробки подряд читались как шум, а не как «когда — если — что». */
.rule-block {
  border: 1px solid var(--border); border-radius: 10px; padding: 12px;
  margin-bottom: 10px; background: var(--surface);
  display: flex; flex-direction: column; gap: 9px;
}
.rule-step { display: flex; align-items: baseline; gap: 8px; }
.rule-step b { font-size: 13.5px; }
.rule-step span { font-size: 12px; color: var(--fg-faint); }
/* «Условие» и «Действие» добавляются кнопкой-призраком: красный в окне
   означает «главное действие» (создать правило) и «удалить», и ссылки
   этого цвета сбивали с толку. */
.rule-add {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 5px;
  border: 1px dashed var(--border-strong); border-radius: 7px; background: transparent;
  padding: 5px 10px; font-size: 12.5px; color: var(--fg-muted);
}
.rule-add:hover { border-style: solid; background: var(--bg-muted); color: var(--fg); }
.rule-toggle { display: flex; align-items: flex-start; gap: 9px; margin-top: 4px; }
.rule-toggle > span { display: flex; flex-direction: column; gap: 1px; }
.rule-toggle b { font-size: 13px; font-weight: 600; }
.rule-toggle span span { font-size: 12px; color: var(--fg-faint); }
.rule-line { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rule-line select, .rule-line input { padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 13px; background: var(--surface); outline: none; max-width: 100%; }
.rule-line input.grow { flex: 1; min-width: 200px; }
/* "One of": the values of a list ticked in the line. */
.rule-values { display: flex; flex-wrap: wrap; gap: 6px; flex: 1 1 100%; }
.rule-value { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; font-size: 12.5px; cursor: pointer; }
.rule-value.on { border-color: var(--accent); color: var(--accent); }
.rule-line .rule-value input { padding: 0; margin: 0; accent-color: var(--accent); }
.rule-inline { display: inline-flex; align-items: center; gap: 4px; font-size: 13px; color: var(--fg-muted); }
.rule-block .link-btn { align-self: flex-start; font-size: 12.5px; }
/* Название с подстановкой: поле и кнопка «+» стоят одним блоком. */
.rule-template { display: flex; align-items: center; gap: 6px; flex: 1; min-width: 240px; }
.rule-template input { flex: 1; min-width: 0; }
/*
 * Настройки созданного согласования — сроки и поля — идут под самой строкой
 * действия: в одну строку они не помещаются, а разбегаться по всему блоку им
 * нельзя, иначе непонятно, к какому действию они относятся.
 */
.rule-sub {
  flex: 1 1 100%; display: flex; flex-direction: column; gap: 6px;
  margin-left: 14px; padding-left: 12px; border-left: 2px solid var(--border);
}
.rule-sub .rule-line { gap: 6px; }
/* A request's headers and body are code: monospaced, and as wide as the rule. */
.rule-http > label { font-size: 12px; color: var(--fg-muted); }
.rule-code {
  width: 100%; box-sizing: border-box; padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: 6px;
  font: 12.5px/1.45 var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
  background: var(--surface); color: var(--fg); outline: none; resize: vertical;
}
/* Поля в настройках согласования — той же породы, что и в строке правила:
   без этого они оставались браузерными и в тёмной теме светились белым. */
.rule-sub input, .rule-sub select {
  padding: 5px 8px; border: 1px solid var(--border-strong); border-radius: 6px;
  font: inherit; font-size: 13px; background: var(--surface); color: var(--fg); outline: none;
}
.rule-sub input[type=checkbox] { padding: 0; }
.rules-log { display: flex; flex-direction: column; border-top: 1px solid var(--border); max-height: 55vh; overflow-y: auto; }
.rules-log-row { display: grid; grid-template-columns: 130px 1fr; gap: 2px 12px; padding: 8px 4px; border-bottom: 1px solid var(--border-soft); font-size: 13px; }
.rules-log-row time { color: var(--fg-muted); font-size: 12px; grid-row: span 2; }
.rules-log-row .ok { color: var(--green); font-size: 12px; }
.rules-log-row .err { color: var(--red); font-size: 12px; }

/* ------------------------------------------------------------- корзина -- */
.trash-page { padding: 8px 32px 40px; max-width: 980px; display: flex; flex-direction: column; gap: 22px; }
.trash-section h2 { margin: 0 0 8px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
.trash-section h2 .n { font-size: 12px; color: var(--fg-faint); font-weight: 500; }
.trash-list { border: 1px solid var(--border); border-radius: 12px; background: var(--surface); overflow: hidden; }
.trash-row { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--border-soft); }
.trash-row:last-child { border-bottom: 0; }
.trash-icon { width: 30px; height: 30px; border-radius: 8px; background: var(--bg-muted); color: var(--fg-muted); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 30px; }
.trash-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.trash-text b { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13.5px; }
.trash-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 12px; color: var(--fg-muted); }
.trash-left { color: var(--fg-faint); }
.trash-row .btn.danger:hover, .toolbar .btn-outline.danger:hover { background: var(--red-soft); color: var(--red); border-color: var(--red-line); }
.trash-empty { padding: 14px 4px; color: var(--fg-muted); font-size: 13px; }
.trash-search { width: min(320px, 100%); padding: 6px 10px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 13px; }
.trash-more { margin-top: 10px; }
@media (max-width: 720px) { .trash-page { padding: 8px 16px 32px; } }

/* ------------------------------------------------------ второй фактор -- */
.mfa-steps { display: flex; flex-direction: column; gap: 16px; margin-top: 12px; }
.mfa-step { display: flex; gap: 12px; align-items: flex-start; }
.mfa-step p { margin: 4px 0 8px; color: var(--fg-muted); font-size: 13px; line-height: 1.5; }
.mfa-step > div > b { display: block; margin-bottom: 2px; }
.mfa-n { width: 24px; height: 24px; border-radius: 50%; background: var(--accent); color: #fff; font-size: 12px; font-weight: 600; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 24px; margin-top: 1px; }
.mfa-qr { display: block; width: fit-content; padding: 8px; background: #fff; border: 1px solid var(--border); border-radius: 10px; margin: 6px 0; }
.mfa-qr svg { display: block; }
.mfa-secret { display: inline-block; font-size: 13px; letter-spacing: .06em; padding: 6px 10px; background: var(--bg-muted); border-radius: 6px; user-select: all; }
.mfa-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; }
.mfa-code { padding: 8px 10px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 15px; letter-spacing: .08em; width: 180px; outline: none; }
.mfa-code:focus { border-color: var(--focus); }
.mfa-row .btn.danger:hover { background: var(--red-soft); color: var(--red); border-color: var(--red-line); }
.mfa-recovery { border-color: var(--amber-line, #f2d9a8); background: var(--amber-soft, #fff8e8); }
.mfa-codes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 18px; margin: 8px 0 4px; }
.mfa-codes code { font-size: 14px; letter-spacing: .05em; padding: 4px 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 6px; user-select: all; }
.mfa-hint { margin-top: 14px; }
.login-form .text-btn { margin-top: 8px; font-size: 13px; }
#mfa-code { letter-spacing: .15em; font-size: 18px; text-align: center; }

/* ------------------------------------------- отпуск и тишина по проекту -- */
.chip.t-amber { background: var(--amber-soft); color: var(--amber-fg, #8a5a00); border-color: var(--amber-line); }
.away-form { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.away-form label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--fg-muted); }
.away-form label.grow { flex: 1; min-width: 220px; }
.away-form input { padding: 7px 9px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 13px; outline: none; }
.away-form input:focus { border-color: var(--focus); }
.away-note { margin-top: 8px; font-size: 13px; color: var(--fg-muted); font-style: italic; }
.wl-cell.away { background-image: repeating-linear-gradient(135deg, transparent 0 6px, rgba(0, 0, 0, .05) 6px 8px); color: var(--fg-faint); }
.home-person.away .avatar { opacity: .6; }
.home-away { font-size: 10.5px; color: var(--amber-fg, #8a5a00); background: var(--amber-soft); padding: 1px 6px; border-radius: 8px; }
.muted-list { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.muted-item { display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 10px; border: 1px solid var(--border); border-radius: 999px; font-size: 13px; }
.muted-item button { border: 0; background: transparent; color: var(--fg-faint); cursor: pointer; display: inline-flex; padding: 2px; border-radius: 50%; }
.muted-item button:hover { background: var(--bg-muted); color: var(--fg); }

/* ------------------------------------------- значения полей-списков --- */
/* Плашка значения: цвет варианта из палитры меток (.t-*), без цвета — нейтральная. */
/* As large as Asana's: the values (status, priority) are read from across the table. */
.fv-chip {
  display: inline-block; font-size: 13px; line-height: 1.15; padding: 4px 9px; border-radius: 6px; font-weight: 500;
  background: var(--chip-bg, #f1f0ef); color: var(--chip-fg, #55595f);
  white-space: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis; vertical-align: middle;
}
.t-plain { --chip-bg: var(--bg-muted); --chip-fg: var(--fg-muted); }
.fv-chips { display: inline-flex; gap: 4px; min-width: 0; overflow: hidden; align-items: center; }
.cell-btn .fv-chips { flex: 1; }
.cell-btn .fv-chip { flex: 0 1 auto; }
.props .v .fv-chips { flex-wrap: wrap; overflow: visible; }
/* Several values in a cell: the first, "+N", and the list on hover. */
.fv-chips.compact .fv-chip { min-width: 0; }
.fv-more {
  flex: none; font-size: 12.5px; font-weight: 500; line-height: 1.15; padding: 4px 7px; border-radius: 6px;
  background: var(--bg-muted); color: var(--fg-muted);
}
.fv-tip {
  position: fixed; z-index: 1000; display: flex; flex-direction: column; gap: 4px; padding: 8px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0, 0, 0, .18);
  max-width: 260px; pointer-events: none;
}
.fv-tip-row { display: flex; }

/* Цветная точка варианта в меню и в редакторе вариантов. */
.swatch {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%; flex: none;
  background: var(--chip-dot, var(--chip-fg, #9aa0a6)); border: 2px solid var(--chip-bg, #f1f0ef); box-sizing: border-box;
}
.menu-item .swatch { margin: 0 3px; }
button.swatch { cursor: pointer; padding: 0; width: 16px; height: 16px; opacity: .55; }
button.swatch:hover { opacity: 1; }
button.swatch.on { opacity: 1; box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--chip-dot, var(--chip-fg, #9aa0a6)); }
button.swatch.t-plain { background: transparent; border: 2px dashed var(--fg-faint); }

/* Редактор вариантов поля. */
.chip.clickable { cursor: pointer; }
.chip.clickable:hover { box-shadow: 0 0 0 2px var(--border-strong); }
.opt-editor { padding: 12px 14px; width: 100%; box-sizing: border-box; }
.opt-title { font-weight: 500; font-size: 13px; margin-bottom: 8px; }
.opt-list { display: flex; flex-direction: column; gap: 6px; max-height: 320px; overflow-y: auto; }
/* A row: its handle, the name, how many tasks hold it, the colour, removal. */
.opt-row { display: flex; gap: 8px; align-items: center; }
.opt-row .opt-value { flex: 1 1 auto; min-width: 80px; }
.opt-row .opt-x { flex: 0 0 24px; }
/* Название варианта правится на месте: поле ввода носит цвет варианта. */
.opt-row .opt-value {
  width: 100%; border: 1px solid var(--border); border-radius: 6px;
  padding: 4px 8px; font: inherit; font-size: 13px; background: var(--bg); color: var(--fg);
}
.opt-row .opt-value:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); outline: none; }
/* Сколько задач держит вариант — рядом с ним, мелко и не мешая. */
.opt-used { font-size: 11.5px; color: var(--fg-faint); white-space: nowrap; }
/* Вид поля и предупреждение о переносе значений — над списком вариантов. */
.opt-type { width: 100%; justify-content: flex-start; }
/* Список видов раскрывается под кнопкой, внутри самого редактора. */
.opt-types {
  margin-top: 4px; border: 1px solid var(--border); border-radius: 8px;
  overflow: hidden auto; max-height: 220px; background: var(--bg);
}
.opt-types .menu-item { width: 100%; }
.opt-warning {
  margin: 0 0 8px; padding: 6px 8px; border-radius: 6px;
  background: var(--amber-soft); color: var(--amber-fg); font-size: 12px;
}
.opt-impact {
  margin: 0 0 8px; padding: 6px 8px; border-radius: 6px;
  background: var(--accent-soft); color: var(--fg-dim); font-size: 12px;
}
.opt-row .fv-chip { justify-self: start; }
/* The option's colour: one dot, full strength; the palette opens from it. */
button.swatch.opt-color { flex: none; }
/* The palette over the editor, as Asana's: the colours by hue, in rows of eight. */
/* Above the popover or the dialog it was opened from. */
.popover.popover-child { min-width: 0; z-index: 1000; padding: 10px; }
.color-palette { display: grid; grid-template-columns: repeat(8, 20px); gap: 8px; }
.color-palette button.swatch, button.swatch.opt-color { width: 20px; height: 20px; opacity: 1; border: 0; }
.color-palette button.swatch.t-plain, button.swatch.opt-color.t-plain { border: 2px dashed var(--fg-faint); }
.color-palette button.swatch:hover { transform: scale(1.15); }
.opt-x { border: 0; background: none; color: var(--fg-faint); font-size: 16px; cursor: pointer; width: 24px; height: 24px; border-radius: 6px; }
.opt-x:hover { color: var(--red-fg); background: var(--red-soft); }
.opt-add { display: flex; gap: 6px; margin-top: 10px; }
.opt-new { flex: 1; padding: 6px 9px; border: 1px solid var(--border-strong); border-radius: 6px; font: inherit; font-size: 13px; background: var(--surface); color: var(--fg); min-width: 0; }
/* A typed number that is not one (markNumber in custom-fields.js). */
input[aria-invalid="true"] { border-color: var(--red) !important; }
.opt-actions { display: flex; justify-content: flex-end; gap: 6px; margin-top: 12px; }

/*
 * Приоритет рисуется теми же плашками, что и варианты полей: своих правил
 * под «высокий» и «низкий» больше нет — цвет приходит от самого варианта
 * или считается по его названию.
 */
.pill[class*="t-"] { background: var(--chip-bg); color: var(--chip-fg); border-color: transparent; }
/* The priority in a cell is the same plate as a field's value, as large. */
.cell-btn .pill[class*="t-"] { font-size: 13px; line-height: 1.15; padding: 4px 9px; border-radius: 6px; font-weight: 500; }

/* ------------------------------------------------- телефон: доводка --- */
@media (max-width: 720px) {
  /* Шапка проекта переносится, а не уезжает вбок: полоса прокрутки под
     названием выглядела поломкой, а кнопки справа обрезались. */
  /* The avatar is in the phone's own top bar: no corner to leave here. */
  .project-head .row { flex-wrap: wrap; overflow-x: visible; row-gap: 8px; padding-right: 0; }
  .project-head .row > .project-title-line { flex: 1 1 0; min-width: 0; }
  .project-head .project-title-line > .project-title-btn { flex: 1 1 0; min-width: 0; }
  .project-head .row h1 { flex: 1 1 auto; max-width: none; overflow: clip visible; min-width: 0; text-overflow: ellipsis; white-space: nowrap; }
  /* The tabs keep their width and scroll with the row; the tools follow them. */
  .tabs-list { flex: 0 0 auto; }
  /* Статус проекта задаётся на вкладке «Обзор» — в шапке телефона он лишний. */
  .project-head .status-set { display: none; }

  /* Строка задачи на главной: название сверху, свойства строкой под ним.
     Свойства не сжимаются, и на узком экране они отбирали у названия всю
     ширину — оставалась пустая строка с плашками. */
  .home-task { flex-wrap: wrap; row-gap: 4px; align-items: flex-start; }
  .home-task .title { flex: 1 1 auto; white-space: normal; }
  .home-task .meta { flex: 1 0 100%; padding-left: 28px; flex-wrap: wrap; row-gap: 4px; }
  .home-widget { padding: 14px 14px 12px; }

  /* Заголовок виджета и вкладки не помещаются в строку: заголовок сверху,
     вкладки под ним отдельной полосой с прокруткой. */
  .home-widget-head { flex-wrap: wrap; row-gap: 6px; }
  .home-widget-head h2 { flex: 1 0 100%; }
  .home-tabs { flex: 1 1 100%; overflow-x: auto; scrollbar-width: none; }
  .home-tabs::-webkit-scrollbar { display: none; }
  .home-tabs button { flex: 0 0 auto; }

  /* Время отрисовки — отладочная мелочь; на телефоне место нужнее поиску. */

  /* Таблица из семи колонок на телефоне нечитаема: строка становится
     карточкой — имя, почта, состояние и действия друг под другом. */
  table.grid.stacked, table.grid.stacked tbody { display: block; }
  table.grid.stacked thead { display: none; }
  table.grid.stacked tr {
    display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px;
    border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
    padding: 10px 12px; margin-bottom: 8px;
  }
  table.grid.stacked td { display: block; border: 0; padding: 0; width: auto; }
  table.grid.stacked tr:hover td, table.grid.stacked tr:hover td.actions { background: transparent; }
  table.grid.stacked td:nth-child(2) { flex: 1 1 auto; min-width: 0; font-weight: 500; }
  table.grid.stacked td.ellipsis, table.grid.stacked td.wide-ellipsis { flex: 1 1 100%; max-width: 100%; }
  table.grid.stacked td.actions {
    position: static; box-shadow: none; margin-left: auto; background: transparent;
  }
  table.grid.stacked .cell-actions { flex-wrap: wrap; justify-content: flex-start; }
}

/* Название задачи на главной не должно исчезать и на широком экране:
   длинное имя проекта в плашке обрезается, а не съедает строку. */
.home-task .title { min-width: 96px; }
.home-task .meta .pill.project { max-width: 190px; overflow: hidden; text-overflow: ellipsis; }

/* Смена пароля в настройках безопасности. */
.pass-form { display: flex; flex-direction: column; gap: 8px; max-width: 320px; margin: 4px 0 10px; }
.pass-input {
  width: 100%; box-sizing: border-box; font: inherit; font-size: 13.5px;
  padding: 7px 10px; border: 1px solid var(--border-strong); border-radius: 8px;
  background: var(--surface); color: var(--fg);
}
.pass-input:focus { outline: none; border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }

/* ------------------------------------------------- календарь срока --- */
/*
 * Две даты задачи правят в одном окне: сверху места под начало и завершение,
 * ниже календарь. Диапазон между ними залит — его видно целиком, и видно,
 * каким он станет, ещё до щелчка.
 */
.dp { padding: 10px; }
.dp-slots { display: flex; align-items: center; gap: 6px; }
.dp-slot {
  flex: 1; min-width: 0; display: flex; align-items: center;
  border: 1px solid var(--border); border-radius: 7px; background: var(--surface);
}
.dp-slot.on { border-color: var(--fg-muted); box-shadow: 0 0 0 2px var(--bg-muted); }
.dp-slot-btn {
  flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 1px;
  border: 0; background: transparent; padding: 5px 8px; text-align: left; cursor: pointer; font: inherit;
}
.dp-slot-name { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; color: var(--fg-faint); }
.dp-slot-date { font-size: 13px; color: var(--fg); }
.dp-slot.dp-empty .dp-slot-date { color: var(--fg-faint); }
.dp-x {
  border: 0; background: transparent; color: var(--fg-faint); cursor: pointer;
  padding: 4px; margin-right: 3px; border-radius: 5px; display: inline-flex;
}
.dp-x:hover { background: var(--border-soft); color: var(--fg); }
.dp-dash { color: var(--fg-faint); font-size: 12px; flex: 0 0 auto; }

.dp-head { display: flex; align-items: center; gap: 4px; margin: 10px 0 2px; }
.dp-nav {
  border: 0; background: transparent; color: var(--fg-muted); cursor: pointer;
  padding: 4px; border-radius: 6px; display: inline-flex;
}
/* Значок каретки смотрит вниз — разворачиваем его по сторонам света. */
.dp-nav .icon { transform: rotate(90deg); }
.dp-nav.next .icon { transform: rotate(-90deg); }
.dp-nav:hover { background: var(--bg-muted); color: var(--fg); }
.dp-month {
  flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 4px;
  border: 0; background: transparent; font: inherit; font-size: 13px; font-weight: 600;
  padding: 4px; border-radius: 6px; cursor: pointer; color: inherit;
}
.dp-month:hover { background: var(--bg-muted); }

/* Shift+click sets a range: some engines extend a text selection across
   the day buttons on it, and the numbers lit up as selected text. */
.dp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px 0; user-select: none; -webkit-user-select: none; }
.dp-wd { text-align: center; font-size: 11px; color: var(--fg-faint); padding: 2px 0 4px; }
.dp-day {
  height: 30px; border: 0; background: transparent; border-radius: 7px;
  font: inherit; font-size: 12.5px; color: var(--fg); cursor: pointer;
}
/* Хвосты соседних месяцев: числа настоящие и выбираются, но это не этот
   месяц — и видно это бледностью, а не пустотой. */
.dp-day.other { color: var(--fg-faint); }
/*
 * Days off are as pale as the neighbouring months' tails, so the working
 * week stands out. Before .inside and .picked: a range or a pick on a
 * weekend still wins. The headers are faint already, so theirs step dimmer.
 */
.dp-day.weekend { color: var(--fg-faint); }
.dp-wd.weekend { opacity: .6; }
.dp-day:hover { background: var(--bg-muted); }
/* Сегодня — обводкой, выбранное — заливкой: цвет здесь ничего не означает,
   красным календарь выглядел тревожно. */
.dp-day.today { font-weight: 700; box-shadow: inset 0 0 0 1.5px var(--border-strong); }
/* Заливка диапазона идёт встык, поэтому у середины углов нет. */
.dp-day.inside { background: var(--bg-muted); border-radius: 0; color: var(--fg); }
.dp-day.picked { background: var(--fg); color: var(--surface); }
.dp-day.picked.other { color: var(--surface); }
.dp-day.picked:hover { background: var(--fg); }
.dp-day.picked.today { box-shadow: none; }
.dp-day.edge-start { border-radius: 7px 0 0 7px; }
.dp-day.edge-due { border-radius: 0 7px 7px 0; }

.dp-foot { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.dp-foot .spacer { flex: 1; }
.dp-time {
  width: 96px; padding: 5px 6px; border: 1px solid var(--border-strong);
  border-radius: 6px; outline: none; background: transparent; font: inherit; font-size: 12.5px;
}
.dp-time:disabled { color: var(--fg-faint); border-color: var(--border); }
/* «Очистить» снимает даты, а не удаляет задачу: красным пугать незачем. */
.dp-foot .dp-clear { margin-right: 0; }

/* Имя автора в ленте: такое же по виду, но ведёт к задачам человека. */
.c-head .who-link {
  border: 0; background: transparent; padding: 0; font: inherit; font-weight: 700;
  color: inherit; cursor: pointer; border-radius: 4px; text-decoration: none;
}
.c-head .who-link:hover { text-decoration: underline; }

/* Настройка поля: имя, варианты и пометка о том, куда это применится. */
.opt-label { display: block; font-size: 12px; color: var(--fg-muted); margin: 0 0 10px; }
.opt-label .opt-name {
  display: block; width: 100%; margin-top: 4px; padding: 7px 9px; font: inherit;
  border: 1px solid var(--border-strong); border-radius: 6px; outline: none; background: transparent;
}
.opt-label .opt-name:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }
/* Имя метки правится прямо в строке списка: чип тут был бы лишним щелчком. */
.opt-row .tag-name {
  width: 100%; border: 1px solid var(--border); border-radius: 6px;
  padding: 5px 8px; font: inherit; font-size: 13px; color: var(--fg); background: var(--bg);
}
.opt-row .tag-name:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); outline: none; }
.opt-note { margin: 8px 0 0; font-size: 11.5px; color: var(--fg-faint); }
/*
 * Значение, которого больше нет среди вариантов поля. Удалять его у задач
 * вместе с вариантом — молча терять данные, поэтому оно остаётся, но видно,
 * что этого больше не выбрать.
 */
.fv-chip.gone { text-decoration: line-through; opacity: .65; }

/*
 * Что изменилось в описании: добавленное выделено, убранное зачёркнуто —
 * как в истории Asana. Показывается по кнопке: в ленте нужен сам факт
 * правки, а подробности — по запросу.
 */
.diff {
  margin: 6px 0 2px; padding: 8px 10px; border-radius: 7px;
  background: var(--bg-muted); font-size: 12.5px; line-height: 1.5;
  white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow: auto;
}
.diff.hidden { display: none; }
.diff .d-add { background: #dff3e4; color: #14532d; border-radius: 3px; }
.diff .d-del { background: #fde8e6; color: #7f1d1d; text-decoration: line-through; border-radius: 3px; }
:root[data-theme="dark"] .diff .d-add { background: #1d3a29; color: #b7e4c7; }
:root[data-theme="dark"] .diff .d-del { background: #3d2220; color: #f3c0bb; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .diff .d-add { background: #1d3a29; color: #b7e4c7; }
  :root:not([data-theme="light"]) .diff .d-del { background: #3d2220; color: #f3c0bb; }
}

/* Список месяцев внутри календаря: на месте сетки дней, с прокруткой. */
.dp-months { max-height: 218px; overflow: auto; display: flex; flex-direction: column; gap: 1px; }
.dp-month-item {
  border: 0; background: transparent; text-align: left; font: inherit; font-size: 12.5px;
  padding: 6px 10px; border-radius: 6px; cursor: pointer; color: var(--fg);
}
.dp-month-item:hover { background: var(--bg-muted); }
.dp-month-item.on { background: var(--bg-muted); color: var(--fg); font-weight: 600; }
.dp-month.on { background: var(--bg-muted); }

/* Задача, о которой запись в истории: название и переход к ней. */
/* Начало комментария в истории: одна строка, дальше — многоточие. */
.act-snippet {
  display: inline-block; max-width: 100%; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; color: var(--fg-dim);
}
.activity-item .task-link {
  border: 0; background: transparent; padding: 0; font: inherit; color: var(--accent);
  cursor: pointer; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.activity-item .task-link:hover { text-decoration: underline; }

/* Выбор одного из списка в диалогах: наша кнопка с меню вместо родного
   <select> — он выглядел деталью из другой программы, а в правиле таких
   деталей до десятка в ряд. */
.pick-btn {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  border: 1px solid var(--border-strong); border-radius: 7px; background: var(--surface);
  padding: 6px 9px; font: inherit; font-size: 13px; color: var(--fg); text-align: left;
}
.pick-btn:hover { border-color: var(--fg-faint); }
/* A date not set yet (dateButton): the word for "none" reads quieter than a date. */
.pick-btn.unset .pick-label { color: var(--fg-faint); }
.pick-btn svg { flex: none; color: var(--fg-faint); }
.pick-btn .avatar { width: 18px; height: 18px; font-size: 8.5px; flex: none; }
.pick-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Пустое окно: значок, строка, примеры и кнопка — вместо абзаца текста. */
.modal-empty {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 26px 12px; text-align: center;
}
.modal-empty-mark {
  width: 42px; height: 42px; border-radius: 50%; display: grid; place-items: center;
  background: var(--bg-muted); color: var(--fg-muted);
}
.modal-empty b { font-size: 14px; }
.modal-empty-hint { font-size: 13px; color: var(--fg-muted); }
.modal-empty-list {
  margin: 4px 0 8px; padding: 0; list-style: none; text-align: left;
  display: flex; flex-direction: column; gap: 5px;
  font-size: 12.5px; color: var(--fg-muted);
}
.modal-empty-list li { padding-left: 14px; position: relative; }
.modal-empty-list li::before { content: '·'; position: absolute; left: 4px; color: var(--fg-faint); }

/* Строка шаблона: правка и удаление проявляются под курсором — в спокойном
   виде читаются названия, а не три кнопки в каждой строке. */
.tpl-row-tools { display: flex; gap: 2px; opacity: 0; transition: opacity .12s; }
.tpl-row:hover .tpl-row-tools,
.tpl-row:focus-within .tpl-row-tools { opacity: 1; }
/* На устройстве без курсора «под курсором» не бывает — там видно всегда. */
@media (hover: none) { .tpl-row-tools { opacity: 1; } }

/* Поля проекта и метки в шаблоне */
.tpl-fields { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.tpl-field { display: flex; flex-direction: column; gap: 5px; }
.tpl-tags { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.tpl-tag { display: inline-flex; align-items: center; gap: 4px; border: 0; cursor: pointer; }
/* Пикер в поле — кнопка по содержимому, а не во всю ширину строки. */
.tpl-field .pick-btn { align-self: flex-start; }
.tpl-tag svg { opacity: .6; }
.tpl-tag:hover svg { opacity: 1; }

/* Окно бага */
.bug-modal { max-width: 640px; }
.bug-desc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 5px; }
.bug-desc-head label { margin: 0; }
.bug-desc {
  width: 100%; font: inherit; font-size: 13px; line-height: 1.55;
  border: 1px solid var(--border-strong); border-radius: var(--radius);
  padding: 9px 11px; background: var(--surface); color: var(--fg); outline: none; resize: vertical;
}
.bug-desc:focus { border-color: var(--focus); box-shadow: 0 0 0 3px var(--focus-soft); }
.field-hint { font-size: 12px; color: var(--fg-faint); margin-top: 4px; display: block; }

/* Letting an app in (OAuth): one card in the middle of the screen. */
.oauth-page { display: flex; justify-content: center; padding: 48px 16px; }
.oauth-card { max-width: 520px; width: 100%; display: flex; flex-direction: column; gap: 12px; }
.oauth-card h2 { margin: 0; font-size: 18px; }
.oauth-card p { margin: 0; line-height: 1.5; }
.oauth-mark { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; background: var(--accent-soft, var(--bg-soft)); color: var(--accent); }
.oauth-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

/* A row taken by its handle to change the order (public/js/sortable.js). */
.drag-grip { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; width: 16px; color: var(--fg-faint); cursor: grab; touch-action: none; }
.drag-grip:hover { color: var(--fg-muted); }
.dragging { opacity: .85; background: var(--bg-muted); box-shadow: 0 4px 14px rgba(0, 0, 0, .12); }
.dragging .drag-grip { cursor: grabbing; }
.opt-row > :not(.opt-value) { flex-shrink: 0; }

/* ------------------------------------------------------ правила, люди --- */
/* The project's Rules tab. */
.rules-page { padding: 16px 32px 40px; max-width: 980px; }
/* On the page the list runs to its end and the page scrolls: a box of 60vh
   left the bottom of the screen empty under it. */
.rules-page .rules-list { max-height: none; overflow: visible; }
.rules-head { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 14px; }
.rules-head .spacer { flex: 1; }
.rules-head .btn { flex: 0 0 auto; white-space: nowrap; }
.rules-note { margin: 0; color: var(--fg-muted); font-size: 13px; max-width: 620px; line-height: 1.5; }
@media (max-width: 720px) { .rules-page { padding: 12px 16px 32px; } .rules-head { flex-wrap: wrap; } }

/* People, under Analytics. */
.people-page { padding: 8px 32px 40px; display: flex; flex-direction: column; gap: 22px; }
.people-group h2 { margin: 0 0 10px; font-size: 15px; display: flex; align-items: center; gap: 8px; }
.people-group h2 .n { font-size: 12px; color: var(--fg-faint); font-weight: 500; }
.people-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 8px; }
.people-card {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px; min-width: 0;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface); color: var(--fg);
  text-align: left; cursor: pointer; font: inherit;
}
.people-card:hover { border-color: var(--border-strong); background: var(--bg-soft, var(--bg-muted)); }
.people-card.away { opacity: .75; }
.people-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.people-who b { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.people-who span { font-size: 12px; color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 720px) { .people-page { padding: 8px 16px 32px; } }

/* A long project name keeps to one line in the sidebar. */
.sidebar .nav-item .nav-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The page's first line leaves the corner to the avatar. */
@media (min-width: 861px) { .page-head, .home-head { padding-right: 88px; } }

/* "Show N earlier updates" in All activity, as in Asana. */
/* A block of its own, as wide as its words: the plate under the pointer is the button, not the feed's width. */
.act-more { display: flex; width: fit-content; margin: 4px -6px 0; font-size: 12.5px; font-weight: 500; }

/* A subtask's project inherited from its parent: the project alone, and a
   removal that is refused, with the reason shown on hover at once. */
.proj-row.inherited .pill { opacity: .85; }
.proj-inherited { font-size: 12px; color: var(--fg-faint); }
.f-del.off { position: relative; opacity: .55; cursor: help; }
.f-del.off:hover { opacity: .9; background: transparent; color: var(--fg-muted); }
.f-del.off[data-tip]:hover::after {
  content: attr(data-tip); position: absolute; right: -6px; bottom: calc(100% + 8px); z-index: 20;
  width: 240px; padding: 9px 11px; border-radius: 8px; white-space: normal; text-align: left;
  background: var(--tooltip-bg, #2b2d31); color: #fff; font-size: 12.5px; line-height: 1.4;
  box-shadow: 0 6px 20px rgba(0, 0, 0, .25); pointer-events: none;
}

/* ---------------------------------------------- подзадачи на карточке --- */
/* The progress chip opens the card's subtasks. */
.card-sub-toggle { display: inline-flex; align-items: center; gap: 3px; border: 0; background: transparent; padding: 1px 3px; margin: -1px -3px; border-radius: 5px; color: inherit; cursor: pointer; }
.card-sub-toggle:hover { background: var(--bg-muted); }
.card-sub-toggle > .icon { transform: rotate(-90deg); transition: transform .12s; color: var(--fg-faint); }
.card-sub-toggle.open > .icon { transform: none; }
.card-subs { margin: 8px -4px -2px; padding-top: 6px; border-top: 1px solid var(--border-soft, var(--border)); display: flex; flex-direction: column; gap: 1px; }
.card-sub { display: flex; align-items: center; gap: 7px; padding: 4px; border-radius: 6px; font-size: 12.5px; cursor: grab; }
.card-sub:hover { background: var(--bg-muted); }
.card-sub.picked { background: var(--accent-soft, var(--bg-muted)); box-shadow: inset 0 0 0 1px var(--accent); }
.card-sub.dragging { opacity: .4; }
/* Picked with Ctrl/Cmd or Shift to be dragged together, and kept picked
   after the drop where they landed. */
.card.picked { box-shadow: 0 0 0 2px var(--accent); }
/* A task the held rows go into when dropped: held still over its middle.
   The words say it in plain text, a frame alone read as "between". */
.tv-row.drop-into { box-shadow: inset 0 0 0 2px var(--accent); background: var(--accent-soft, var(--bg-muted)); }
.tv-row.drop-into .task-title-host::after {
  content: 'подзадачей ↓'; margin-left: 8px; padding: 1px 8px; border-radius: 999px;
  background: var(--accent); color: #fff; font-size: 11.5px; font-weight: 500; white-space: nowrap; flex: none; align-self: center;
}
/* The other picked rows while one of them is dragged for all: dimmed in
   place, not taken out, so the rows under the pointer do not jump. */
.tv-row.drag-hidden { opacity: .35; }
/* Several held: one chip with how many, in place of the first card's picture. */
.drag-count {
  position: fixed; top: -200px; left: -200px; z-index: 1000; padding: 7px 14px; border-radius: 16px;
  background: var(--accent); color: #fff; font-size: 13px; font-weight: 600; white-space: nowrap;
  box-shadow: var(--shadow-md);
}
.card-sub.done .card-sub-title { color: var(--fg-muted); text-decoration: line-through; }
/* A section inside the task: the heading band of the list and the card, not a line of work. */
.card-sub.sec { font-weight: 600; color: var(--fg-muted); }
.card-sub.sec:not(.picked) { background: var(--section-fill); }
.card-sub-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.card-sub .check { width: 16px; height: 16px; flex: 0 0 16px; }
.card-sub-empty { font-size: 12px; color: var(--fg-faint); padding: 6px 4px; }
.card-sub-placeholder { height: 3px; margin: 2px 4px; border-radius: 2px; background: var(--accent); }
/* Held over long enough: the card opens and takes the drop. */
.card.drop-into { border-color: var(--accent); box-shadow: 0 0 0 2px var(--focus-soft); }
.card-subs.drop-ready { min-height: 30px; }

/* ---------------------------------------------- «Мои задачи» как в Asana --- */
/* The Filter panel: quick filters, the added ones as rows, "Add filter". */
.myf-panel { padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; box-sizing: border-box; }
.myf-head { display: flex; align-items: center; justify-content: space-between; }
.myf-head b { font-size: 14px; font-weight: 600; }
.myf-label { font-size: 12px; color: var(--fg-muted); }
.myf-chips, .myf-choices { display: flex; flex-wrap: wrap; gap: 8px; }
.myf-chip { display: inline-flex; align-items: stretch; border: 1px solid var(--border-strong); border-radius: 999px; background: var(--surface); overflow: hidden; }
.myf-chip.on { border-color: var(--accent); background: var(--accent-soft, var(--bg-muted)); }
.myf-chip-main { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; padding: 5px 11px; font: inherit; font-size: 13px; color: var(--fg); cursor: pointer; }
.myf-chip.on .myf-chip-main { color: var(--accent); }
.myf-chip-caret { border: 0; border-left: 1px solid var(--border-strong); background: transparent; padding: 0 7px; color: var(--fg-muted); cursor: pointer; }
.myf-chip-main:hover, .myf-chip-caret:hover { background: var(--bg-muted); }
.myf-choice { border: 1px solid var(--border); background: var(--surface); border-radius: 6px; padding: 4px 9px; font-size: 12.5px; color: var(--fg); cursor: pointer; }
.myf-choice.on { border-color: var(--accent); color: var(--accent); }
.myf-rows { display: flex; flex-direction: column; gap: 6px; border-top: 1px solid var(--border-soft, var(--border)); padding-top: 10px; }
.myf-row { display: flex; align-items: center; gap: 8px; }
.myf-row-name { display: inline-flex; align-items: center; gap: 6px; width: 160px; flex: 0 0 160px; font-size: 13px; color: var(--fg-muted); }
.myf-row-name > span, .myf-kind > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.myf-row select { flex: 1; min-width: 0; padding: 5px 8px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 13px; }
/* A column of choices: the test on the line, the values ticked under it. */
.myf-row-multi { flex-direction: column; align-items: stretch; gap: 6px; }
.myf-row-line { display: flex; align-items: center; gap: 8px; }
.myf-values { display: flex; flex-wrap: wrap; gap: 6px; padding-left: 168px; max-height: 180px; overflow-y: auto; }
.myf-value { display: inline-flex; align-items: center; gap: 5px; border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; font-size: 12.5px; cursor: pointer; }
.myf-value.on { border-color: var(--accent); color: var(--accent); }
.myf-value input { margin: 0; accent-color: var(--accent); }
.myf-span { display: inline-flex; align-items: center; gap: 6px; }
.myf-span input { padding: 4px 6px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 12.5px; }
.myf-add { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; font-size: 13px; }
.myf-kinds { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; border-top: 1px solid var(--border-soft, var(--border)); padding-top: 8px; }
.myf-kind { display: flex; align-items: center; gap: 9px; padding: 7px 8px; border: 0; background: transparent; border-radius: 6px; font: inherit; font-size: 13.5px; color: var(--fg); text-align: left; cursor: pointer; }
.myf-kind:hover { background: var(--bg-muted); }
.my-head + .toolbar .my-search { width: 220px; }

/* The personal rule editor. */
.my-rule-form { display: flex; flex-direction: column; gap: 14px; padding: 4px 20px 8px; }
.my-rule-field { display: flex; flex-direction: column; gap: 6px; }
.my-rule-field > span { font-size: 12px; font-weight: 600; color: var(--fg-muted); }
.my-rule-field input[type="text"], .my-rule-line select, .my-rule-line input {
  padding: 6px 9px; border: 1px solid var(--border-strong); border-radius: 6px; background: var(--surface); color: var(--fg); font: inherit; font-size: 13px;
}
.my-rule-line { display: flex; align-items: center; gap: 8px; }
.my-rule-line select { flex: 1; min-width: 0; }
.my-rule-days { width: 90px; }
.my-rule-text { flex: 1; }

/* An archived project says so above its head, with the way back. */
.archived-banner { display: flex; align-items: center; gap: 10px; margin: 2px 0 10px; max-width: 760px; padding: 8px 12px; border-radius: 8px; background: var(--bg-muted); color: var(--fg-muted); font-size: 13px; }
.archived-banner .btn { margin-left: auto; }

/*
 * Values of fields (status, priority…) as Asana draws them: a solid,
 * saturated plate with dark text, the same in both themes. The pale plates
 * with coloured text sank into a dark table — "Merged", "High" read as grey
 * words — while Asana's are seen from across the screen. Tags keep their
 * quieter chips (--chip-bg); only values take these.
 */
.t-rose { --solid-bg: #f19385; }
.t-amber { --solid-bg: #f5ab73; }
.t-gold { --solid-bg: #f7c85f; }
.t-yellow { --solid-bg: #f4dc6b; }
.t-olive { --solid-bg: #d3e470; }
.t-lime { --solid-bg: #b0dd8b; }
.t-emerald { --solid-bg: #7fd8ac; }
.t-teal { --solid-bg: #86ddd3; }
.t-sky { --solid-bg: #97c8f5; }
.t-indigo { --solid-bg: #aab1f5; }
.t-violet { --solid-bg: #c6abf3; }
.t-magenta { --solid-bg: #e3aae7; }
.t-fuchsia { --solid-bg: #f3a3c9; }
.t-pink { --solid-bg: #f6c1ce; }
.t-slate { --solid-bg: #c9d0d7; }
.fv-chip[class*="t-"]:not(.t-plain), .pill.solid[class*="t-"], .cell-btn .pill[class*="t-"], .card .pill[class*="t-"] {
  background: var(--solid-bg, var(--chip-bg)); color: #1e1f21;
}
.fv-chip, .cell-btn .pill[class*="t-"] { font-size: 14px; line-height: 1.2; padding: 4px 10px; border-radius: 6px; font-weight: 500; }

/*
 * A date of an open task reads as the name beside it does; a done task's
 * date is faint, as in Asana. Both were the same muted grey, and on a board
 * of done and open tasks the dates ran together.
 */
.date-btn:not(.empty) { color: var(--fg); }
.tv-row.done .date-btn, .task-row.done .date-btn, .subtask-row.done .date-btn { color: var(--fg-faint); }
.card .pill:not(.overdue):not(.due-soon):not([class*="t-"]) { color: var(--fg); }
/* As specific as the rule above, or it lost to it and a done card's date stayed bright. */
.card.done .pill:not(.overdue):not(.due-soon):not([class*="t-"]) { color: var(--fg-faint); }
.card.done .pill[class*="t-"], .tv-row.done .fv-chip, .tv-row.done .cell-btn .pill[class*="t-"] { opacity: .55; }

/* The bulk bar's "Projects": each project of the picked tasks with its section, as in Asana. */
.bulk-projects { padding: 12px; display: flex; flex-direction: column; gap: 6px; }
.bulk-proj-head { font-size: 12px; font-weight: 600; color: var(--fg-muted); margin-bottom: 2px; }
.bulk-proj-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.bulk-proj-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.bulk-proj-section { display: inline-flex; align-items: center; gap: 4px; flex: 0 1 auto; max-width: 160px; border: 0; background: transparent; color: var(--fg-muted); font: inherit; font-size: 12.5px; padding: 3px 6px; border-radius: 5px; cursor: pointer; }
.bulk-proj-section span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bulk-proj-section:hover { background: var(--bg-muted); color: var(--fg); }
.bulk-proj-add { padding-top: 6px; }

