/* Tasks board. The theme follows the OS unless the reader picked one on
   Налаштування → Дисплей, which writes `data-theme` on <html> and wins over
   the media query. Both branches read the same `--dark-*` values below, so a
   colour is edited in one place and cannot drift between them. */
:root {
  --bg: #f6f7f9; --panel: #fff; --ink: #1a1d21; --dim: #6b7280;
  --line: #e3e6ea; --accent: #2563eb;
  --ok: #15803d; --warning: #b45309;
  /* Waiting on somebody with no date: a state to notice, not to be alarmed by.
     It wore --warning, which in the light theme is a burnt amber that reads as
     red at a glance and put every quiet correspondence next to a real overdue
     date. Grey says the same thing without shouting it. Owner, 2026-09-15. */
  --waiting: #6b7280;
  /* Closed work is black and their move is green — the colour closed used to
     wear. Owner, 2026-09-15: a finished row should sink into the page, and a
     thread where somebody else owes the answer is not a problem of ours, which
     is what the purple was making it look like. */
  --closed: #15171b; --theirs: #15803d;
  --decision: #7c3aed; --work: #0f766e; --outcome: #15803d; --measure: #0e7490;
  --urgent: #dc2626; --high: #ffa373; --medium: #64748b; --low: #94a3b8;
  --priority-medium: #58b3e2;
  --status-backlog: #15171b; --status-todo: #58b3e2; --status-progress: #15803d;
  --status-review: #dc2626; --status-done: #cbd5e1; --status-canceled: #94a3b8;

  /* The dark palette itself. Defined once, applied by the two rules below. */
  --dark-bg: #14161a; --dark-panel: #1c1f24; --dark-ink: #e6e8eb; --dark-dim: #9aa1ab;
  --dark-line: #2a2e35; --dark-accent: #60a5fa;
  --dark-ok: #4ade80; --dark-warning: #fbbf24;
  --dark-waiting: #9aa1ab;
  --dark-closed: #05070a; --dark-theirs: #4ade80;
  --dark-decision: #c084fc; --dark-work: #5eead4; --dark-outcome: #4ade80; --dark-measure: #67e8f9;
  --dark-urgent: #f87171; --dark-high: #ffa373; --dark-medium: #94a3b8; --dark-low: #6b7280;
  --dark-priority-medium: #58b3e2;
  --dark-status-backlog: #050608; --dark-status-todo: #58b3e2; --dark-status-progress: #4ade80;
  --dark-status-review: #f87171; --dark-status-done: #64748b; --dark-status-canceled: #4b5563;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: var(--dark-bg); --panel: var(--dark-panel); --ink: var(--dark-ink); --dim: var(--dark-dim);
    --line: var(--dark-line); --accent: var(--dark-accent);
    --ok: var(--dark-ok); --warning: var(--dark-warning);
    --waiting: var(--dark-waiting);
    --closed: var(--dark-closed); --theirs: var(--dark-theirs);
    --decision: var(--dark-decision); --work: var(--dark-work);
    --outcome: var(--dark-outcome); --measure: var(--dark-measure);
    --urgent: var(--dark-urgent); --high: var(--dark-high);
    --medium: var(--dark-medium); --low: var(--dark-low);
    --priority-medium: var(--dark-priority-medium);
    --status-backlog: var(--dark-status-backlog); --status-todo: var(--dark-status-todo);
    --status-progress: var(--dark-status-progress); --status-review: var(--dark-status-review);
    --status-done: var(--dark-status-done); --status-canceled: var(--dark-status-canceled);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: var(--dark-bg); --panel: var(--dark-panel); --ink: var(--dark-ink); --dim: var(--dark-dim);
  --line: var(--dark-line); --accent: var(--dark-accent);
  --ok: var(--dark-ok); --warning: var(--dark-warning);
  --waiting: var(--dark-waiting);
  --closed: var(--dark-closed); --theirs: var(--dark-theirs);
  --decision: var(--dark-decision); --work: var(--dark-work);
  --outcome: var(--dark-outcome); --measure: var(--dark-measure);
  --urgent: var(--dark-urgent); --high: var(--dark-high);
  --medium: var(--dark-medium); --low: var(--dark-low);
  --priority-medium: var(--dark-priority-medium);
  --status-backlog: var(--dark-status-backlog); --status-todo: var(--dark-status-todo);
  --status-progress: var(--dark-status-progress); --status-review: var(--dark-status-review);
  --status-done: var(--dark-status-done); --status-canceled: var(--dark-status-canceled);
  color-scheme: dark;
}
:root[data-theme="light"] { color-scheme: light; }
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--bg); color: var(--ink);
  font: 14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
a { color: inherit; text-decoration: none; }

.top {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 12px 20px; background: var(--panel); border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 5;
}
/* The mark is a 1770×490 bitmap kept at its own proportion; only the two files
   differ — dark ink for a light ground, light ink for a dark one — and the
   swap follows the theme, whether that came from the system or from the
   reader's own choice. The URLs themselves come from render_brand(), which
   stamps each with the file's mtime; the plain paths below are the fallback
   for a header rendered without it. */
.brand {
  display: block; flex: none; width: 72px; height: 20px;
  background: var(--logo-light, url("img/tasks_logo_on_white.png")) no-repeat center / contain;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .brand {
    background-image: var(--logo-dark, url("img/tasks_logo_on_black.png"));
  }
}
:root[data-theme="dark"] .brand {
  background-image: var(--logo-dark, url("img/tasks_logo_on_black.png"));
}
.tools a, .crumb a {
  padding: 4px 9px; border-radius: 6px; color: var(--dim); font-size: 13px;
}
.tools a:hover { background: var(--bg); color: var(--ink); }

/* New Card Plus Button */
.tools a.new-card-plus {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--accent); color: #fff; text-decoration: none;
  margin: 0 10px 0 6px; font-weight: bold;
  font-size: 16px;
  padding: 0 1px 3px 0;
  line-height: 1; vertical-align: middle;
}
.tools a.new-card-plus:hover {
  background: var(--accent);
  opacity: 0.9;
  color: #fff;
}

/* Two menus instead of a row of eleven project chips. A native select is also
   the one control a phone renders as a full-height picker, which is why the
   scrolling strip that used to live here is gone rather than restyled. */
.filters { display: flex; gap: 8px; flex: 1; min-width: 0; }
.filters select {
  font: inherit; font-size: 13px; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line); border-radius: 8px;
  padding: 6px 28px 6px 10px; max-width: 100%;
  appearance: none;
  /* The arrow is drawn here because appearance:none takes the native one away,
     and a menu that does not look like a menu is not one. */
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M2 4.5 6 8.5 10 4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 11px;
}
.filters select:hover { border-color: var(--dim); }
.filters select:focus { outline: none; border-color: var(--accent); }
.filters button { font: inherit; font-size: 13px; padding: 6px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink); }
.tools { display: flex; align-items: center; gap: 6px; }
.menu-toggle {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 3px;
  width: 30px; height: 30px; padding: 0; border: 1px solid var(--line); border-radius: 7px;
  background: var(--panel); color: var(--dim); cursor: pointer;
}
.menu-toggle span {
  display: block; width: 14px; height: 1.5px; margin: 0 auto;
  background: currentColor; border-radius: 2px;
}
.menu-toggle:hover, .menu-toggle:focus-visible {
  color: var(--accent); border-color: var(--accent); outline: none;
}
.sync {
  display: inline-flex; align-items: center; min-height: 26px; padding: 3px 8px;
  border: 1px solid var(--line); border-radius: 20px; color: var(--dim);
  font: 11px/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
}
.sync-synced { color: var(--ok); border-color: var(--ok); }
.sync-local_changes, .sync-push_pending, .sync-update_available { color: var(--warning); border-color: var(--warning); }
.sync-diverged, .sync-unknown { color: var(--urgent); border-color: var(--urgent); }

.sync-spinner {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border: 1px solid var(--line); border-radius: 20px;
  color: var(--warning); background: var(--bg);
  cursor: help; flex: none;
}
.sync-spinner:hover {
  border-color: var(--warning);
  background: color-mix(in srgb, var(--warning) 10%, var(--bg));
}
.sync-spinner:focus-visible {
  outline: 2px solid var(--warning); outline-offset: 1px;
}
.sync-spinner svg {
  display: block;
  animation: sync-spin .85s linear infinite;
}
@keyframes sync-spin {
  to { transform: rotate(360deg); }
}

.filter { padding: 10px 20px 0; color: var(--dim); }

.drawer-scrim {
  position: fixed; inset: 0; z-index: 20;
  background: color-mix(in srgb, var(--ink) 26%, transparent);
}
.side-panel {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 21;
  width: min(360px, calc(100vw - 34px)); padding: 14px;
  background: var(--panel); border-left: 1px solid var(--line);
  transform: translateX(100%); transition: transform .18s ease;
  visibility: hidden; overflow-y: auto;
}
.panel-open .side-panel {
  transform: translateX(0); visibility: visible;
  box-shadow: -20px 0 45px rgba(0, 0, 0, .16);
}
.panel-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 2px 0 12px; border-bottom: 1px solid var(--line);
}
.panel-head h2, .history-block h2 {
  margin: 0; color: var(--dim); font-size: 12px; line-height: 1.35;
  text-transform: uppercase; letter-spacing: .06em;
}
.panel-close {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; border: 1px solid var(--line);
  border-radius: 7px; background: var(--bg); color: var(--dim);
  font: 22px/1 Arial, sans-serif; cursor: pointer;
}
.panel-close:hover, .panel-close:focus-visible {
  color: var(--accent); border-color: var(--accent); outline: none;
}
.panel-actions {
  display: grid; gap: 8px; padding: 14px 0; border-bottom: 1px solid var(--line);
}
.panel-action-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;
}
.panel-action-grid.with-fix { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.panel-actions a, .panel-actions button {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%;
  min-height: 36px; padding: 7px 10px; border: 1px solid var(--line);
  border-radius: 8px; color: var(--ink); background: var(--bg); font: inherit; font-size: 13px;
  cursor: pointer;
}
.panel-action-grid a, .panel-action-grid button {
  justify-content: center; text-align: center; flex-direction: column; gap: 6px;
  height: 74px; min-height: 74px; padding: 10px 4px; font-size: 12px; line-height: 1.2;
}
.panel-action-grid form { height: 100%; }
.panel-actions form { margin: 0; }
.panel-actions a:hover, .panel-actions button:hover {
  border-color: var(--accent); color: var(--accent);
}
.panel-actions button span {
  color: #fff; background: var(--urgent); border-radius: 20px;
  min-width: 20px; padding: 1px 6px; font-size: 12px; font-weight: 700;
}
.history-block { padding-top: 14px; }
.history-list { display: grid; gap: 7px; margin-top: 10px; }
.history-item {
  position: relative; display: grid; gap: 2px;
  padding: 8px 34px 8px 10px; border: 1px solid var(--line);
  border-left: 3px solid var(--line); border-radius: 8px;
  background: var(--panel);
}
.history-item.s-backlog { border-left-color: var(--status-backlog); }
.history-item.s-todo { border-left-color: var(--status-todo); }
.history-item.s-in_progress { border-left-color: var(--status-progress); }
.history-item.s-in_review { border-left-color: var(--status-review); }
.history-item.s-done { border-left-color: var(--status-done); }
.history-item.s-canceled, .history-item.s-cancel { border-left-color: var(--status-canceled); }
.history-item.p0 { padding-right: 10px; }
.history-item:hover {
  border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--panel));
}
.history-item.s-backlog:hover { border-left-color: var(--status-backlog); }
.history-item.s-todo:hover { border-left-color: var(--status-todo); }
.history-item.s-in_progress:hover { border-left-color: var(--status-progress); }
.history-item.s-in_review:hover { border-left-color: var(--status-review); }
.history-item.s-done:hover { border-left-color: var(--status-done); }
.history-item.s-canceled:hover, .history-item.s-cancel:hover { border-left-color: var(--status-canceled); }
.history-item:hover .history-title { color: var(--accent); }
.history-item.p1 .priority-mark { --priority: var(--urgent); }
.history-item.p2 .priority-mark { --priority: var(--high); }
.history-item.p3 .priority-mark { --priority: var(--priority-medium); }
.history-item.p4 .priority-mark { --priority: var(--low); }
.history-line {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  font: 11px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--dim);
}
.history-line time { flex: none; }
.history-title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; font-size: 13px; line-height: 1.35;
}
.history-meta { color: var(--dim); font-size: 11.5px; }

.board {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(175px, 1fr));
  gap: 12px; padding: 16px 20px 40px; align-items: start;
}
@media (max-width: 1400px) { .board { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } }
@media (max-width: 900px)  { .board { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .board { grid-template-columns: 1fr; } }

.board.single { grid-template-columns: minmax(0, 680px); justify-content: center; }
/* One status, a column per project (DIM-543). Six columns to a screen at most,
   whatever the screen: past six the row scrolls sideways rather than squeezing
   everything in — owner, 2026-09-17. Six or fewer share the width. 60px is the
   five 12px gaps between six columns. */
.board.by-project { grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: max(230px, calc((100% - 60px) / 6)); overflow-x: auto; }
.board.by-project.fits { grid-auto-columns: minmax(230px, 1fr); }
.col.is-project h2 { text-transform: none; letter-spacing: 0; font-size: 13px; }
.col.is-project h2 a { color: inherit; text-decoration: none; }
.col.is-project h2 a:hover { text-decoration: underline; }

.col h2 {
  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); margin: 0 0 8px; padding: 0 2px;
}
.col h2 span { opacity: .6; }
.col h2 .wip-over { font-style: normal; margin-left: 6px; color: #b7791f; cursor: help; }

.card {
  position: relative; display: block; background: var(--panel); border: 1px solid var(--line);
  border-left: 3px solid var(--line); border-radius: 8px;
  padding: 10px 11px; margin-bottom: 8px;
}
.card:hover { border-color: var(--accent); }
.card.s-backlog { border-left-color: var(--status-backlog); }
.card.s-todo { border-left-color: var(--status-todo); }
.card.s-in_progress { border-left-color: var(--status-progress); }
.card.s-in_review { border-left-color: var(--status-review); }
.card.s-done { border-left-color: var(--status-done); }
.card.s-canceled { border-left-color: var(--status-canceled); }
.card.s-backlog:hover { border-left-color: var(--status-backlog); }
.card.s-todo:hover { border-left-color: var(--status-todo); }
.card.s-in_progress:hover { border-left-color: var(--status-progress); }
.card.s-in_review:hover { border-left-color: var(--status-review); }
.card.s-done:hover { border-left-color: var(--status-done); }
.card.s-canceled:hover { border-left-color: var(--status-canceled); }
.card .id { display: block; font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums; }
.card .title { display: block; margin: 2px 0 6px; }
.card .meta {
  display: flex; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--dim);
}
.card:not(.p0) .meta { padding-right: 24px; }
.card .meta b { font-weight: 500; background: var(--bg); border-radius: 4px; padding: 1px 5px; }
.card .meta em { font-style: normal; opacity: .8; }
.card .meta i { font-style: normal; color: var(--accent); }
.priority-mark {
  --priority: var(--dim);
  position: absolute; right: 10px; bottom: 10px;
  display: grid; grid-template-columns: repeat(4, 3px); align-items: end; gap: 2px;
  width: 18px; height: 15px; color: var(--priority);
}
.card.p1 .priority-mark { --priority: var(--urgent); }
.card.p2 .priority-mark { --priority: var(--high); }
.card.p3 .priority-mark { --priority: var(--priority-medium); }
.card.p4 .priority-mark { --priority: var(--low); }
.priority-mark span {
  display: block; border: 1px solid currentColor; border-radius: 2px;
}
.priority-mark span:nth-child(1) { height: 5px; }
.priority-mark span:nth-child(2) { height: 8px; }
.priority-mark span:nth-child(3) { height: 11px; }
.priority-mark span:nth-child(4) { height: 14px; }
.priority-mark .filled { background: currentColor; }
.card.pending { border-style: dashed; }
.card.conflict {
  border-color: var(--urgent);
  background: color-mix(in srgb, var(--urgent) 7%, var(--panel));
}
.card .meta .conflict-text { color: var(--urgent); }
.card.has-playbook { border-top-right-radius: 0; }
.card.has-playbook::after {
  content: ''; position: absolute; top: -1px; right: -1px;
  width: 13px; height: 13px;
  background: linear-gradient(225deg, var(--bg) 0 50%, var(--dim) 50% 100%);
  border-left: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.card.has-playbook:hover::after { border-color: var(--accent); }
.card.has-playbook .head { padding-right: 10px; }
.empty { color: var(--line); text-align: center; margin: 6px 0; }

.page { max-width: 820px; margin: 0 auto; padding: 26px 20px 60px; }
.card-project {
  display: inline-flex; align-items: center; gap: 6px; margin: 0 0 7px;
  min-height: 28px; padding: 3px 9px 3px 2px; border-radius: 8px;
  color: var(--dim); border-bottom: 1px solid var(--line);
  font-size: 16px; text-transform: uppercase;
}
.card-project svg {
  width: 14px; height: 14px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linejoin: round;
}
.card-project:hover, .card-project:focus-visible { color: var(--accent); border-bottom-color: currentColor; }
.card-project:focus-visible { outline: none; }
.project-jump { margin: 0; padding: 0; border: 0; background: transparent; }
.project-jump select {
  max-width: min(320px, 100%); min-height: 28px;
  padding: 3px 26px 3px 9px; border-radius: 8px;
  background: var(--panel); color: var(--text); border: 1px solid var(--line);
  font: inherit; font-size: 13px; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M2 4.5 6 8.5 10 4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center; background-size: 10px;
}
.project-jump select:hover { border-color: var(--dim); }
.project-jump select:focus { outline: none; border-color: var(--accent); }
.page h1 { font-size: 22px; line-height: 1.3; margin: 0 0 12px; }
.page h1 .id {
  color: var(--bg);
  font-weight: 400;
  font-size: 17px;
  top: -1px;
  background: var(--low);
  padding: 1px 8px 2px 7px;
  position: relative;
  border-radius: 6px;
}
/* The two copy buttons after the card's title.
   Deliberately faint: they are a convenience beside the heading, not part of
   what the heading says, and a pair of solid controls at the end of every title
   would read as the title's own punctuation. They earn colour on hover and on
   keyboard focus, and go green for a moment once the buffer holds something. */
.copy-pair { display: inline-flex; gap: 2px; margin-left: 8px; vertical-align: middle; }
.copy {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--dim); cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.copy svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.copy:hover { color: var(--accent); border-color: var(--line); background: var(--panel); }
/* Focus is drawn, not inherited: these sit inside an h1 where the browser's own
   outline is easy to lose against a large font. */
.copy:focus-visible { color: var(--accent); border-color: var(--accent); outline: none; }
/* The pencil stands first in that row and is built like its neighbours, glyph
   instead of drawn icon — the only mark here that is a letter rather than a
   shape, and at this size a drawn pencil reads as a smudge. */
.title-edit {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0; font: inherit; font-size: 14px; line-height: 1;
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--dim); cursor: pointer;
  transition: color .12s ease, border-color .12s ease, background .12s ease;
}
.title-edit:hover { color: var(--accent); border-color: var(--line); background: var(--panel); }
.title-edit:focus-visible { color: var(--accent); border-color: var(--accent); outline: none; }
.copy.is-done { color: var(--ok); border-color: currentColor; }
.copy.is-bad { color: var(--urgent); border-color: currentColor; }
/* The spoken half. Off-screen rather than hidden, so a screen reader announces
   it while nothing in the layout moves when it fills and empties. */
.copy-said {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

.badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0 0 6px; }
.badge {
  font-size: 12px; padding: 2px 8px; border-radius: 20px;
  background: var(--panel); border: 1px solid var(--line); color: var(--dim);
}
.badge.s-in_progress { border-color: var(--accent); color: var(--accent); }
.badge.s-done { opacity: .6; }
.badge.arch, .badge.due { border-color: var(--high); color: var(--high); }
/* The date, as a control. Dashed while empty, like the thread field, so a card
   with no date looks like a blank waiting to be filled rather than a thing that
   failed to render. */
.due-field { display: inline-flex; align-items: center; gap: 4px; min-height: 26px; padding: 1px 8px; cursor: pointer; }
.due-field .due-word { color: var(--dim); }
.due-field.is-empty { border-style: dashed; }
.due-field.is-overdue { border-color: var(--urgent); color: var(--urgent); }
.due-field input[type=date] {
  font: inherit; font-size: 12px; line-height: 1.25; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer; min-width: 92px;
}
.due-field input[type=date]:disabled { cursor: wait; opacity: .65; }
.due-field input[type=date]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.due-field .due-clear { border: 0; background: none; color: var(--dim); font: inherit; font-size: 13px;
  line-height: 1; padding: 0 2px; cursor: pointer; opacity: .55; }
.due-field:hover .due-clear, .due-field .due-clear:focus-visible { opacity: 1; color: var(--urgent); }
.badge-select {
  position: relative; display: inline-flex; align-items: center;
  min-height: 26px; padding: 0; overflow: hidden;
}
.badge-select > span {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.badge-select select {
  max-width: 150px; min-height: 24px; padding: 2px 23px 2px 8px;
  border: 0; background: transparent; color: inherit;
  font: inherit; line-height: 1.25; cursor: pointer; appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M2 4.5 6 8.5 10 4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 10px;
}
.badge-select select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
.badge-select select:disabled { cursor: wait; opacity: .65; }
/* The thread on a card: a path typed or picked, saved on change like a select. */
/* The thread as words. The crumbs are the field now; the path input behind them
   is what the form posts and what a browser without script shows instead. */
/* A line of its own, filled rather than outlined. The break is a zero-height
   full-width item: with flex-wrap that is all it takes to start a row, and it
   keeps the rule in the stylesheet instead of in a second container. */
.badges-break { flex-basis: 100%; height: 0; margin: 0; }
.thread-field { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap;
  min-height: 26px; padding: 3px 10px; border: 0; border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 7%, var(--panel)); }
/* Add comment sits at the far end of that same line. */
.badges .open-comment { margin-left: auto; }
.thread-field .thread-label { color: var(--dim); }
.thread-line { display: inline-flex; align-items: center; gap: 8px; min-width: 0; max-width: 100%; }
.thread-line > .thread-label { font-weight: 700; color: var(--ink); }
.thread-line > .thread-field { min-width: 0; }
.thread-crumbs { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; min-width: 0; }
/* Named apart from the header's own `.crumb`, which is a flex row with
   `flex: 1` on it: sharing the word made every step of this path a flex box
   sized to a fraction of the row, so a two-word node wrapped onto two lines and
   the pencil ended up beside the second one. */
.thread-crumb { color: var(--ink); text-decoration: none; white-space: nowrap; border-bottom: 1px dotted var(--line); }
/* On a phone the path wraps to two or three lines, and the pencil trailing it
   came to rest alone on a line of its own. Pinned to the block's top corner
   instead, with room reserved so no step of the path can run under it. */
@media (max-width: 700px) {
  .thread-field { position: relative; padding-right: 30px; }
  .thread-field .thread-edit { position: absolute; top: 4px; right: 6px; opacity: 1; }
}
.thread-crumb:hover { color: var(--accent); border-bottom-color: var(--accent); }
.thread-sep { color: var(--dim); }
.thread-none { color: var(--dim); }
.thread-edit { border: 0; background: none; color: var(--dim); font: inherit; font-size: 11px; line-height: 1;
  padding: 0 2px; cursor: pointer; opacity: .5; }
.thread-field:hover .thread-edit, .thread-edit:focus-visible { opacity: 1; color: var(--accent); }

/* The picker dialog: the map's tree, one row per node. `select` is invisible
   until the row is under the cursor — browsing must not look like choosing. */
.thread-modal {
  width: min(560px, calc(100vw - 28px));
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel); color: var(--ink);
  padding: 20px 22px 18px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .32);
}
.thread-modal::backdrop { background: rgba(0, 0, 0, .45); }
.thread-modal h2 { font-size: 15px; margin: 0 0 4px; }
.thread-modal .modal-hint { color: var(--dim); font-size: 12.5px; margin: 0 0 12px; }
.thread-modal .thread-tree { max-height: min(56vh, 460px); overflow: auto; border: 1px solid var(--line); border-radius: 10px; background: var(--bg); }
.thread-modal .thread-chosen { margin: 10px 0 0; font-size: 12px; color: var(--dim); }
.thread-modal menu { display: flex; justify-content: flex-end; gap: 8px; margin: 12px 0 0; padding: 0; }
.thread-modal menu button { font: inherit; font-weight: 600; border-radius: 9px; padding: 9px 16px; cursor: pointer;
  color: var(--dim); background: var(--bg); border: 1px solid var(--line); }
.thread-modal menu button:hover { color: var(--ink); border-color: var(--accent); }
.thread-modal menu button.new-root { margin-right: auto; }
.thread-modal .tt-row { display: flex; align-items: center; gap: 4px; padding: 3px 10px 3px calc(8px + var(--d, 0) * 16px); min-height: 26px; }
.thread-modal .tt-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
.thread-modal .tt-row.is-current { background: color-mix(in srgb, var(--accent) 16%, transparent); }
.thread-modal .tt-fold { flex: none; width: 18px; height: 18px; border: 0; background: none; color: var(--dim); font: inherit; font-size: 12px; cursor: pointer; padding: 0; }
.thread-modal .tt-fold.is-leaf { cursor: default; opacity: 0; }
.thread-modal .tt-name { flex: 0 1 auto; min-width: 0; text-align: left; border: 0; background: none; color: var(--ink); font: inherit;
  padding: 0; cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.thread-modal .tt-name:disabled { cursor: default; }
/* `+` sits beside `select`, quieter: making a folder is rarer than choosing
   one, and the two must not read as a pair of equal buttons. */
.thread-modal .tt-add { flex: none; margin-left: auto; font: inherit; font-size: 13px; line-height: 1;
  padding: 0 6px; cursor: pointer; color: var(--dim); background: none; border: 0; visibility: hidden; }
.thread-modal .tt-row:hover .tt-add, .thread-modal .tt-add:focus-visible { visibility: visible; }
.thread-modal .tt-add:hover { color: var(--accent); }
.thread-modal .tt-select { flex: none; font: inherit; font-size: 11px; font-weight: 600; border-radius: 7px;
  padding: 2px 9px; cursor: pointer; color: #fff; background: var(--accent); border: 0; visibility: hidden; }
.thread-modal .tt-row:hover .tt-select, .thread-modal .tt-select:focus-visible { visibility: visible; }
/* A finger has no hover. On a phone the button is simply there — the rule the
   owner asked for is about a mouse not choosing by accident, and a tap that
   lands on a name still only opens and closes. */
@media (hover: none) { .thread-modal .tt-select, .thread-modal .tt-add { visibility: visible; } }
.thread-modal .tt-row.is-current .tt-select { visibility: visible; color: var(--dim); background: var(--bg); border: 1px solid var(--line); cursor: default; }

.thread-field input {
  min-height: 24px; padding: 2px 8px; border: 0; background: transparent; color: var(--ink);
  font: inherit; font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; line-height: 1.25;
}
.thread-field input::placeholder { color: var(--dim); font-family: inherit; }
.thread-field input:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.thread-field input:invalid { color: var(--urgent); }
.thread-field.is-empty { box-shadow: inset 0 0 0 1px var(--line); }
.label-picker {
  position: relative; min-height: 26px; padding: 0;
}
.label-picker summary {
  display: inline-flex; align-items: center; min-height: 24px;
  padding: 2px 23px 2px 8px; cursor: pointer; list-style: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath fill='%23888' d='M2 4.5 6 8.5 10 4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 8px center; background-size: 10px;
}
.label-picker summary::-webkit-details-marker { display: none; }
.label-picker summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}
.label-options {
  position: absolute; z-index: 30; top: calc(100% + 6px); left: 0;
  display: grid; gap: 2px; min-width: 170px; padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: 0 14px 35px rgba(0,0,0,.28);
}
.label-options label {
  display: flex; align-items: center; gap: 7px; padding: 5px 6px;
  border-radius: 6px; color: var(--text); cursor: pointer; white-space: nowrap;
}
.label-options label:hover { background: var(--soft); }
.label-options input { width: 14px; height: 14px; margin: 0; accent-color: var(--accent); }
.label-options input:disabled { cursor: wait; opacity: .65; }
.dates, .src { color: var(--dim); font-size: 12px; }
.src { margin-top: 40px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.links a { display: block; color: var(--accent); font-size: 13px; }

.card-controls {
  padding: 0; background: transparent; border: 0; border-radius: 0;
}
.card-controls button {
  font: inherit; cursor: pointer;
}
.card-controls .open-comment {
  margin-left: auto; min-height: 26px; padding: 2px 10px;
  font-size: 12px; font-weight: 650; color: var(--accent);
  border-color: var(--accent);
}
.card-controls .open-comment:hover { background: var(--accent); color: #fff; }
.card-controls .open-comment[disabled] {
  opacity: .5; cursor: default; border-color: var(--line); color: var(--dim);
}
.card-controls .comment-note { margin: 0; }
.save-note {
  margin: 14px 0 0; padding: 9px 11px; border-radius: 8px;
  color: var(--accent); background: var(--panel); border: 1px solid var(--line);
  background: color-mix(in srgb, var(--accent) 9%, var(--panel));
  border-color: color-mix(in srgb, var(--accent) 32%, var(--line));
  font-size: 13px;
}
.save-note.pending::before { content: '↻'; display: inline-block; margin-right: 6px; }
.save-note.saved::before { content: '✓'; margin-right: 6px; }
.save-note.error { color: var(--urgent); border-color: var(--urgent); }
.save-note.error::before { content: '!'; font-weight: 800; margin-right: 7px; }
.save-note.conflict { color: var(--urgent); border-color: var(--urgent); }
.save-note.conflict::before { content: '!'; font-weight: 800; margin-right: 7px; }
.action-link { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }

.record {
  display: grid; gap: 12px; margin-top: 18px;
}
.record-block {
  --section: var(--dim);
  min-width: 0; overflow: hidden;
  background: var(--panel); border: 1px solid var(--line);
  border-left: 4px solid var(--section); border-radius: 10px;
}
.record-task { --section: var(--accent); }
.record-decisions { --section: var(--decision); }
.record-done { --section: var(--work); }
.record-outcome { --section: var(--outcome); }
.record-open { --section: var(--warning); }
.record-log { --section: var(--medium); }
.record-playbook { --section: var(--measure); }
.record-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 10px 18px;
  background: color-mix(in srgb, var(--section) 7%, var(--panel));
  border-bottom: 1px solid color-mix(in srgb, var(--section) 20%, var(--line));
}
.record-head h2 {
  margin: 0; color: var(--section); font-size: 12px; line-height: 1.35;
  text-transform: uppercase; letter-spacing: .065em;
}
.record-note {
  color: var(--dim); font: 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: nowrap;
}
.record-content { padding: 10px 18px 14px; }
.record-content h3 { font-size: 14px; margin: 18px 0 6px; }
.record-content h4 { font-size: 13px; margin: 16px 0 5px; }
/* A Журнал entry is a link target: Рішення points at it as #log-N (DIM-510).
   The margin keeps the sticky top bar from covering the heading it jumped to. */
.log-entry { scroll-margin-top: 80px; border-radius: 6px; }
.log-entry:target { background: color-mix(in srgb, var(--medium) 16%, var(--panel)); box-shadow: 0 0 0 6px color-mix(in srgb, var(--medium) 16%, var(--panel)); }
.log-anchor { color: var(--dim); font: 12px/1 ui-monospace, SFMono-Regular, Menlo, monospace; margin-right: 4px; }
.log-anchor:hover { color: var(--accent); text-decoration: underline; }
.record-content p { margin: 8px 0; }
.record-content ul { margin: 8px 0; padding-left: 20px; }
.record-content li { margin: 3px 0; }
.record-content del { color: var(--dim); }
.record-content a { color: var(--accent); }
.record-content code, .record-content pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px;
  background: var(--bg); border-radius: 5px;
}
.record-content code { padding: 1px 5px; }
.record-content pre { padding: 10px 12px; overflow-x: auto; }
/* Tables in a card are reference, not decoration: a column of ids, a column of
   files, a column of numbers. They are read across, so the cell borders stay
   and the header does not shout. `display: block` on the wrapper would need an
   extra element in the markup, so the table itself scrolls the only way a plain
   table can — the container clips, and a wide table wins its own scrollbar. */
.record-content .tbl {
  border-collapse: collapse; margin: 10px 0; font-size: 13px; line-height: 1.45;
  display: block; overflow-x: auto; max-width: 100%;
}
.record-content .tbl th, .record-content .tbl td {
  border: 1px solid var(--line); padding: 5px 9px; text-align: left; vertical-align: top;
}
.record-content .tbl th { background: var(--bg); font-weight: 600; }
.record-content .tbl code { background: transparent; padding: 0; }
.record-empty { color: var(--dim); font-style: italic; }

/* The measurement windows a card opened, read live out of the SEO journals.
   Written as a list of small records rather than a table: the target is a URL
   that has to be allowed to wrap, and a table cell that wraps a URL stops being
   a table on a phone. */
.pb-list { list-style: none; margin: 0; padding: 0; }
.pb-entry { padding: 10px 0; border-top: 1px dashed var(--line); }
.pb-entry:first-child { border-top: 0; padding-top: 2px; }
.pb-state { margin: 0 0 3px; font-weight: 600; }
/* The state line is never coloured for a good or a bad verdict — "worse" is an
   honest answer and colouring it red would read as a fault. Only the third
   state is coloured, because it IS one: the night should have measured this and
   did not. */
.pb-entry.is-late .pb-state { color: var(--urgent); }
.pb-entry.is-waiting .pb-state { color: var(--dim); font-weight: 500; }
.pb-id {
  font: 11px/1.3 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--dim); padding: 1px 5px; border-radius: 5px; background: var(--bg);
}
.pb-what { margin: 2px 0; }
.pb-target { color: var(--dim); word-break: break-word; }
.pb-window { margin: 2px 0; color: var(--dim); font-size: 12.5px; }
.pb-numbers { margin: 4px 0 2px; font-variant-numeric: tabular-nums; }
.pb-span { color: var(--dim); font-size: 11.5px; }
/* The journal's own prose, folded: it is the evidence for the verdict above it,
   in English because that is the language the journals are written in, and it
   runs to a paragraph. Open it when the number is being argued with. */
.pb-more { margin: 4px 0 0; }
.pb-more summary { cursor: pointer; color: var(--dim); font-size: 12.5px; }
.pb-more p { margin: 6px 0 0; color: var(--dim); font-size: 12.5px; }
.record-block.is-empty .record-head { background: var(--panel); }

.foot { color: var(--dim); font-size: 12px; padding: 0 20px 30px; }

.col.s-canceled h2, .badge.s-canceled { opacity: .55; }

/* On card pages the header keeps navigation only: the card's identity belongs
   to the page title below, while the project remains reachable from anywhere. */
.crumb { display: flex; align-items: baseline; gap: 8px; min-width: 0; flex: 1; }
/* The project menu inside the crumb is as wide as the longest project name,
   and a flex item's automatic minimum is that same width — so on a phone it
   used to hold its 241px and lie on top of whatever the header put beside it.
   Told it may shrink, it takes the room that is left instead. */
.crumb .project-jump { min-width: 0; max-width: 100%; }
.crumb .project-jump select { width: 100%; min-width: 0; }

/* Narrow screens. The project list is ten names long and several of them are
   four words; wrapped, it pushed the first card below the fold. It becomes a
   strip that scrolls sideways instead — the same row order, just reachable by
   thumb rather than by growing the header downwards. */
@media (max-width: 700px) {
  .top { flex-wrap: wrap; gap: 10px 12px; padding: 10px 14px; }
  .brand { order: 1; width: 65px; height: 18px; }
  .tools { order: 2; margin-left: auto; }
  .head-links { order: 2; }
  /* Since the links moved ahead of it, the project menu has no room left on
     the first row and collapsed to a bare arrow. It takes a row of its own,
     like the filters it stands in for. */
  .crumb { order: 3; flex-basis: 100%; }
  .filters { order: 4; flex-basis: 100%; }
  .filters select { flex: 1; min-width: 0; padding-top: 8px; padding-bottom: 8px; }
  .board { padding: 12px 14px 40px; gap: 10px; }
  .col h2 { margin-top: 6px; }
  .page { padding: 18px 14px 50px; }
  .record { gap: 10px; }
  .record-head { padding: 9px 13px; }
  .record-content { padding: 9px 13px 12px; }
}

/* Phone: the columns become a strip you swipe through, one at a time, each
   scrolling inside itself. Stacked, they put 54 backlog cards between the top
   of the page and the work actually in motion — the one column a morning
   check is opened for.
   Gated on :has(.board) so the card page, which must scroll normally, is left
   alone; a browser without :has simply keeps the stacked layout. */
@media (max-width: 700px) {
  html:has(.board), body:has(.board) { height: 100%; overflow: hidden; }
  body:has(.board) { display: flex; flex-direction: column; }
  body:has(.board) .top { position: static; flex: none; }
  body:has(.board) .filter { flex: none; }

  .board {
    display: flex; flex-direction: row; align-items: stretch;
    flex: 1; min-height: 0;
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    padding: 12px 14px 0;
  }
  .board::-webkit-scrollbar { display: none; }

  .board.single .col { flex-basis: 100%; }

  .col {
    flex: 0 0 86%; min-width: 0; min-height: 0;
    scroll-snap-align: center;
    display: flex; flex-direction: column;
  }
  .col h2 { flex: none; margin-top: 0; }
  /* Only the cards move; the column heading stays put, so the answer to
     "which column am I in" never scrolls away. */
  .col .stack { flex: 1; min-height: 0; overflow-y: auto; padding-bottom: 12px; }

  .foot { flex: none; padding: 8px 14px 10px; }
}

/* Installed to the home screen the page owns the whole display, including the
   strip behind the notch and the bar at the bottom. viewport-fit=cover lets the
   background reach them; these keep the content out. */
@supports (padding: env(safe-area-inset-top)) {
  .top  { padding-top: calc(10px + env(safe-area-inset-top)); }
  .top, .board, .page, .foot, .filter {
    padding-left:  max(14px, env(safe-area-inset-left));
    padding-right: max(14px, env(safe-area-inset-right));
  }
  .foot { padding-bottom: calc(10px + env(safe-area-inset-bottom)); }
  .col .stack { padding-bottom: calc(12px + env(safe-area-inset-bottom)); }
}

/* The pull-to-refresh dial. It sits above the header and rides down from
   behind it, so the gesture reads as dragging the page itself rather than
   dropping a widget onto it. */
.ptr {
  position: fixed; top: -46px; left: 50%; z-index: 20;
  transform: translate(-50%, 0); opacity: 0;
  width: 38px; height: 38px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--panel); color: var(--dim);
  border: 1px solid var(--line);
  box-shadow: 0 2px 10px rgba(0, 0, 0, .18);
  pointer-events: none;
}
.ptr svg { transition: transform .05s linear; }
.ptr.ready { color: var(--accent); border-color: var(--accent); }
.ptr.spin { transition: transform .2s ease; }
.ptr.spin svg { animation: ptr-spin .7s linear infinite; transition: none; }
@keyframes ptr-spin { to { transform: rotate(360deg); } }

/* The sign-in page. One field's worth of chrome: it is seen once per device. */
body.login {
  min-height: 100dvh; display: flex; align-items: center; justify-content: center;
  padding: 24px;
}
body.login form {
  width: 100%; max-width: 320px; text-align: center;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 14px; padding: 28px 22px;
}
body.login img { border-radius: 12px; }
body.login h1 { font-size: 17px; margin: 10px 0 18px; }
body.login input {
  display: block; width: 100%; margin-bottom: 10px; padding: 11px 12px;
  font: inherit; color: var(--ink); background: var(--bg);
  border: 1px solid var(--line); border-radius: 9px;
}
body.login input:focus { outline: none; border-color: var(--accent); }
body.login button {
  width: 100%; padding: 11px; margin-top: 4px;
  font: inherit; font-weight: 600; color: #fff; background: var(--accent);
  border: 0; border-radius: 9px; cursor: pointer;
}
body.login .err { color: var(--urgent); font-size: 13px; margin: 0 0 12px; }

.tools .new-link { color: var(--accent); font-weight: 600; }

/* The new-card form. Wider fields than the card controls: this is the one
   place in the board where someone actually types. */
.new-card { max-width: 620px; }
.new-card label { display: block; margin-bottom: 14px; }
.new-card label > span, .new-card legend {
  display: block; font-size: 12px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--dim); margin-bottom: 5px;
}
.new-card input[type=text], .new-card textarea, .new-card select {
  width: 100%; font: inherit; color: var(--ink); background: var(--panel);
  border: 1px solid var(--line); border-radius: 9px; padding: 10px 12px;
}
.new-card textarea { resize: vertical; min-height: 120px; line-height: 1.5; }
.new-card input:focus, .new-card textarea:focus, .new-card select:focus {
  outline: none; border-color: var(--accent);
}
.new-card .row { display: flex; gap: 12px; }
.new-card .row label { flex: 1; }
.new-card fieldset.labels { border: 0; padding: 0; margin: 0 0 18px; }
.new-card .chk {
  display: inline-flex; align-items: center; gap: 6px; margin: 0 10px 8px 0;
  font-size: 13px; color: var(--ink);
}
.new-card .chk span { text-transform: none; letter-spacing: 0; color: inherit; margin: 0; font-size: 13px; }
.new-card button {
  font: inherit; font-weight: 600; color: #fff; background: var(--accent);
  border: 0; border-radius: 9px; padding: 11px 20px; cursor: pointer;
}
.new-card .hint { color: var(--dim); font-size: 12px; margin-top: 10px; }

/* Adding to the Журнал. It sits under the card body, where the entries it
   joins already are, rather than up beside the status controls: writing a
   sentence and flipping a status are different acts. */
.comment-form { margin: 22px 0 0; }
.comment-form label > span {
  display: block; font-size: 12px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--dim); margin-bottom: 5px;
}
.comment-form textarea {
  width: 100%; font: inherit; line-height: 1.5; color: var(--ink);
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 11px 13px; resize: vertical; min-height: 92px;
}
.comment-form textarea:focus { outline: none; border-color: var(--accent); }
.comment-form button {
  margin-top: 9px; font: inherit; font-weight: 600; color: #fff;
  background: var(--accent); border: 0; border-radius: 9px;
  padding: 10px 18px; cursor: pointer;
}
.comment-form button[disabled] { opacity: .55; cursor: default; }
.comment-form .save-note { margin-top: 10px; }

.comment-modal {
  width: min(560px, calc(100vw - 28px));
  border: 1px solid var(--line); border-radius: 14px;
  background: var(--panel); color: var(--ink);
  padding: 20px 22px 18px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, .32);
}
.comment-modal::backdrop { background: rgba(0, 0, 0, .45); }
.comment-modal h2 { font-size: 15px; margin: 0 0 4px; }
.comment-modal .modal-hint { color: var(--dim); font-size: 12.5px; margin: 0 0 12px; }
.comment-modal textarea {
  width: 100%; font: inherit; line-height: 1.5; color: var(--ink);
  background: var(--bg); border: 1px solid var(--line);
  border-radius: 10px; padding: 11px 13px; resize: vertical; min-height: 130px;
}
.comment-modal textarea:focus { outline: none; border-color: var(--accent); }
.comment-modal menu {
  display: flex; justify-content: flex-end; gap: 8px; margin: 12px 0 0; padding: 0;
}
.comment-modal button {
  font: inherit; font-weight: 600; border-radius: 9px; padding: 9px 16px; cursor: pointer;
}
.comment-modal button.cancel { color: var(--dim); background: var(--bg); border: 1px solid var(--line); }
.comment-modal button[type=submit] { color: #fff; background: var(--accent); border: 0; }
.comment-modal button[disabled] { opacity: .55; cursor: default; }

.settings-page {
  max-width: 880px;
}
.settings-form {
  display: grid; gap: 14px;
}
.settings-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.settings-head h1 {
  margin: 0; font-size: 22px; line-height: 1.2;
}
.icon-button {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 1px solid var(--line);
  border-radius: 8px; background: var(--panel); color: var(--ink);
  font: 700 20px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  cursor: pointer;
}
.icon-button:hover, .icon-button:focus-visible {
  border-color: var(--accent); color: var(--accent); outline: none;
}
/* Розділ «Налаштування»: перелік карток, по одній на групу параметрів. */
.settings-cards {
  display: grid; gap: 10px;
}
.settings-card {
  display: grid; gap: 2px 12px;
  grid-template-columns: 1fr auto;
  grid-template-areas: "title meta" "note meta";
  padding: 14px 16px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel);
}
.settings-card:hover, .settings-card:focus-visible {
  border-color: var(--accent); outline: none;
}
.settings-card-title {
  grid-area: title; font-size: 16px; font-weight: 700; line-height: 1.25;
}
.settings-card-note {
  grid-area: note; color: var(--dim);
}
.settings-card-meta {
  grid-area: meta; align-self: center;
  color: var(--dim); font-size: 12px; white-space: nowrap;
}

/* Дисплей. */
.settings-block {
  padding: 16px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel);
}
.settings-block h2 {
  margin: 0; font-size: 16px; line-height: 1.25;
}
.settings-hint {
  margin: 4px 0 0; color: var(--dim);
}
.theme-options {
  display: grid; gap: 8px; margin-top: 14px;
}
.theme-option {
  display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--bg); cursor: pointer;
}
.theme-option:has(input:checked) {
  border-color: var(--accent);
}
.theme-option input {
  width: 18px; height: 18px; margin: 0; accent-color: var(--accent);
}
.theme-option-body {
  display: grid; gap: 2px;
}
.theme-option-title {
  font-weight: 600;
}
.theme-option-note {
  color: var(--dim); font-size: 13px; line-height: 1.35;
}
.theme-state {
  margin-top: 12px;
}
/* Зразок теми: два трикутники в одному квадраті для «як у системі». */
.theme-swatch {
  width: 34px; height: 24px; border: 1px solid var(--line); border-radius: 6px;
}
.theme-swatch-light { background: #f6f7f9; }
.theme-swatch-dark { background: #14161a; }
.theme-swatch-auto {
  background: linear-gradient(105deg, #f6f7f9 0 50%, #14161a 50% 100%);
}

.project-settings-list {
  display: grid; gap: 8px;
}
.project-settings-row {
  display: grid; grid-template-columns: 34px minmax(140px, .85fr) minmax(220px, 1.3fr) minmax(80px, .35fr);
  gap: 8px; align-items: end;
  padding: 10px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--panel);
}
.project-settings-row.dragging {
  opacity: .55;
}
.drag-handle {
  width: 34px; height: 34px; padding: 0; border: 1px solid var(--line);
  border-radius: 7px; background: var(--bg); color: var(--dim);
  cursor: grab;
}
.drag-handle:active {
  cursor: grabbing;
}
.project-settings-row label {
  display: grid; gap: 4px;
}
.project-settings-row label span {
  color: var(--dim); font-size: 11px; line-height: 1.2;
  text-transform: uppercase; letter-spacing: .06em;
}
.project-description-field {
  grid-column: 2 / -1;
}
.project-settings-row input,
.project-settings-row textarea {
  width: 100%; min-width: 0; height: 34px; padding: 6px 9px;
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg); color: var(--ink); font: inherit;
}
.project-settings-row textarea {
  height: auto; min-height: 48px; resize: vertical;
}
.project-settings-row input:focus,
.project-settings-row textarea:focus {
  outline: none; border-color: var(--accent);
}
.settings-actions {
  display: flex; justify-content: flex-end; padding-top: 4px;
}
.settings-actions button {
  min-height: 36px; padding: 8px 14px; border: 0; border-radius: 8px;
  background: var(--accent); color: #fff; font: inherit; font-weight: 700;
  cursor: pointer;
}
.settings-actions button[disabled] {
  opacity: .55; cursor: default;
}
.project-modal {
  width: min(420px, calc(100vw - 32px)); padding: 18px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--panel); color: var(--ink);
}
.project-modal::backdrop {
  background: rgba(0, 0, 0, .45);
}
.project-modal h2 {
  margin: 0 0 14px; font-size: 18px; line-height: 1.2;
}
.project-modal-form {
  display: grid; gap: 12px;
}
.project-modal-form label {
  display: grid; gap: 4px;
}
.project-modal-form label span {
  color: var(--dim); font-size: 11px; line-height: 1.2;
  text-transform: uppercase; letter-spacing: .06em;
}
.project-modal-form input,
.project-modal-form textarea {
  width: 100%; height: 36px; padding: 7px 9px;
  border: 1px solid var(--line); border-radius: 7px;
  background: var(--bg); color: var(--ink); font: inherit;
}
.project-modal-form textarea {
  height: auto; min-height: 72px; resize: vertical;
}
.project-modal-form input:focus,
.project-modal-form textarea:focus {
  outline: none; border-color: var(--accent);
}
.project-modal-form menu {
  display: flex; justify-content: flex-end; gap: 8px;
  margin: 4px 0 0; padding: 0;
}
.project-modal-form button {
  min-height: 34px; padding: 7px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--bg); color: var(--ink);
  font: inherit; cursor: pointer;
}
.project-modal-form button[type=submit] {
  border-color: var(--accent); background: var(--accent); color: #fff;
}

@media (max-width: 640px) {
  .project-settings-row {
    grid-template-columns: 34px 1fr;
  }
  .project-settings-row label {
    grid-column: 2;
  }
}

/* The due badge. Coloured like an overdue card's stripe rather than like the
   accent, because it is the one thing in the header that is a claim about time
   running out. */
.tools .due-pill {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 26px; min-height: 26px; padding: 3px 8px; border-radius: 20px;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: #fff; background: var(--urgent); border: 1px solid var(--urgent);
  white-space: nowrap;
}
.tools .due-pill:hover, .tools .due-pill.on {
  color: #fff; background: var(--urgent); filter: brightness(.95);
}

/* The date sits opposite the id, on the tile's own top line, so it is read
   without opening the card — which is the only reason to put it on a tile. */
.card .head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.card .due {
  font-size: 11px; color: var(--dim); font-variant-numeric: tabular-nums;
  white-space: nowrap; flex: none;
}
/* Arrived or overdue reads as the stripe does: this is the one number on the
   tile that is a claim about time running out. */
.card .due.is-due { color: var(--urgent); font-weight: 600; }

/* The calendar. The button that leads to it sits in the filter row rather than
   among the tools on the right, because it is a way of looking at the same
   cards — a third menu, in effect — and not an action. */
/* Календар and Dash. On the board they sit inside the filter form, on every
   other page they are their own group next to the crumb — one rule either way. */
.head-links { display: flex; align-items: center; gap: 8px; flex: none; }

.cal-link {
  display: inline-flex; align-items: center; flex: none;
  font-size: 13px; padding: 6px 12px; border-radius: 8px;
  color: var(--ink); background: var(--bg); border: 1px solid var(--line);
  white-space: nowrap;
}
.cal-link:hover { border-color: var(--accent); color: var(--accent); }

.cal { padding: 16px 20px 40px; }
.cal-nav { display: flex; align-items: center; justify-content: center; gap: 14px; margin: 0 0 14px; }
.cal-nav h1 { font-size: 17px; margin: 0; min-width: 190px; text-align: center; }
.cal-nav a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 8px;
  background: var(--panel); border: 1px solid var(--line); color: var(--dim);
}
.cal-nav a:hover { border-color: var(--accent); color: var(--accent); }

.cal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.cal-wd {
  font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--dim); text-align: center; padding-bottom: 2px;
}
.cal-day {
  min-height: 104px; padding: 6px; border-radius: 10px;
  background: var(--panel); border: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
}
/* The days before and after the month are drawn, not skipped: without them the
   first week would start at the left edge and the columns would stop meaning
   weekdays. */
.cal-day.is-pad { background: transparent; border-color: transparent; min-height: 0; }
.cal-day.is-past { opacity: .72; }
.cal-day.is-today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.cal-date {
  display: flex; align-items: baseline; justify-content: space-between;
  font-size: 12px; color: var(--dim); font-variant-numeric: tabular-nums;
}
.cal-day.is-today .cal-date { color: var(--accent); font-weight: 700; }
.cal-date span {
  font-size: 10px; padding: 0 5px; border-radius: 20px;
  background: var(--bg); border: 1px solid var(--line);
}

.cal-card {
  display: block; min-width: 0; padding: 4px 6px; border-radius: 7px;
  background: var(--bg); border: 1px solid var(--line);
  border-left: 3px solid var(--medium);
  font-size: 11px; line-height: 1.35;
}
.cal-card:hover { border-color: var(--accent); border-left-color: var(--accent); }
.cal-card b { display: block; font-size: 10px; color: var(--dim); font-weight: 600; }
/* Two lines of title and no more: a day with five cards has to stay a day, and
   the whole title is one hover — or one tap — away. */
.cal-card span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The project on its own line, under the title, and only when the calendar is
   showing more than one — the same rule the board tile follows. In a cell a
   seventh of the screen wide it cannot share a line with the id: the names run
   to three words, and the id is the half that would lose. */
.cal-card em {
  display: block; font-style: normal; font-size: 10px; color: var(--dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 2px;
}
.cal-card.p1 { border-left-color: var(--urgent); }
.cal-card.p2 { border-left-color: var(--high); }
.cal-card.p3 { border-left-color: var(--medium); }
.cal-card.p4 { border-left-color: var(--low); }
.cal-card.is-due b { color: var(--urgent); }
/* Done and canceled keep their square on the grid — the date happened — but
   they stop competing for the eye with the work still owing. */
.cal-card.is-closed { opacity: .5; }
.cal-card.is-closed span { text-decoration: line-through; }

/* A measurement window, sharing the day with the cards but not pretending to be
   one. Dashed instead of solid, because it is not work somebody assigned — it
   is a date on which a number became readable, and the card beside it is where
   the reading gets written up.

   The two states are the point of the row (owner, 2026-09-08): the leading mark
   says whether the check has actually been run, since a closed card does not
   mean a measured entry and reading one as the other is the mistake this is
   here to prevent. */
.cal-pb {
  position: relative;
  display: block; min-width: 0; padding: 4px 6px; border-radius: 7px;
  background: transparent; border: 1px dashed var(--line);
  border-left: 3px dashed var(--dim);
  /* Square at the top right, because the fold is drawn over that corner and a
     dog-ear sitting on a rounded one reads as a rendering fault. */
  border-top-right-radius: 0;
  font-size: 11px; line-height: 1.35; color: var(--dim);
}
/* The folded corner (owner, 2026-09-09). The leading mark already says whether
   the check has run, but it is read only after the row is read; the fold says
   "this is a measurement, not a card" before any text is, and it costs the row
   no height in a cell a seventh of the screen wide. Two triangles: the day's
   own background where the corner has been turned back, and the turned-back
   piece beneath it. */
.cal-pb::after {
  content: ''; position: absolute; top: -1px; right: -1px;
  width: 13px; height: 13px;
  background: linear-gradient(225deg, var(--panel) 0 50%, var(--dim) 50% 100%);
  border-left: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.cal-pb:hover { border-color: var(--accent); border-left-color: var(--accent); }
.cal-pb:hover::after { border-color: var(--accent); }
/* The id stops short of the fold; nothing else on the row reaches that corner. */
.cal-pb b { display: block; font-size: 10px; font-weight: 600; padding-right: 15px; }
.cal-pb span {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.cal-pb em {
  display: block; font-style: normal; font-size: 10px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-top: 2px;
}
/* Measured is the quiet state: the work is done and the row stays only so the
   month reads as a record rather than as a list with holes in it. */
.cal-pb.is-measured { opacity: .55; }
.cal-pb.is-measured span { text-decoration: line-through; }
/* Closed without a measurement (DIM-570): as quiet as a measured one, but not
   struck through — nothing was checked off. */
.cal-pb.is-unmeasurable { opacity: .45; }

/* Phone: seven columns of 40px are not a calendar, they are a colour chart. The
   grid becomes one day per row, and days with nothing on them are dropped —
   scrolling past 20 empty squares to reach the next date is not reading. */
@media (max-width: 700px) {
  .cal { padding: 12px 14px 40px; }
  .cal-grid { grid-template-columns: 1fr; gap: 4px; }
  .cal-wd { display: none; }
  .cal-day.is-pad { display: none; }
  .cal-day { min-height: 0; }
  .cal-day:not(.has-cards):not(.is-today) { display: none; }
  /* The weekday column headings are gone here, so the day carries its own —
     "8" alone does not say which day of the week the date landed on. */
  .cal-date { justify-content: flex-start; gap: 6px; }
  .cal-date::before { content: attr(data-wd); }
  .cal-date span { margin-left: auto; }
  .cal-card { font-size: 12px; padding: 5px 8px; }
  .cal-card span { -webkit-line-clamp: 3; }
}

/* Project Edit Controls */
.card-project-container { display: flex; align-items: center; gap: 8px; }
.project-edit-controls { display: inline-flex; align-items: center; gap: 4px; transition: opacity 0.2s; opacity: 0; }
.card-project-container:hover .project-edit-controls, .project-edit-controls:focus-within { opacity: 1; }
.project-edit-form { display: none; align-items: center; gap: 4px; }
.edit-project, .save-project, .cancel-project { cursor: pointer; background: none; border: none; padding: 4px; }
.edit-project { color: var(--dim); }
.edit-project:hover { color: var(--accent); }

/* ------------------------------------------------------------------ map --
   One row per thread, weeks across. The left column is a tree; the timeline
   is a grid of --n week columns, each --w wide, and every mark on it is placed
   by column index in a CSS variable so the page can be drawn with no
   measuring at all. Colours are the card states from threads.php, worst wins.
   The legend and the column header are one sticky block under the page
   header; the header's own scroller is kept level with the rows by script. */
.map { padding: 12px 20px 40px; --w: 22px; --label: 300px; }
.map-sticky { position: sticky; top: var(--top, 56px); z-index: 4; background: var(--bg); padding-top: 4px; }
.map-legend {
  display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center;
  margin: 0 0 8px; font-size: 12px; color: var(--dim);
}
.map-key { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
/* The six state keys are links, and look like what they are only once the
   cursor is on them: at rest the row must still read as a legend, because that
   is what it is for most of the time it is looked at. */
a.map-key { color: inherit; text-decoration: none; border-radius: 6px; padding: 2px 6px; margin: -2px 0; }
a.map-key:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.map-key.is-off { opacity: .4; }
.map-key.is-on { background: color-mix(in srgb, var(--accent) 16%, transparent); font-weight: 600; }
.map-key.is-clear { color: var(--accent); font-weight: 600; }
.map-key i { display: inline-block; width: 18px; height: 8px; border-radius: 4px; background: var(--st, var(--line)); }
.map-key i.wait { width: auto; height: auto; border-radius: 0; background: none; font-size: 11px; font-style: normal; }
.map-key i.dot { width: 8px; height: 8px; background: var(--ink); }
.map-key i.due { width: 3px; height: 12px; border-radius: 1px; background: var(--accent); }
.map-key i.beyond { width: 0; height: 0; border-radius: 0; background: none;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 7px solid var(--accent); }
.map-key i.pb { width: 8px; height: 8px; border-radius: 1px; background: none; border: 2px solid var(--measure); transform: rotate(45deg); }
.map-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--line); background: var(--panel); }
.map-scroll.is-head { overflow: hidden; border-radius: 10px 10px 0 0; border-bottom: 0; }
.map-scroll.is-body { border-radius: 0 0 10px 10px; }
.map-grid {
  display: grid; grid-template-columns: var(--label) 44px calc(var(--w) * var(--n));
  min-width: max-content; font-size: 13px;
}
.map-row { display: contents; }
.map-row[hidden] { display: none; }
.map-row > div, .map-head { border-bottom: 1px solid var(--line); min-height: 30px; }
.map-head { background: var(--panel); font-size: 11px; color: var(--dim);
  text-transform: uppercase; letter-spacing: .06em; display: flex; align-items: flex-end; padding: 8px 8px 4px; }
.map-head.map-tl { padding: 0; }
.map-head.map-label { z-index: 2; }
/* The edge the column is dragged by: a line the reader can find and hold. */
.map-label { border-right: 2px solid var(--line); }
.map-handle { position: absolute; top: 0; bottom: -1px; right: -8px; width: 16px; cursor: col-resize; z-index: 3; touch-action: none; }
.map-handle::after { content: ""; position: absolute; top: 0; bottom: 0; left: 6px; width: 2px; background: var(--dim); opacity: .6; }
.map-handle:hover::after, body.is-resizing .map-handle::after { background: var(--accent); opacity: 1; }
body.is-resizing { cursor: col-resize; user-select: none; }
.map-label { position: sticky; left: 0; z-index: 2; background: var(--panel); display: flex; align-items: center; gap: 4px;
  padding: 0 8px 0 calc(8px + var(--d, 0) * 16px); min-width: 0; }
.map-fold { flex: none; width: 18px; height: 18px; border: 0; background: none; color: var(--dim); font: inherit; font-size: 12px; cursor: pointer; padding: 0; }
.map-fold.is-leaf { cursor: default; }
/* The name takes the width of the name and no more, so the pencil that follows
   it sits against the word it renames rather than out at the column edge, half
   a screen from the row it belongs to. The counts keep the right edge with
   `margin-left: auto`, which is the job the name's old `flex-grow` was doing. */
.map-name { flex: 0 1 auto; min-width: 0; text-align: left; border: 0; background: none; color: var(--ink); font: inherit; padding: 0;
  cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-name:disabled { cursor: default; color: var(--dim); }
.map-name em { font-style: normal; font-size: 11px; margin-left: 4px; }
/* The pencil sits quiet until the row is under the cursor: a hundred rows of
   bright pencils would read as the point of the page, and the point is the
   timeline. It stays legible when dim, because on a touch screen there is no
   hover to bring it up. */
.map-rename { flex: none; width: 18px; height: 18px; border: 0; background: none; color: var(--dim); font: inherit; font-size: 11px;
  line-height: 1; cursor: pointer; padding: 0; opacity: .4; }
.map-row:hover .map-rename, .map-rename:focus-visible { opacity: 1; color: var(--accent); }
.map-rename:disabled { cursor: progress; opacity: .3; }
.map-row.d0 .map-name { font-weight: 700; }
.map-row.d1 .map-name { font-weight: 600; }
.map-row.node.st-empty .map-name { color: var(--dim); }
.map-counts { flex: none; margin-left: auto; display: inline-flex; gap: 3px; font-size: 10px; font-variant-numeric: tabular-nums; }
.map-counts span, .map-counts b { padding: 0 5px; border-radius: 10px; background: var(--bg); border: 1px solid var(--line); color: var(--dim); font-weight: 600; }
.map-counts .c-overdue { color: #fff; background: var(--urgent); border-color: var(--urgent); }
.map-counts .c-theirs { color: var(--panel); background: var(--theirs); border-color: var(--theirs); }
.map-counts .c-nodue { color: var(--panel); background: var(--waiting); border-color: var(--waiting); }
.map-counts .c-stale { color: #fff; background: var(--high); border-color: var(--high); }
.map-age { display: flex; align-items: center; justify-content: flex-end; padding: 0 8px; color: var(--dim); font-size: 11px; font-variant-numeric: tabular-nums; }
.map-age.is-old { color: var(--high); font-weight: 700; }
.map-card { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); font-size: 12px; }
.map-card b { color: var(--dim); font-weight: 600; font-size: 10px; margin-right: 2px; }
.map-card:hover { color: var(--accent); }
.map-row.card > div { background: color-mix(in srgb, var(--bg) 60%, var(--panel)); }
/* The row is `display: contents`, so the stripe belongs to its first cell —
   the label — which is also the one that stays put when the timeline scrolls
   sideways. */
.map-row.card > .map-label { border-left: 3px solid var(--line); }
.map-row.card.s-backlog > .map-label { border-left-color: var(--status-backlog); }
.map-row.card.s-todo > .map-label { border-left-color: var(--status-todo); }
.map-row.card.s-in_progress > .map-label { border-left-color: var(--status-progress); }
.map-row.card.s-in_review > .map-label { border-left-color: var(--status-review); }
.map-row.card.s-done > .map-label { border-left-color: var(--status-done); }
.map-row.card.s-canceled > .map-label { border-left-color: var(--status-canceled); }

.map-tl { position: relative; overflow: hidden;
  background-image: repeating-linear-gradient(to right, var(--line) 0 1px, transparent 1px var(--w));
  background-size: var(--w) 100%; }
.map-wk { position: absolute; left: calc(var(--i) * var(--w)); bottom: 3px; width: var(--w); font-size: 10px; text-align: left; padding-left: 3px; letter-spacing: 0; text-transform: none; white-space: nowrap; }
.map-wk.is-month { border-left: 1px solid var(--dim); }
.map-wk.is-today { color: var(--accent); font-weight: 700; }
.map-today { position: absolute; top: 0; bottom: 0; left: calc(var(--i) * var(--w)); width: var(--w); background: color-mix(in srgb, var(--accent) 10%, transparent); }
.map-bar { position: absolute; top: 11px; height: 8px; border-radius: 4px; left: calc(var(--a) * var(--w) + 2px); width: calc(var(--b) * var(--w) - 4px);
  background: var(--st, var(--line)); opacity: .55; }
.map-dot { position: absolute; top: 12px; width: 6px; height: 6px; border-radius: 50%; background: var(--ink); left: calc(var(--i) * var(--w) + (var(--w) - 6px) / 2); }
.map-due { position: absolute; top: 5px; bottom: 5px; width: 3px; border-radius: 1px; background: var(--accent); left: calc(var(--i) * var(--w) + (var(--w) - 3px) / 2); }
.map-due.is-overdue { background: var(--urgent); }
/* Further out than the window reaches: an arrow at the edge, not a date in a
   week it does not belong to. */
.map-due.is-beyond { width: 0; height: 0; top: 8px; border-radius: 0; background: none;
  border-top: 5px solid transparent; border-bottom: 5px solid transparent; border-left: 7px solid var(--accent);
  left: calc(var(--i) * var(--w) + (var(--w) - 7px) / 2); opacity: .8; }
/* A playbook window: hollow, so a check that is due reads differently from a
   date that is; filled once the night has measured it. */
.map-pb { position: absolute; top: 3px; width: 8px; height: 8px; border: 2px solid var(--measure); border-radius: 1px; background: var(--panel);
  transform: rotate(45deg); left: calc(var(--i) * var(--w) + (var(--w) - 8px) / 2); }
.map-pb.is-measured { background: var(--measure); opacity: .6; }

.st-overdue { --st: var(--urgent); }
.st-theirs { --st: var(--theirs); }
.st-nodue { --st: var(--waiting); }
.st-stale { --st: var(--high); }
.st-open { --st: var(--status-todo); }
.st-closed { --st: var(--closed); }
/* A black bar at a third of its opacity is a smudge; closed keeps more of
   itself so the stretch is still readable as a stretch. */
.map-row.st-closed .map-bar { opacity: .75; }
/* The legend swatch has no row behind it to sit on, so black on black would be
   nothing at all: it gets an outline, and only there. */
.map-key.st-closed i { box-shadow: inset 0 0 0 1px var(--line); }
.map-row.st-empty .map-tl { background-image: none; }
/* The name of a thread is not coloured by its state. The counts carry it, and
   the bar carries it, and three of them saying the same thing turned the map
   into a page of coloured words where the eye had nowhere to rest. Owner,
   2026-09-15. The only name still dimmed is an empty node's, which is not a
   state but the absence of one: with no cards there is no badge to say it. */
.map-row.is-closed .map-fold { color: var(--accent); }

@media (max-width: 700px) {
  .map { padding: 8px 10px 30px; --w: 18px; --label: 190px; }
  .map-grid { grid-template-columns: var(--label) 36px calc(var(--w) * var(--n)); }
  .map-counts span { display: none; }
}

/* The thread on the new-card form: the same field as a card's, wearing the
   form's own clothes — a full-width row with the same border and radius as the
   inputs above it. The label the badge carries is dropped here, since the form
   already has one over the row, and the form's rules for `span` and `button`
   are undone: left alone they made the path shout in capitals and turned the
   pencil into a second blue submit. */
.field-row.thread-field {
  display: flex; width: 100%; box-sizing: border-box; min-height: 44px;
  padding: 10px 12px; background: var(--panel); border: 1px solid var(--line); border-radius: 9px;
}
.field-row.thread-field.is-empty { box-shadow: none; border-style: dashed; }
.field-row .thread-label { display: none; }
.new-card .thread-field span, .new-card .thread-field a {
  text-transform: none; letter-spacing: 0; font-size: 13px; margin: 0;
}
.new-card .thread-field .thread-none { color: var(--dim); }
.new-card .thread-field .thread-crumb { color: var(--ink); }
.new-card .thread-field .thread-edit {
  color: var(--dim); background: none; border: 0; border-radius: 0;
  padding: 0 2px; font-size: 12px; font-weight: 400;
}
.new-card .thread-field .thread-edit:hover { color: var(--accent); }
.new-card .thread-field input[name="thread"] { width: 100%; }

/* Search. Closed, the magnifier is one more small button in the header;
   opened, the field grows to the left of it. On a phone the opened form drops
   to a row of its own under the menus, so the field is as wide as the thumb
   needs and nothing else in the header has to move. */
.search { display: flex; align-items: center; gap: 4px; flex: none; }
.search input {
  font: inherit; font-size: 13px; padding: 6px 10px; width: 200px; max-width: 46vw;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
  -webkit-appearance: none; appearance: none;
}
.search input:focus { outline: none; border-color: var(--accent); }
.search input[hidden] { display: none; }
.search-toggle {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 28px; height: 28px; padding: 0; border-radius: 8px; cursor: pointer;
  color: var(--dim); background: transparent; border: 1px solid transparent;
}
.search-toggle:hover, .search.open .search-toggle { color: var(--ink); background: var(--bg); border-color: var(--line); }
.search-toggle:focus-visible { outline: none; border-color: var(--accent); }
/* Below a wide desktop the opened field would only crush the menus beside it,
   so from there down it takes a row of its own instead. */
@media (max-width: 1100px) {
  .search.open { order: 4; flex-basis: 100%; }
  .search.open input { flex: 1; width: auto; max-width: none; }
}
@media (max-width: 700px) {
  .search { order: 2; }
  .search.open { order: 4; }
  .search.open input { padding-top: 8px; padding-bottom: 8px; }
}

/* The results: the history tile's shape, in a column that does not stretch
   across a wide screen, because a list of thirty rows reads down, not across. */
.results { padding: 0 20px 40px; }
.results .filter { padding-left: 0; padding-right: 0; }
.result-list { max-width: 820px; margin-top: 12px; }
.result-snippet { color: var(--dim); font-size: 12.5px; line-height: 1.4; margin-top: 2px; word-break: break-word; }
.results-empty { max-width: 560px; color: var(--dim); }
.results mark {
  background: color-mix(in srgb, var(--accent) 22%, transparent); color: inherit;
  border-radius: 3px; padding: 0 1px;
}
@media (max-width: 700px) {
  .results { padding: 0 14px 40px; }
}
