/* crm/crm.css — CRM module styles. LIGHT THEME only (product decision 2026-07-09:
   the CRM is always light regardless of the OS colour scheme). */
:root {
  --bg: #f1f3f6; --surface: #ffffff; --surface-2: #f7f8fa; --surface-3: #eef0f4;
  --border: #e4e7ec; --border-2: #d7dbe2;
  --text: #1a1d23; --text-dim: #5b6472; --text-faint: #98a1b0;
  --accent: #2563eb; --accent-soft: #eaf1ff;
  --radius: 10px; --shadow: 0 1px 2px rgba(20,24,33,.06), 0 1px 3px rgba(20,24,33,.04);
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body { margin: 0; font-family: 'Inter', system-ui, sans-serif; background: var(--bg); color: var(--text);
  -webkit-font-smoothing: antialiased; }

.crm-header {
  position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: 14px;
  padding: 11px 18px; background: var(--surface); border-bottom: 1px solid var(--border);
  box-shadow: 0 1px 0 rgba(20,24,33,.03);
  /* WRAP, don't overflow. A no-wrap flex row silently pushes trailing controls
     off the right edge on a phone — there is no scrollbar and no clue they
     exist. At 390px this was already clipping the leads badge before the 🔔
     control was added, and it made that control completely invisible on the
     device it matters most for. Every control in this bar must be reachable at
     the narrowest supported width. */
  flex-wrap: wrap; row-gap: 8px;
}
.crm-header h1 { font-size: 16px; font-weight: 700; margin: 0; letter-spacing: -.01em; }
.crm-back { color: var(--text-dim); text-decoration: none; font-size: 13px; font-weight: 600; }
.crm-back:hover { color: var(--accent); }
/* The 🔔 owns the right corner (owner, 2026-08-18). .crm-badge keeps its own
   margin-left:auto so that on the rare load where it DOES appear — a failed
   move, a failed load — the error pill lands beside the bell instead of in the
   middle of the bar. */
#crm-push-bell { margin-left: auto; }
.crm-badge { margin-left: auto; font-size: 12px; font-weight: 600; color: var(--text-dim);
  background: var(--surface-2); border: 1px solid var(--border); padding: 3px 10px; border-radius: 999px; }
.crm-badge.ok { color: #15803d; background: #f0fdf4; border-color: #bbf7d0; }
.crm-badge.err { color: #b91c1c; background: #fef2f2; border-color: #fecaca; }

main { padding: 14px 18px 24px; }
.crm-loading, .crm-error, .crm-empty { padding: 40px; text-align: center; color: var(--text-dim); }
.crm-error { color: #b91c1c; }

/* ── stats + filter bar ── */
.crm-stats { display: flex; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.crm-stat { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 8px 15px; box-shadow: var(--shadow); }
.crm-stat b { display: block; font-size: 20px; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
.crm-stat span { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .04em; }

.crm-filters { display: flex; gap: 9px; margin-bottom: 14px; flex-wrap: wrap; align-items: center; }
.crm-input { font: inherit; font-size: 13.5px; padding: 8px 12px; border: 1px solid var(--border-2);
  border-radius: 8px; background: var(--surface); color: var(--text); box-shadow: var(--shadow); }
.crm-input[type="search"] { flex: 1; min-width: 220px; }
.crm-input:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }

/* ── KANBAN board (leads by pipeline stage) ── */
/* COLUMNS ARE FULL HEIGHT, like the Tasker's status groups, and for the one
   reason that matters: the empty space under the last card has to BELONG to the
   stage, or dropping a lead there hits the page background and nothing happens
   (owner, 2026-08-18: "не могу передвинуть лид на traffic calculated, бросив
   его ниже District Steak"). align-items:flex-start let every column shrink to
   its cards, so a two-lead stage was a two-lead-tall target. */
.crm-kanban { display: flex; gap: 12px; align-items: stretch; overflow-x: auto; padding-bottom: 14px;
  scrollbar-width: thin; min-height: calc(100vh - 190px); }
/* The floor keeps a short board (everything filtered away) from collapsing the
   columns to their headers; the cap is what makes the card list scroll instead
   of the page. */
.crm-col { flex: 0 0 268px; width: 268px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 12px; display: flex; flex-direction: column;
  max-height: calc(100vh - 190px); min-height: 260px; }
.crm-col-head { display: flex; align-items: center; gap: 8px; padding: 10px 12px 9px;
  border-top: 3px solid var(--stage, #c7ccd6); border-top-left-radius: 12px; border-top-right-radius: 12px;
  position: sticky; top: 0; background: var(--surface-2); z-index: 1; }
.crm-col-name { font-size: 12.5px; font-weight: 700; color: var(--text); line-height: 1.2;
  overflow: hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.crm-col-count { margin-left: auto; flex-shrink: 0; font-size: 11px; font-weight: 700; color: var(--text-dim);
  background: var(--surface-3); border-radius: 999px; padding: 1px 9px; }
.crm-col-body { padding: 8px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px;
  flex: 1 1 auto; }
.crm-col-empty { color: var(--text-faint); font-size: 12px; text-align: center; padding: 14px 0; }
.crm-col-more { color: var(--text-faint); font-size: 11.5px; text-align: center; padding: 6px 0 2px; }

/* Горизонтальная полоса доски — всегда на виду (владелец, 2026-08-19).
   Причин было ДВЕ, обе замерены на живой доске, и лечится только пара сразу:
   1. Полоса НАКЛАДНАЯ — занимала 0 px и гасла, когда доску не крутят (macOS).
   2. Низ доски уезжал за экран. Высота задавалась константой `100vh - 190px`,
      а шапка с фильтрами занимает 202 px плюс 14 px отступа — на 26 больше,
      то есть полоса рисовалась ниже видимой области, даже когда проявлялась.
      Константа к тому же расходилась заново, стоило шапке перенестись на две
      строки (`flex-wrap` у .crm-header — её штатное поведение на узком окне),
      поэтому высота больше не считается, а берётся флексом от самого экрана.
   🔴 `scrollbar-width: auto` здесь несущий, а не косметика: Chrome игнорирует
   ВСЮ стилизацию ::-webkit-scrollbar, если scrollbar-width отличен от auto.
   С оставленным `thin` (как было) правка молча не делает ничего — проверено
   замером: полоса остаётся 0 px, все правила ниже просто не применяются.
   Телефона это не касается: там полоса всегда накладная и форсировать её
   нельзя ничем, аффорданс даёт колонка в 82vw в блоке ≤640px ниже. */
@media (min-width: 641px) {
  body:has(.crm-kanban) { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
  body:has(.crm-kanban) #crm-root { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
  .crm-kanban { flex: 1 1 auto; min-height: 0; padding-bottom: 0; scrollbar-width: auto; }
  .crm-col { max-height: 100%; }
  .crm-kanban::-webkit-scrollbar { height: 12px; width: 12px; }
  .crm-kanban::-webkit-scrollbar-track { background: var(--surface-2); border-radius: 999px; }
  .crm-kanban::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 999px;
    border: 3px solid transparent; background-clip: content-box; }
  .crm-kanban::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }
}

.crm-kcard { display: block; background: var(--surface); border: 1px solid var(--border); border-radius: 9px;
  padding: 9px 11px; text-decoration: none; color: inherit; box-shadow: var(--shadow);
  transition: border-color .1s, box-shadow .1s, transform .1s; }
.crm-kcard:hover { border-color: var(--accent); box-shadow: 0 2px 8px rgba(37,99,235,.12); transform: translateY(-1px); }
.crm-kcard-name { font-weight: 650; font-size: 13px; line-height: 1.3; color: var(--text);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.crm-kcard-meta { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-top: 5px; }
.crm-kcard-region { font-size: 11.5px; color: var(--text-dim); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Region pill, painted from the portal's region registry (board.js regionColor).
   Dark text on every pastel in that palette; the neutral surface is the
   fallback when the registry has no colour for the region. ONE rule for the
   kanban card and the list row — they show the same lead. */
/* In the lead header the same pill is also a DOOR — it opens the name editor.
   It says so with an inner hairline that turns accent on hover, not with the
   dashed underline the plain values wear: a dash under a coloured chip reads
   as a smudge on it. */
.crm-reg-pill-edit { cursor: pointer; box-shadow: inset 0 0 0 1px rgba(26,29,35,.16); }
.crm-reg-pill-edit:hover { box-shadow: inset 0 0 0 1px var(--accent); }
.crm-reg-pill-edit:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.crm-reg-pill { display: inline-block; max-width: 100%; font-size: 11px; font-weight: 600; line-height: 1.55;
  color: #1f2937; background: var(--surface-3); border-radius: 999px; padding: 0 8px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.crm-kcard-price { font-size: 12px; color: var(--text); flex-shrink: 0; }
.crm-kcard-owner { display: flex; align-items: baseline; gap: 8px; font-size: 11px;
  color: var(--text-faint); margin-top: 5px; }
.crm-kcard-owner > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Created date, right edge of the owner line: with no owner it still lands right. */
.crm-kcard-date { margin-left: auto; flex-shrink: 0; }
.crm-kcard.crm-draggable { cursor: grab; }
.crm-kcard.crm-dragging { opacity: .45; cursor: grabbing; }
/* The whole column lights up, because the whole column takes the drop. */
.crm-col.crm-drop-over { background: var(--accent-soft); box-shadow: inset 0 0 0 2px var(--accent); }

/* ── view toggle (Kanban / List) ── */
.crm-viewtabs { display: inline-flex; background: var(--surface-3); border: 1px solid var(--border-2);
  border-radius: 8px; padding: 2px; box-shadow: var(--shadow); }
.crm-viewtab { font: inherit; font-size: 13px; font-weight: 600; color: var(--text-dim); background: none;
  border: 0; padding: 6px 14px; border-radius: 6px; cursor: pointer; }
.crm-viewtab.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }

/* ── multi-select filter dropdown (regions / owners) ── */
.crm-md { position: relative; }
.crm-md-btn { font: inherit; font-size: 13.5px; font-weight: 500; color: var(--text); background: var(--surface);
  border: 1px solid var(--border-2); border-radius: 8px; padding: 8px 12px; cursor: pointer; box-shadow: var(--shadow);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.crm-md-btn.has { border-color: var(--accent); color: var(--accent); }
.crm-md-count { background: var(--accent); color: #fff; border-radius: 999px; font-size: 11px; font-weight: 700; padding: 0 7px; line-height: 17px; }
.crm-md-panel { display: none; position: absolute; top: calc(100% + 4px); left: 0; z-index: 30; min-width: 210px; max-height: 340px;
  overflow-y: auto; background: var(--surface); border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: 0 8px 26px rgba(20,24,33,.16); padding: 6px; }
.crm-md.open .crm-md-panel { display: block; }
.crm-md-panel { max-width: calc(100vw - 16px); }
.crm-md-panel.crm-md-flip { left: auto; right: 0; }   /* set by CrmBoard.render when the default left anchor overflows the viewport */
.crm-md-item { display: flex; align-items: center; gap: 9px; padding: 7px 9px; border-radius: 7px; font-size: 13.5px; cursor: pointer; }
.crm-md-item:hover { background: var(--surface-2); }
.crm-md-item input { cursor: pointer; margin: 0; flex-shrink: 0; }
.crm-md-clear { display: block; width: 100%; text-align: left; font: inherit; font-size: 12px; font-weight: 600; color: var(--text-dim);
  background: none; border: 0; border-bottom: 1px solid var(--border); padding: 6px 9px; margin-bottom: 4px; cursor: pointer; }
.crm-md-clear:hover { color: var(--accent); }
.crm-md-empty { color: var(--text-faint); font-size: 12px; padding: 8px 9px; }

/* ── date-range filter (Created/Closed toggle + presets + custom range) ── */
.crm-date-panel { min-width: 252px; padding: 8px; }
.crm-date-toggle { display: flex; background: var(--surface-3); border: 1px solid var(--border-2);
  border-radius: 8px; padding: 2px; margin-bottom: 8px; }
.crm-date-field { flex: 1; font: inherit; font-size: 13px; font-weight: 600; color: var(--text-dim);
  background: none; border: 0; padding: 6px 0; border-radius: 6px; cursor: pointer; }
.crm-date-field.active { background: var(--surface); color: var(--text); box-shadow: var(--shadow); }
.crm-date-custom { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.crm-date-in { flex: 1; min-width: 0; font: inherit; font-size: 12.5px; color: var(--text);
  background: var(--surface); border: 1px solid var(--border-2); border-radius: 7px; padding: 6px 8px; }
.crm-date-dash { color: var(--text-faint); }
.crm-date-presets { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.crm-date-preset { font: inherit; font-size: 12.5px; color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 7px; padding: 6px 8px; cursor: pointer; text-align: left; }
.crm-date-preset:hover { border-color: var(--accent); color: var(--accent); }
.crm-date-preset.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.crm-date .crm-md-clear { margin: 8px 0 0; border-bottom: 0; border-top: 1px solid var(--border); padding-top: 8px; }

/* ── List (sortable table) ── */
.crm-listwrap { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  overflow: auto; box-shadow: var(--shadow); }
.crm-list { width: 100%; border-collapse: collapse; font-size: 13px; }
.crm-list thead th { position: sticky; top: 0; z-index: 1; background: var(--surface-2); text-align: left;
  font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: .03em; color: var(--text-dim);
  padding: 10px 14px; border-bottom: 1px solid var(--border); cursor: pointer; white-space: nowrap; user-select: none; }
.crm-list thead th:hover { color: var(--text); }
.crm-list thead th.sorted { color: var(--accent); }
.crm-list th.num, .crm-list td.num { text-align: right; }
.crm-list tbody td { padding: 10px 14px; border-bottom: 1px solid var(--border); vertical-align: middle; }
.crm-list tbody tr:last-child td { border-bottom: 0; }
.crm-row { cursor: pointer; }
.crm-row:hover td { background: var(--accent-soft); }
.crm-td-name { font-weight: 650; color: var(--text); }
.crm-td-stage { display: inline-flex; align-items: center; gap: 7px; }
.crm-td-stage .crm-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.crm-td-region, .crm-td-owner { color: var(--text-dim); }
.crm-td-created { white-space: nowrap; color: var(--text-dim); }

/* ── lead detail ── */
.crm-l-head { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 18px; margin-bottom: 14px; box-shadow: var(--shadow); }
.crm-l-title { display: flex; align-items: flex-start; gap: 8px; }
/* flex: 0 1 auto, NOT 1 1 auto (owner, 2026-08-18): the title is a click target
   that opens the name editor, so its box must stop where the text stops. Growing
   it to fill the row made the whole empty strip up to the ⋯ menu open the editor
   — the tooltip followed the cursor across half the card. It still SHRINKS, so a
   long name wraps inside the row instead of pushing the menu off it. */
.crm-l-title h1 { flex: 0 1 auto; min-width: 0; font-size: 20px; font-weight: 800; margin: 0;
  letter-spacing: -.02em; line-height: 1.25; }
/* THE BAR — everything about the lead except its name, in named groups.
   One markup, two layouts: this block is the phone (groups stack into rows,
   the <i class="crm-l-brk"> breaks decide where), and the 900px @media below
   turns the same groups into a divided bar. Nothing here is ever truncated:
   the longest stage name is 63 characters and the longest refusal reason 47,
   and both are read, not glanced at. */
.crm-l-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 7px 9px; margin-top: 8px;
  font-size: 13px; color: var(--text-dim); }
.crm-l-grp { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.crm-l-grp-state { flex-wrap: wrap; }
.crm-l-brk { flex-basis: 100%; height: 0; }                 /* a row break, nothing visible */
.crm-l-grp-tags { flex-wrap: wrap; gap: 6px; }
.crm-l-grp-icons { gap: 5px; margin-left: auto; }
.crm-l-kv { display: inline-flex; align-items: baseline; gap: 6px; }
.crm-l-kvk { display: none; }                               /* the label is a wide-screen thing */
.crm-l-kvv { font-weight: 700; color: var(--text); white-space: nowrap; }
/* Running together on one line, the groups need the separator the vertical
   rules give them on a wide screen. Scoped to the narrow layout by a media
   query of its own rather than cancelled from inside the wide one: a media
   query adds no specificity, so `content: none` there lost to this selector
   and both the dot and the rule were drawn for the same boundary. */
@media (max-width: 899.98px) {
  .crm-l-grp-place ~ .crm-l-grp:not(.crm-l-grp-tags):not(.crm-l-grp-icons)::before {
    content: '·'; color: var(--border-2); margin-right: 3px; }
}
/* map links as squares: W = our map, G = Google, 📍 = our coordinates */
.crm-l-mbtn { position: relative; display: inline-flex; width: 26px; height: 26px; align-items: center;
  justify-content: center; border-radius: 7px; text-decoration: none; font-size: 12px; font-weight: 800;
  line-height: 1; box-sizing: border-box; }
.crm-l-mbtn:hover { box-shadow: 0 2px 6px rgba(15,23,42,.18); }
.crm-l-mbtn-pin { background: var(--surface); border: 1px solid #cfd5df; }
.crm-l-mbtn-g { color: #4285f4; background: var(--surface); border: 1px solid #cfd5df; }
.crm-l-mbtn-g::after { content: ''; position: absolute; left: 4px; right: 4px; bottom: 3px; height: 2px;
  border-radius: 2px; background: linear-gradient(90deg,#4285f4 0 25%,#ea4335 25% 50%,#fbbc05 50% 75%,#34a853 75% 100%); }
.crm-l-mbtn-w { color: #fff; background: var(--accent); border: 1px solid var(--accent); }
/* M — the math model. Purple, not the accent blue W wears: side by side the two
   letters are told apart by colour before they are read. #7c3aed is the V2
   colour already used by the /models badge and app.js's popup button. */
.crm-l-mbtn-m { color: #fff; background: #7c3aed; border: 1px solid #7c3aed; }
/* ⋯ — the lead's own actions, out of the row and out of the way */
/* margin-left: auto — the title no longer grows, so the menu holds the right edge itself. */
.crm-l-morewrap { position: relative; flex: 0 0 auto; margin-left: auto; }
.crm-l-more { font: inherit; font-size: 15px; line-height: 1; color: var(--text-faint); background: none;
  border: 1px solid transparent; border-radius: 7px; padding: 3px 8px; cursor: pointer; }
.crm-l-more:hover { background: var(--surface-3); color: var(--text); }
.crm-l-moremenu { display: none; position: absolute; top: calc(100% + 4px); right: 0; z-index: 40;
  min-width: 160px; background: var(--surface); border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: 0 8px 26px rgba(20,24,33,.16); padding: 5px; }
.crm-l-morewrap.open .crm-l-moremenu { display: block; }
.crm-l-moreitem { display: block; width: 100%; text-align: left; font: inherit; font-size: 13px;
  color: var(--text); background: none; border: 0; border-radius: 7px; padding: 8px 10px; cursor: pointer; }
.crm-l-moreitem:hover { background: var(--surface-2); }
.crm-l-moreitem-danger { color: #b3261e; }
.crm-l-moreitem-danger:hover { background: #fdf3f2; }
/* The stage pill wears the stage's own colour — stagePillStyle() in lead-view.js
   sets --pill-* per lead. The fallbacks keep the pill readable if it is ever
   rendered without that style attribute (unknown status → no colour at all). */
.crm-l-stage { display: inline-block; font-weight: 600; color: var(--pill-fg, var(--accent));
  background: var(--pill-bg, var(--accent-soft)); border: 1px solid transparent;
  border-radius: 7px; padding: 2px 9px;
  /* The longest stage name is 63 characters and wraps on a phone. It is a
     <button>, so without this the second line centres itself under the first. */
  text-align: left; }
/* editable stage picker on the lead card */
.crm-l-stagewrap { position: relative; display: inline-block; }
.crm-l-stage-btn { font: inherit; font-weight: 600; cursor: pointer; }   /* `font` shorthand resets the weight — restate it */
.crm-l-stage-btn:hover { border-color: var(--pill-edge, var(--accent)); }
.crm-l-stage-menu { display: none; position: absolute; top: calc(100% + 4px); left: 0; z-index: 40;
  min-width: 230px; max-height: 320px; overflow-y: auto; background: var(--surface);
  border: 1px solid var(--border-2); border-radius: 10px; box-shadow: 0 8px 26px rgba(20,24,33,.16); padding: 5px; }
.crm-l-stagewrap.open .crm-l-stage-menu { display: block; }
.crm-l-stage-opt { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; font: inherit;
  font-size: 13px; color: var(--text); background: none; border: 0; border-radius: 6px; padding: 7px 9px; cursor: pointer; }
.crm-l-stage-opt:hover { background: var(--surface-2); }
.crm-l-stage-opt.active { color: var(--accent); font-weight: 600; }
.crm-l-stage-opt .crm-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
/* inline field editing on the lead card (AUTOSAVE on editor close; Esc cancels) */
.crm-l-editable { cursor: pointer; border-bottom: 1px dashed var(--border-2); }
.crm-l-editable:hover { color: var(--accent); border-bottom-color: var(--accent); }
.crm-l-frow-edit { align-items: center; }                                   /* editor stays inline (on the value side) */
.crm-l-editslot { flex: 1 1 auto; min-width: 0; }                           /* fills the value side of the row */
/* choice modal — "Select a refusal reason" when moving into Unsuccessful deal;
   shared by the board drop and the card stage menu; options reuse .crm-l-fmenu-opt */
.crm-choice-mask { position: fixed; inset: 0; z-index: 100; background: rgba(20,24,33,.35);
  display: flex; align-items: center; justify-content: center; padding: 16px; }
.crm-choice { background: var(--surface); border: 1px solid var(--border-2); border-radius: 12px;
  box-shadow: 0 12px 40px rgba(20,24,33,.25); padding: 14px; width: 100%; max-width: 380px;
  max-height: 80vh; overflow-y: auto; }
.crm-choice-title { font-size: 14px; font-weight: 700; margin-bottom: 2px; }
.crm-choice-hint { font-size: 12px; color: var(--text-dim); margin-bottom: 10px; }
.crm-choice-cancel { width: 100%; margin-top: 10px; }
/* the committing button of a two-step choice (hand this lead over) */
.crm-choice-go { width: 100%; margin-top: 12px; padding: 9px 14px; font-size: 13px; }
/* dropdown fields: custom options menu (same pattern as the stage picker) —
   the list is open on the FIRST click in every browser, and unlike a native
   <select> popup it stays visible on Google Meet screen shares */
.crm-l-fmenuwrap { position: relative; flex: 1 1 auto; min-width: 0; display: flex; justify-content: flex-end; }
.crm-l-fmenu { position: absolute; top: calc(100% + 4px); right: 0; z-index: 40; min-width: 200px; max-height: 300px;
  overflow-y: auto; background: var(--surface); border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: 0 8px 26px rgba(20,24,33,.16); padding: 5px; }
.crm-l-fmenu-opt { display: block; width: 100%; text-align: left; font: inherit; font-size: 13px; color: var(--text);
  background: none; border: 0; border-radius: 7px; padding: 7px 10px; cursor: pointer; white-space: nowrap; }
.crm-l-fmenu-opt:hover { background: var(--surface-2); }
.crm-l-fmenu-opt.active { color: var(--accent); font-weight: 600; }
.crm-l-fmenu-new { color: var(--accent); font-weight: 600; border-top: 1px solid var(--border); border-radius: 0 0 7px 7px; }
.crm-l-fmenu-hint { color: var(--text-faint); font-size: 12px; padding: 7px 10px; }
/* header-hosted fmenu (Refusal, under the stage pill): don't stretch inside
   the flex .crm-l-bar row, and anchor the menu left like the stage menu */
.crm-l-bar .crm-l-fmenuwrap { flex: 0 0 auto; display: inline-flex; }
.crm-l-bar .crm-l-fmenu { left: 0; right: auto; }
/* Reason for refusal — a chip, whole, never shortened. Red because a refused
   lead is the one state somebody scans a card for. */
.crm-l-ref { display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 600;
  color: #b3261e; background: #fdf3f2; border: 1px solid #f3d6d3; border-radius: 7px; padding: 3px 8px;
  cursor: pointer; text-align: left; }
.crm-l-ref:hover { border-color: #b3261e; }
.crm-l-ref-ro { cursor: default; }
.crm-l-ref-ro:hover { border-color: #f3d6d3; }
/* No reason yet: the "+ tag" language from the bottom of this same card, so it
   reads as an invitation instead of as a filled-in field. */
.crm-l-refadd { font: inherit; font-size: 12px; font-weight: 600; color: var(--text-faint);
  background: none; border: 1px dashed var(--border-2); border-radius: 7px; padding: 3px 9px; cursor: pointer; }
.crm-l-refadd:hover { color: #b3261e; border-color: #f3d6d3; background: #fdf3f2; }
/* The owner in the header. A control for whoever may hand the lead over — a
   manager or an admin — and the plain text it always was for everybody else,
   so nothing invites a scout to press what the server will refuse. 44px of
   thumb on a phone: the header row is where a fat finger lands first. */
.crm-l-owner { font: inherit; color: inherit; background: none; border: 0; border-radius: 6px;
  padding: 1px 5px; margin: -1px -5px; cursor: pointer; }
.crm-l-owner:hover { background: var(--surface-3); color: var(--text); }
/* THE NAME EDITOR — clicking the title opens it (owner 2026-08-11). The title
   keeps its size and weight and gains the dashed underline every editable value
   on this card carries, so it reads as the same affordance rather than a link. */
/* text-decoration, not border-bottom: the h1 is a block, so a border drew a
   dashed rule across the whole card — detached from the name on a phone, where
   the 44px floor below pushed it further away, and reading as a divider. */
.crm-l-h1edit { cursor: pointer; text-decoration: underline dashed var(--border-2);
  text-underline-offset: 4px; text-decoration-thickness: 1px; }
.crm-l-h1edit:hover { color: var(--accent); text-decoration-color: var(--accent); }
.crm-l-h1edit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.crm-l-namep { margin-top: 12px; padding: 12px; border: 1px solid var(--border-2);
  border-radius: 10px; background: var(--surface-2); max-width: 560px; }
.crm-l-nprow { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.crm-l-nprow > span { flex: 0 0 84px; font-size: 12px; color: var(--text-dim); }
.crm-l-nprow .crm-input { flex: 1 1 auto; min-width: 0; }
/* Region and Subregion use the card's own dropdown, not a <select>: a native
   popup is invisible to viewers of a shared browser tab (CLAUDE.md, Known
   Issues), and choosing a region is what happens on a call. The shared
   .crm-l-fmenuwrap right-aligns for the fields panel — here the value sits
   at the start of the row, under a label, so the menu anchors left. */
.crm-l-npmenu { justify-content: flex-start; }
.crm-l-npmenu .crm-l-fmenu { left: 0; right: auto; min-width: 220px; }
.crm-l-npmenu .crm-l-fval { padding: 6px 0; }
/* The two lines that make a rename honest: what it becomes, over what it is. */
.crm-l-npout { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.crm-l-npline { display: flex; align-items: baseline; gap: 10px; font-size: 13px; line-height: 1.5; }
.crm-l-npline > .crm-l-nplbl { flex: 0 0 84px; font-size: 12px; color: var(--text-faint); }
.crm-l-npline > b { min-width: 0; word-break: break-word; }
.crm-l-npold { min-width: 0; color: var(--text-dim); word-break: break-word; }
.crm-l-npwarn { margin-top: 8px; font-size: 12px; line-height: 1.45; color: #92400e;
  background: #fef3c7; border: 1px solid #fde68a; border-radius: 8px; padding: 6px 9px; }
.crm-l-npwarn[hidden] { display: none; }
.crm-l-npmsg { align-self: center; font-size: 12px; color: #b91c1c; }
/* canonical Region · Subregion in the header — read-only text that opens the name editor */
.crm-l-regionwrap { display: inline-flex; align-items: baseline; gap: 5px; }
/* Field-group heading — a BUTTON that folds its group away. Same face it had as
   a plain heading, plus the resets a button needs and a caret that turns. The
   first group keeps its top rule off the panel edge. */
.crm-l-fgroup { display: flex; align-items: center; gap: 6px; width: 100%;
  font: inherit; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-dim); background: none; text-align: left; cursor: pointer;
  border: 0; border-top: 1px solid var(--border); margin-top: 12px; padding: 10px 0 2px; }
.crm-l-fgrp:first-child .crm-l-fgroup { border-top: 0; margin-top: 0; }
.crm-l-fgroup:hover { color: var(--text); }
.crm-l-fgcaret { font-size: 9px; line-height: 1; transition: transform .12s ease; }
.crm-l-fgrp.shut .crm-l-fgcaret { transform: rotate(-90deg); }
.crm-l-fglabel { flex: 1 1 auto; }
/* What is inside a folded group — an empty heading says nothing about whether
   there is anything behind it. */
.crm-l-fgcount { font-weight: 600; font-variant-numeric: tabular-nums; opacity: .6; }
.crm-l-fgrp.shut .crm-l-fgrows { display: none; }
/* tag chips in the lead header — a group of .crm-l-bar now (.crm-l-grp-tags) */
.crm-l-tag { display: inline-flex; align-items: center; gap: 3px; font-size: 11.5px; font-weight: 600;
  color: var(--text-dim); background: var(--surface-3); border: 1px solid var(--border-2);
  border-radius: 999px; padding: 1px 8px; max-width: 240px; }
.crm-l-tagtxt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crm-l-tagrm { font: inherit; font-size: 13px; line-height: 1; color: var(--text-faint); background: none;
  border: 0; padding: 0 0 0 2px; cursor: pointer; }
.crm-l-tagrm:hover { color: #b91c1c; }
.crm-l-tagin { font: inherit; font-size: 12px; color: var(--text); background: var(--surface);
  border: 1px dashed var(--border-2); border-radius: 999px; padding: 2px 10px; width: 90px; }
.crm-l-tagin:focus { outline: none; border-color: var(--accent); border-style: solid; width: 160px; }
/* A scout gets the same affordance as a button: the menu is their whole
   permission, so there is nothing to type into. Same dashed pill, so the header
   does not change shape depending on who is looking at it. */
.crm-l-tagadd { font: inherit; font-size: 12px; color: var(--text-dim); background: var(--surface);
  border: 1px dashed var(--border-2); border-radius: 999px; padding: 2px 10px; cursor: pointer; }
.crm-l-tagadd:hover, .crm-l-tagadd[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }
/* the existing-tag suggestions menu — our own DOM, not a native <datalist>,
   because the native popup never appeared in the phone PWA (owner 2026-08-18).
   Anchored under the input; options reuse .crm-l-fmenu-opt, with a taller tap
   target than the field menus since this one is used on a phone. */
.crm-l-tagwrap { position: relative; display: inline-flex; }
.crm-l-tagmenu { left: 0; right: auto; min-width: 180px; max-width: min(320px, 78vw); max-height: 260px; }
.crm-l-tagmenu .crm-l-fmenu-opt { padding: 9px 10px; overflow: hidden; text-overflow: ellipsis; }
.crm-l-tagmenu .crm-l-fmenu-opt.on { background: var(--surface-2); }
/* `display:block` on the shared option class outranks the browser's own
   [hidden] rule, so a filtered-out option stayed on screen (caught in the
   harness). Say it explicitly rather than restating display on every option. */
.crm-l-tagmenu .crm-l-fmenu-opt[hidden] { display: none; }
.crm-l-frow-edit.crm-l-frow-wide { flex-direction: column; align-items: stretch; gap: 6px; }  /* textarea full-width below label */
.crm-l-fin { font: inherit; font-size: 13px; color: var(--text); background: var(--surface);
  border: 1px solid var(--accent); border-radius: 6px; padding: 6px 8px; width: 100%; box-sizing: border-box; }
.crm-l-fin-area { resize: vertical; white-space: pre-wrap; }
/* fields autosave — brief per-row confirmation after a successful write */
.crm-l-savedflash { color: #15803d; font-size: 11px; font-weight: 700; margin-left: 6px; flex-shrink: 0;
  animation: crm-saved-fade 1.5s forwards; }
@keyframes crm-saved-fade { 0%, 60% { opacity: 1; } 100% { opacity: 0; } }
.crm-l-fsave { font: inherit; font-size: 12.5px; font-weight: 600; background: var(--accent); color: #fff;
  border: 0; border-radius: 6px; padding: 5px 12px; cursor: pointer; }
.crm-l-fsave:disabled { opacity: .5; cursor: default; }
.crm-l-fdiscard { font: inherit; font-size: 12.5px; font-weight: 600; background: var(--surface); color: var(--text-dim);
  border: 1px solid var(--border-2); border-radius: 6px; padding: 5px 12px; cursor: pointer; }
.crm-l-editpen { font: inherit; font-size: 12px; color: var(--text-faint); background: none; border: 0;
  cursor: pointer; padding: 0 2px; margin-left: 4px; }
.crm-l-editpen:hover { color: var(--accent); }
/* contact display card — labelled rows (name heading, then Position/Phones/Emails) */
.crm-l-cview { padding: 4px 0 8px; }
.crm-l-cview + .crm-l-cview { border-top: 1px solid var(--border); padding-top: 10px; }  /* separator between contacts, no trailing border (avoids :last-of-type gotcha) */
.crm-l-cvhead { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 4px; }
.crm-l-cvname { font-size: 13.5px; font-weight: 700; color: var(--text); word-break: break-word; }
.crm-l-cvrow { display: flex; gap: 10px; font-size: 13px; padding: 2px 0; }
.crm-l-cvlbl { color: var(--text-dim); flex-shrink: 0; min-width: 64px; }
.crm-l-cvval { color: var(--text); word-break: break-word; min-width: 0; }
/* contact edit form (one Save per contact) */
.crm-l-cform { padding: 8px 0; }
.crm-l-crow { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.crm-l-crow span { color: var(--text-dim); font-size: 12.5px; min-width: 56px; flex-shrink: 0; }
.crm-l-cbar { display: flex; gap: 8px; margin-top: 2px; }
.crm-l-addcontact { font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent); background: none;
  border: 1px dashed var(--border-2); border-radius: 7px; padding: 6px 10px; margin-top: 8px; cursor: pointer; width: 100%; }
.crm-l-addcontact:hover { border-color: var(--accent); background: var(--accent-soft); }

/* ── the header on a wide screen (≥900px) ───────────────────────────────────
   The same groups, now a divided bar (owner's pick, 2026-08-18). Vertical rules
   run the FULL height of the bar rather than each group's own height: the state
   group is two lines tall (stage over reason), and a short divider beside it
   broke off in the middle of its neighbour and read as a stray tick.

   900, not the 640 the rest of the page switches at: between the two the page
   is already the two-column desktop grid, but four divided groups on one line
   are cramped there — the stack still reads better. */
@media (min-width: 900px) {
  .crm-l-bar { align-items: stretch; gap: 0 20px; }
  .crm-l-brk { display: none; }                    /* the phone's row breaks */
  .crm-l-grp { align-items: flex-start; }
  /* Only a group that FOLLOWS another on the bar line draws one, so the row can
     lose any of them — price hidden by field access, icons absent — without
     leaving a rule dividing nothing. */
  .crm-l-grp-state ~ .crm-l-grp:not(.crm-l-grp-place):not(.crm-l-grp-tags):not(.crm-l-grp-icons) {
    padding-left: 20px; border-left: 1px solid var(--border); }
  /* Place belongs to the name, so it goes ABOVE the bar as its own line and
     draws the rule the bar sits under. */
  .crm-l-grp-place { order: -1; flex: 1 1 100%; padding: 0 0 10px; margin-bottom: 11px;
    border-bottom: 1px solid var(--border); }
  .crm-l-grp-state { flex-direction: column; align-items: flex-start; gap: 5px; }
  /* Tags close the header on their own line, under everything they describe. */
  .crm-l-grp-tags { order: 10; flex: 1 1 100%; margin-top: 11px; }
  .crm-l-grp-icons { align-items: center; }
  /* Named columns: the label is what makes the number readable without a
     sentence around it, and there is room for it here. */
  .crm-l-kv { flex-direction: column; align-items: flex-start; gap: 0; }
  .crm-l-kvk { display: block; font-size: 10.5px; font-weight: 600; text-transform: uppercase;
    letter-spacing: .05em; color: var(--text-faint); margin-bottom: 3px; white-space: nowrap; }
  .crm-l-kvv { font-size: 15px; }
  .crm-l-kvv-s { font-size: 13.5px; font-weight: 700; color: var(--text); }
}

/* Fields (key data) on the LEFT, Timeline (history) on the RIGHT + wide. */
.crm-l-cols { display: grid; grid-template-columns: 400px 1fr; gap: 14px; align-items: stretch; }

/* Independent vertical scroll (amoCRM-style): the header + lead-head stay put;
   Fields and Timeline each scroll on their own. Scoped to the lead page via :has
   so the kanban board page is unaffected. */
body:has(.crm-l-cols) { height: 100vh; overflow: hidden; display: flex; flex-direction: column; }
body:has(.crm-l-cols) main { flex: 1; min-height: 0; display: flex; flex-direction: column; padding-bottom: 8px; }
body:has(.crm-l-cols) #crm-root { flex: 1; min-height: 0; display: flex; flex-direction: column; }
body:has(.crm-l-cols) .crm-l-cols { flex: 1; min-height: 0; }
body:has(.crm-l-cols) .crm-l-side,
body:has(.crm-l-cols) .crm-l-main { min-height: 0; max-height: 100%; overflow-y: auto; padding-right: 4px; scrollbar-width: thin; }
body:has(.crm-l-cols) .crm-l-side > :last-child,
body:has(.crm-l-cols) .crm-l-main > :last-child { margin-bottom: 0; }
.crm-l-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; margin-bottom: 14px; box-shadow: var(--shadow); }
/* z-index above the sticky Timeline heading: on a short viewport the two
   pinned bars meet, and the composer must stay the one on top. */
.crm-l-composer { position: sticky; bottom: 0; z-index: 3; margin-bottom: 0; }
.crm-l-composer .crm-l-crow { margin-bottom: 8px; }
.crm-l-composer textarea.crm-l-fin { width: 100%; resize: vertical; }
/* "What" and "to whom" on ONE line (owner, 2026-08-21) — Send left this row for
   the attach bar below, which is what freed the width. The kind gives way first
   if it ever has to: three known words are cheap to abbreviate, a person's name
   is not. The arrow needs min-width:0 because .crm-l-crow span imposes a 56px
   floor meant for the written labels in the contact form. */
.crm-l-composer .crm-l-crow-pick { display: flex; gap: 8px; align-items: center; }
.crm-l-composer .crm-l-crow-pick select { width: auto; }
.crm-l-composer .crm-l-crow-pick .crm-l-ckind { flex: 0 1 auto; min-width: 0; }
/* Grows to the room a PHONE has, and stops there. Without the ceiling the
   same rule stretched the name across a 1300px card — a select the width of
   the screen holding the word "Lek". The cap is above any width the phone
   row can offer (kind + arrow leave it ~200px), so this changes nothing
   there and only reins in the desktop. */
.crm-l-composer .crm-l-crow-pick .crm-l-cto { flex: 1 1 auto; min-width: 0; max-width: 280px; }
.crm-l-composer .crm-l-crow-pick span.crm-l-carrow { min-width: 0; flex: 0 0 auto;
  color: var(--text-faint); }
.crm-l-panel h2 { font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-dim); margin: 0 0 12px; }
.crm-l-count { background: var(--surface-3); color: var(--text-dim); border-radius: 999px; padding: 1px 8px; font-size: 11px; }
/* A panel heading that folds its own body. The <h2> keeps the face it had —
   the button around it is the control, so nothing about the panel looks
   different until it is folded. */
.crm-l-panelfold { display: flex; align-items: center; gap: 6px; width: 100%;
  font: inherit; color: inherit; background: none; border: 0; padding: 0; text-align: left; cursor: pointer; }
.crm-l-panelfold h2 { margin: 0; }
.crm-l-panel:not(.shut) > .crm-l-panelfold { margin-bottom: 12px; }   /* the h2's old gap, moved out */
.crm-l-panelfold:hover h2 { color: var(--text); }
.crm-l-panel.shut .crm-l-panelbody { display: none; }
.crm-l-panel.shut .crm-l-fgcaret { transform: rotate(-90deg); }
.crm-l-frow { display: flex; justify-content: space-between; gap: 12px; padding: 7px 0; border-bottom: 1px solid var(--border); font-size: 13px; }
.crm-l-frow:last-child { border-bottom: 0; }
.crm-l-frow span { color: var(--text-dim); flex-shrink: 0; }
.crm-l-frow b { text-align: right; word-break: break-word; }
/* wide row for long textarea fields (Comments) — stack label over value, keep line breaks */
.crm-l-frow-wide { flex-direction: column; align-items: stretch; gap: 4px; }
.crm-l-frow-wide b { text-align: left; font-weight: 500; }
.crm-l-pre { white-space: pre-wrap; }
.crm-l-frow b a { color: var(--accent); text-decoration: none; font-weight: 600; }
.crm-l-frow b a:hover { text-decoration: underline; }

/* Media & Documents — Drive/photo/video link buttons */
.crm-l-media-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.crm-l-media { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600;
  color: var(--accent); background: var(--accent-soft); border: 1px solid #d4e2ff; border-radius: 8px;
  padding: 6px 11px; text-decoration: none; max-width: 100%; }
.crm-l-media:hover { border-color: var(--accent); box-shadow: var(--shadow); }

/* Timeline system-entry toggle (field-change noise hidden by default) */
.crm-l-tlwrap h2 { display: flex; align-items: center; gap: 8px; }
.crm-l-systoggle { margin-left: auto; font-size: 11px; font-weight: 600; color: var(--text-dim);
  text-transform: none; letter-spacing: 0; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; }
.crm-l-systoggle input { cursor: pointer; margin: 0; }
.crm-l-tlwrap .crm-l-sys { display: none; }
.crm-l-tlwrap:has(.crm-l-syschk:checked) .crm-l-sys { display: block; }
/* The heading stays pinned while the feed scrolls under it — it carries the
   entry count and the "show N system changes" switch. A lead with 47 entries
   opens with the top of the feed already out of view, so that switch used to be
   a full scroll away from wherever you were reading (owner, 2026-08-19).
   Negative margins pull the bar out to the panel's padding edges: without them
   entries slide past the heading through the 16px gutters. */
body:has(.crm-l-cols) .crm-l-tlwrap h2 { position: sticky; top: 0; z-index: 2;
  margin: -14px -16px 12px; padding: 12px 16px; background: var(--surface);
  border-bottom: 1px solid var(--border); border-radius: var(--radius) var(--radius) 0 0; }

.crm-l-tl { padding: 8px 0 8px 13px; border-left: 2px solid var(--border-2); margin-left: 3px; }
.crm-l-tl.crm-l-sys { border-left-color: var(--border); }
.crm-l-tl.crm-l-internal { border-left-color: #f59e0b; }
.crm-l-tl-meta { display: flex; justify-content: space-between; gap: 10px; font-size: 11px; color: var(--text-faint); }
.crm-l-tl-body { font-size: 13px; margin-top: 2px; word-break: break-word; white-space: pre-wrap; }
.crm-l-svc { color: var(--text-faint); font-style: normal; font-size: 11px; }
/* The composer's own refusal, under the field it is about. Red and 12px: the
   hint below it is grey and 11px, and a warning that looks like a hint is a
   warning nobody reads. */
.crm-l-cerr { margin: 4px 0 0; color: #b91c1c; font-size: 12px; font-weight: 600; }
.crm-l-fin[aria-invalid="true"] { border-color: #b91c1c; }
.crm-l-task { font-size: 13px; padding: 11px 0; border-top: 1px solid var(--border); }
.crm-l-task:first-of-type { border-top: 0; }
.crm-l-task-line { display: flex; align-items: stretch; gap: 10px; min-width: 0; }
/* A STATE, not a control — and now a RAIL rather than a circle. The ○ read as
   an unticked checkbox and did nothing when tapped; a coloured edge says the
   same thing the pill says in words and invites no click at all. Its colour is
   set from the row's band below. */
.crm-l-task-toggle { width: 3px; flex: 0 0 3px; border-radius: 3px; background: var(--border-2);
  align-self: stretch; cursor: default; }
.crm-l-task.band-overdue > .crm-l-task-line > .crm-l-task-toggle { background: #dc2626; }
.crm-l-task.band-today > .crm-l-task-line > .crm-l-task-toggle { background: #e0a020; }
.crm-l-task.done > .crm-l-task-line > .crm-l-task-toggle { background: #bbf7d0; }
/* The text and the deadline share the top line: the pill is the first thing the
   eye reaches after the words, instead of the last thing in a grey byline. */
.crm-l-task-head { display: flex; align-items: flex-start; gap: 8px; }
.crm-l-task-head .crm-l-task-txt { flex: 1 1 auto; min-width: 0; }
.crm-l-task-when { flex: 0 0 auto; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--border-2);
  background: var(--surface-3); color: var(--text-dim); font: 700 11px/1.5 inherit; white-space: nowrap; }
.crm-l-task-when.late { background: #fef2f2; border-color: #fecaca; color: #b91c1c; }
.crm-l-task-when.today { background: #fff8ec; border-color: #f5dcb3; color: #a36100; }
.crm-l-task-when.done { background: #f0fdf4; border-color: #bbf7d0; color: #15803d; }
.crm-l-task-when.none { background: transparent; }
/* Two faces and an arrow: the same two or three people recur on every lead, and
   a disc is recognised where a name has to be read. The names stay beside them
   — for a screen reader, and for anyone who does not know the initials yet. */
.crm-l-task-by { display: inline-flex; align-items: center; gap: 5px; min-width: 0; }
.crm-l-task-arrow { color: var(--text-faint); margin: 0 2px; }
.crm-l-av { flex: 0 0 auto; width: 20px; height: 20px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: hsl(var(--av-h, 220) 55% 42%); color: #fff; font: 700 9px/1 inherit; letter-spacing: .02em; }
/* The verb and the discussion sit on one line under the row, so a task is one
   block of related things rather than a control floating beside the text. */
.crm-l-task-acts { display: flex; align-items: center; gap: 8px; margin-top: 7px; min-width: 0; }
/* The discussion line takes the room the verb leaves and truncates inside it —
   `width: 100%` (what it has on its own) pushed the last comment's date past
   the edge of the drawer. */
.crm-l-task-acts .crm-l-task-threadline { margin: 0; width: auto; flex: 1 1 auto; min-width: 0; }
/* The bands of the open tab. A heading is drawn only when something is under
   it — see renderTaskDrawer. */
.crm-l-taskgrp { display: flex; align-items: center; gap: 7px; margin: 16px 0 2px; }
.crm-l-taskgrp:first-child { margin-top: 6px; }
.crm-l-taskgrp h3 { margin: 0; font: 700 11px/1 inherit; letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-dim); }
.crm-l-taskgrp.band-overdue h3 { color: #b91c1c; }
.crm-l-taskgrp.band-today h3 { color: #a36100; }
.crm-l-taskgrp + .crm-l-task { border-top: 0; }
/* The verb. Sits at the end of the line where the eye lands after the text, and
   only exists for somebody who may actually do it — nothing greyed out to poke
   at and wonder about. */
.crm-l-task-act { flex: 0 0 auto; min-height: 32px; padding: 0 10px;
  border: 1px solid var(--border-2); border-radius: 8px; background: var(--surface);
  color: var(--accent); font: 600 12px/1 inherit; cursor: pointer; white-space: nowrap; }
.crm-l-task-act:hover:not(:disabled) { border-color: var(--accent); background: var(--accent-soft); }
.crm-l-task-act:disabled { cursor: wait; opacity: .55; }
.crm-l-task-copy { flex: 1; min-width: 0; }
/* The instruction keeps the line breaks it was written with: a task is
   typically a list of two or three things to do, and collapsing them into
   one run-on sentence is how it read until 2026-08-24. The list page
   (.crm-t-txt) still clamps to two lines on purpose — that is a row, this
   is the full instruction. */
.crm-l-task-txt { color: var(--text); font-weight: 600; line-height: 1.35; overflow-wrap: anywhere;
  white-space: pre-wrap; }
.crm-l-task.done .crm-l-task-txt { color: var(--text-faint); text-decoration: line-through; }
.crm-l-task-meta { display: flex; flex-wrap: wrap; gap: 3px 10px; margin-top: 3px; color: var(--text-dim); font-size: 11.5px; }
.crm-l-task-meta time { flex-shrink: 0; }
.crm-l-task-overdue { color: #b91c1c; font-weight: 700; }
.crm-l-task-today { color: #a36100; font-weight: 700; }
/* The discussion, as a line under the task rather than a control beside it.
   Full width so a long last message truncates against the row, not against a
   button's idea of its own size. */
.crm-l-task-threadline { display: flex; align-items: baseline; gap: 6px; width: 100%;
  margin: 4px 0 0; padding: 4px 6px 4px 0; border: 0; border-radius: 8px;
  background: transparent; color: var(--text-dim); font: inherit; font-size: 12px;
  text-align: left; cursor: pointer; }
.crm-l-task-threadline:hover { background: var(--surface-2); color: var(--text); }
.crm-l-task-threadline .crm-l-task-thread-ic { flex: 0 0 auto; font-size: 11px; }
.crm-l-task-thread-n { flex: 0 0 auto; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
/* ONE LINE, always. A three-line last comment would push the next task off the
   screen and turn a summary back into the thing it summarises. */
.crm-l-task-thread-lbl { flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.crm-l-task-threadline time { flex: 0 0 auto; opacity: .8; }
/* Indented to the RAIL's content edge (3px rail + 10px gap), not to the 22px
   circle that used to sit there. */
.crm-l-task-result, .crm-l-task-audit-result { margin: 8px 0 0 13px; padding: 7px 9px; border-radius: 7px;
  background: var(--surface-2); color: var(--text-dim); white-space: pre-wrap; overflow-wrap: anywhere; }
.crm-l-task-audit-result { margin-left: 0; }
.crm-l-task-complete-form, .crm-l-task-thread { margin: 10px 0 0 13px; padding: 10px; border-radius: 9px;
  background: var(--surface-2); }
.crm-l-task-complete-form label { display: block; color: var(--text-dim); font-size: 12px; font-weight: 600; }
.crm-l-task-complete-form textarea, .crm-l-task-comment-form textarea { display: block; width: 100%; box-sizing: border-box;
  margin-top: 5px; padding: 9px 10px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface); color: var(--text);
  font: 13px/1.4 inherit; resize: vertical; }
.crm-l-task-formacts { display: flex; align-items: flex-end; justify-content: flex-end; gap: 8px; margin-top: 8px; }
/* The forms stack now — a textarea, its attach row, then the verb. The row of
   chips has nowhere to go beside a button. */
.crm-l-task-comment-form { display: block; margin-top: 8px; }
.crm-l-task-comment-form .crm-l-attbar, .crm-l-task-complete-form .crm-l-attbar { margin-top: 8px; }
/* WHY THE BUTTON IS OFF, in words under the control it disables. */
.crm-l-task-hint { margin-top: 6px; color: var(--text-dim); font-size: 11.5px; }
/* SCOPED TO THE VERB ROW, not to every button in the form. A blanket
   `form button` rule painted the attach control and the chips' remove crosses
   solid blue the moment those forms grew an attachment row — three primary
   actions on one form, none of them the one that commits it. */
.crm-l-task-formacts button { min-height: 36px; padding: 0 12px; border: 0; border-radius: 7px;
  background: var(--accent); color: #fff; font: 700 12px/1 inherit; cursor: pointer; }
.crm-l-task-formacts button:disabled { opacity: .55; cursor: wait; }
.crm-l-task-complete-form .crm-l-task-secondary { background: transparent; color: var(--text-dim); border: 1px solid var(--border); }
.crm-l-task-comment-form textarea { width: 100%; margin-top: 0; }
.crm-l-task-comment { padding: 8px 0; border-bottom: 1px solid var(--border); }
.crm-l-task-comment:first-child { padding-top: 0; }
.crm-l-task-comment-meta { display: flex; justify-content: space-between; gap: 8px; color: var(--text-dim); font-size: 11px; }
.crm-l-task-comment-meta b { display: inline-flex; align-items: center; gap: 5px; }
.crm-l-task-comment-body { margin-top: 3px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
.crm-l-task-error { margin-top: 8px; color: #b91c1c; }
.crm-l-tl-meta time { flex-shrink: 0; }

/* Tasks summary + drawer. The side column carries one button; the list lives in
   a modal drawer (right-hand panel on a desktop, full-screen sheet on a phone).
   44px minimum on every control — these are thumb targets in the field. */
.crm-l-task-open { display: block; width: 100%; min-height: 44px; padding: 10px 12px; border: 1px solid var(--border);
  border-radius: 9px; background: var(--surface-2); color: var(--text); font: 600 13px/1.3 inherit; text-align: left; cursor: pointer; }
.crm-l-task-open:hover { background: var(--accent-soft); border-color: var(--accent); }
/* Overdue is ALSO said in the label text — colour alone is not a signal. */
.crm-l-task-open.has-overdue { color: #b91c1c; border-color: #f0b4b4; }
/* The lock has to OUTRANK the phone rule that gives the page its natural
   scroll (`body:has(.crm-l-cols){overflow:visible}`, same 0-1-1 and later in
   the file — a plain `body.crm-drawer-open` lost to it and the page went on
   scrolling behind the full-screen sheet, on exactly the platform the sheet is
   for). Two class-level parts make this 0-2-1.
   NOT `position: fixed`: it does hold iOS harder, but it also scrolls the card
   back to the top the moment the sheet opens, and the reader loses their place
   on closing — a worse, more visible defect than the one being fixed. */
body.crm-drawer-open,
body.crm-drawer-open:has(.crm-l-cols) { overflow: hidden; }
.crm-l-drawer-mask { position: fixed; inset: 0; z-index: 60; background: rgba(15, 23, 42, .38); }
.crm-l-drawer { position: fixed; z-index: 61; top: 0; right: 0; bottom: 0; width: min(440px, 100vw);
  display: flex; flex-direction: column; background: var(--surface); border-left: 1px solid var(--border);
  box-shadow: -12px 0 32px rgba(15, 23, 42, .18); padding-bottom: env(safe-area-inset-bottom); }
.crm-l-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 12px 14px; border-bottom: 1px solid var(--border); }
.crm-l-drawer-head h2 { margin: 0; font-size: 15px; }
.crm-l-drawer-close { width: 44px; height: 44px; flex: 0 0 44px; border: 0; border-radius: 9px; background: transparent;
  color: var(--text-dim); font-size: 16px; line-height: 1; cursor: pointer; }
.crm-l-drawer-close:hover { background: var(--accent-soft); color: var(--text); }
.crm-l-drawer-tabs { margin: 10px 14px 0; }
.crm-l-drawer-body { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 4px 14px 14px; }
.crm-l-drawer-body .crm-l-task:first-of-type { border-top: 0; }
.crm-l-drawer-body [data-task-row]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 8px; }
.crm-l-drawer-foot { padding: 10px 14px; border-top: 1px solid var(--border); }
.crm-l-drawer-foot button { width: 100%; min-height: 44px; }

.crm-l-empty { color: var(--text-faint); font-size: 13px; padding: 8px 0; }
/* Empty field rows — every registry field is always shown (amoCRM parity);
   dim the blanks so filled data stands out, clicking the dash opens the editor. */
.crm-l-frow-blank span { color: var(--text-faint); }
.crm-l-frow-blank .crm-l-fval { color: var(--text-faint); font-weight: 500; }

/* ── create-lead form (new.html) + entry button ── */
.crm-newlead { font: inherit; font-size: 13px; font-weight: 700; color: #fff; background: var(--accent);
  text-decoration: none; padding: 7px 14px; border-radius: 8px; box-shadow: var(--shadow); }
.crm-newlead:hover { background: #1d4fd7; }
.crm-settings { font: inherit; font-size: 13px; font-weight: 600; color: var(--text-dim); background: var(--surface);
  border: 1px solid var(--border-2); border-radius: 8px; padding: 6px 12px; text-decoration: none; white-space: nowrap; }
.crm-settings:hover { color: var(--accent); border-color: var(--accent); }
.crm-new { max-width: 640px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px; }
.crm-new .crm-l-panel { display: flex; flex-direction: column; gap: 12px; }
.crm-new-row { display: flex; flex-direction: column; gap: 5px; width: 100%; min-width: 0; }
.crm-new-row label { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.crm-new-row .crm-input, .crm-new-row .crm-l-fin {
  display: block; width: 100%; min-width: 0; max-width: 100%;
}
/* iOS WebKit currently adds date-control padding outside percentage sizing.
   Let flexbox stretch the margin box instead of giving date inputs a 100%
   content width, while retaining the native picker appearance. */
.crm-new-row input.crm-l-fin[type="date"],
.crm-new-row input.crm-l-fin[type="datetime-local"] {
  width: auto; max-width: none; align-self: stretch;
}
.crm-req { color: #dc2626; margin-left: 2px; }
.crm-new-pinbar { display: flex; gap: 8px; flex-wrap: wrap; }
#nl-geo { white-space: nowrap; }
#nl-draftbar { font-size: 12.5px; border-radius: 8px; padding: 8px 12px;
  background: #ecfdf5; color: #047857; border: 1px solid #a7f3d0; }
#nl-draftbar button { margin-left: 6px; }
.crm-new-pinbar .crm-input { flex: 1; }
.crm-new-hint { font-size: 12px; color: var(--text-faint); }
/* Name is auto-generated & read-only — show it as non-editable. */
.crm-input.crm-name-auto { background: var(--surface-3); color: var(--text-dim); cursor: default; }
.crm-input.crm-name-auto:focus { outline: none; border-color: var(--border-2); }
.crm-new-map { height: 300px; border: 1px solid var(--border-2); border-radius: 9px; overflow: hidden; background: var(--surface-3); }

/* ── Find business (Places) search + dropdown ── */
.crm-bizsearch { position: relative; }
.crm-bizmenu {
  position: absolute; left: 0; right: 0; top: calc(100% + 3px); z-index: 30;
  background: var(--surface); border: 1px solid var(--border-2); border-radius: 9px;
  box-shadow: 0 6px 20px rgba(20,24,33,.14); overflow: hidden; max-height: 320px; overflow-y: auto;
}
.crm-bizmenu-item { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid var(--border); }
.crm-bizmenu-item:last-child { border-bottom: 0; }
.crm-bizmenu-item:hover { background: var(--accent-soft); }
.crm-bizmenu-main { font-size: 13.5px; font-weight: 600; color: var(--text); }
.crm-bizmenu-sub { font-size: 12px; color: var(--text-dim); margin-top: 1px; }
.crm-new-coords { font-size: 13px; color: var(--text-dim); font-weight: 500; }
.crm-new-ok { color: #15803d; font-weight: 600; }
.crm-new-bad { color: #b91c1c; font-weight: 600; }
.crm-new-more summary { font-size: 13px; font-weight: 600; color: var(--accent); cursor: pointer; }
.crm-new-more[open] summary { margin-bottom: 10px; }
.crm-new-more > div { display: flex; flex-direction: column; gap: 12px; }
.crm-new-bar { display: flex; align-items: center; gap: 10px; }
.crm-new-bar .crm-l-fsave { font-size: 13.5px; padding: 9px 18px; }
.crm-new-bar .crm-l-fdiscard { text-decoration: none; display: inline-flex; align-items: center; }
.crm-new-err { color: #b91c1c; font-size: 12.5px; }

/* The Fields/History switch is a PHONE affordance: above 640px both columns
   are visible side by side and a switch would be nonsense. */
.crm-l-tabs { display: none; }

@media (max-width: 640px) {
  main { padding: 10px 12px 20px; }
  /* 44px of reach for the group fold without 44px of white space: the heading
     looks the same height and the target grows into the gap above it. */
  .crm-l-fgroup { padding-top: 14px; padding-bottom: 6px; }
  .crm-col { flex-basis: 82vw; width: 82vw; }
  .crm-l-cols { grid-template-columns: 1fr; }

  /* Undo the desktop "fixed viewport, each column scrolls itself" model. It is
     right on a wide screen — fields left, history right, both in view — and
     wrong on a phone, where the columns stack and you get two little scroll
     boxes inside a page that cannot scroll. Here the page scrolls normally. */
  body:has(.crm-l-cols) { height: auto; overflow: visible; display: block; }
  body:has(.crm-l-cols) main,
  body:has(.crm-l-cols) #crm-root,
  body:has(.crm-l-cols) .crm-l-cols { display: block; flex: none; min-height: 0; }
  body:has(.crm-l-cols) .crm-l-side,
  body:has(.crm-l-cols) .crm-l-main { max-height: none; overflow: visible; padding-right: 0; }
  /* …and with it the sticky Timeline heading: there is no scroll box to pin to
     here, and the lead summary below already owns top:0 — a second sticky layer
     would just slide under it and disappear. */
  body:has(.crm-l-cols) .crm-l-tlwrap h2 { position: static; margin: 0 0 12px;
    padding: 0; background: none; border-bottom: 0; border-radius: 0; }

  /* Lead summary + switch stay put while a pane scrolls: on a long timeline
     you otherwise lose track of which lead you are in, and the switch drifts
     off-screen. */
  .crm-header { position: static; }
  /* …and on the lead card the page header stays pinned above it (owner,
     2026-08-21): it carries "← Leads", and letting it scroll away meant the only
     route back to the board was to scroll a long card all the way up again.
     Two sticky layers means the second one has to start where the first one
     ends, and that height is NOT a constant we can write here — the bar wraps
     to two rows on a narrow screen, the safe-area inset adds to it in the
     installed app, and rotation changes both. So it is measured and published
     as --crm-hdr-h by trackHeaderOffset() in lead-view.js; the fallback of 0px
     is the old behaviour (summary at the very top), not a broken layout.
     Scoped to the lead card on purpose: the board's own header is untouched. */
  body:has(.crm-l-cols) .crm-header { position: sticky; top: 0; }
  .crm-l-sticky { position: sticky; top: var(--crm-hdr-h, 0px); z-index: 15;
    background: var(--bg); margin: 0 -12px; padding: 0 12px 2px; }
  .crm-l-sticky .crm-l-head { margin-bottom: 0; }

  .crm-l-tabs { display: flex; margin: 10px 0; }
  .crm-l-tabs .crm-viewtabs { width: 100%; }
  .crm-l-tabs .crm-viewtab { flex: 1; padding: 9px 10px; }
  .crm-l-cols[data-tab="fields"] .crm-l-main,
  .crm-l-cols[data-tab="history"] .crm-l-side { display: none; }
  /* header dropdowns (Refusal / Region / Subregion): anchor to the whole wrapped row, not the control —
     a left-anchored 200px menu from a control at x>230 would clip off-screen
     (the lead page body is overflow:hidden, so clipped options are unreachable) */
  .crm-l-bar { position: relative; }
  .crm-l-bar .crm-l-fmenuwrap { position: static; }
  /* SCOPED to .crm-l-fmenuwrap. Unscoped, this caught the tag menu as well —
     whose wrapper is .crm-l-tagwrap, ~51px wide — so `min-width: 0` plus
     left:0/right:0 squeezed it to the width of the "+ tag" button and every
     option ellipsised to a single letter ("A…", "G…", "O…"). Only on a phone,
     which is the only device a scout has, and only in the menu that IS a
     scout's whole permission to tag (2026-08-18, found by screenshot). */
  .crm-l-bar .crm-l-fmenuwrap .crm-l-fmenu { left: 0; right: 0; min-width: 0; }
  /* The tag menu anchors to the whole tags row instead, for the same reason
     the rule above exists: from a control sitting at x>200 a left-anchored
     180px menu would run off a 390px screen, and the page body is
     overflow:hidden, so what is clipped cannot be reached. */
  .crm-l-bar .crm-l-grp-tags { position: relative; }
  .crm-l-bar .crm-l-tagwrap { position: static; }
  /* Sits in a row of dim 13px text; without a floor it is a 17px-tall target. */
  .crm-l-owner { min-height: 44px; display: inline-flex; align-items: center; }
  /* ⋯ is the only route to Delete lead now, and it draws as 33×23. The thumb
     floor is bought with an invisible overlay rather than with padding: padding
     would grow the title row on every card, and the row's height is the header's
     biggest single cost on a phone. -8px on the left stays inside the 8px gap
     to the name, so it never steals a tap meant for the title. */
  .crm-l-more { position: relative; }
  .crm-l-more::after { content: ''; position: absolute; inset: -11px -8px; }
  /* Name editor: an 84px label column plus a control leaves the control ~120px
     wide on a phone, which is not enough for a region name — stack instead.
     The h1 is the way in, so it needs a thumb-sized target of its own. */
  /* A thumb target that costs no height: the negative margin gives the padding
     back to the layout. min-height:44px used to add ~20px of blank card above
     the fold on every lead, and it pushed the dashed rule away from the name. */
  .crm-l-h1edit { display: inline-block; padding: 7px 0; margin: -7px 0; }
  .crm-l-nprow { flex-direction: column; align-items: stretch; gap: 4px; }
  .crm-l-nprow > span, .crm-l-npline > .crm-l-nplbl { flex: 0 0 auto; }
  .crm-l-npline { flex-direction: column; gap: 0; }
  .crm-l-task-line { flex-wrap: wrap; }
  .crm-l-task-threadline { min-height: 44px; }
  /* The verb is what a thumb aims at now — the circle stopped taking clicks. */
  .crm-l-task-act { min-height: 44px; }
  .crm-l-task-complete-form, .crm-l-task-thread, .crm-l-task-result { margin-left: 0; }
  .crm-l-task-complete-form button { min-height: 44px; }
  .crm-l-task-formacts button { min-height: 44px; }
  /* Full-screen sheet: a 440px panel on a 360px phone is a panel with no page.
     Stretched between both edges rather than sized with 100vw — `right: 0` is
     measured against the viewport INCLUDING the scrollbar while `100vw` is not,
     and the difference showed up as a live strip of the page down the left. */
  .crm-l-drawer { left: 0; width: auto; border-left: 0; }
}

/* ── Mobile PWA polish (T4a) ────────────────────────────────────────── */
/* Safe-area insets only produce non-zero values with viewport-fit=cover (set
   in the page metas, T4a). Desktop reports 0 for every inset — additive-safe.
   Scoped to standalone so in-browser layouts are untouched. */
/* Not scoped to standalone: env() is 0 on desktop anyway, and in-browser
   Safari landscape on notched iPhones needs the side insets too (review). */
.crm-header { padding-top: calc(11px + env(safe-area-inset-top));
  padding-left: calc(18px + env(safe-area-inset-left));
  padding-right: calc(18px + env(safe-area-inset-right)); }
main { padding-bottom: calc(24px + env(safe-area-inset-bottom)); }
/* lead page: body:has(.crm-l-cols) main{padding-bottom:8px} (0-1-2) outranks
   the bare `main` rule — restate with matching specificity or the scout's
   last field row sits under the iPhone home indicator (review finding 1) */
body:has(.crm-l-cols) main { padding-bottom: calc(8px + env(safe-area-inset-bottom)); }
.crm-kanban { padding-left: env(safe-area-inset-left); padding-right: env(safe-area-inset-right); }
/* iOS zooms the page on focusing any input under 16px — keep phone inputs at
   16px (desktop keeps the denser 13.5px). */
@media (max-width: 640px) {
  .crm-input, .crm-l-fin, select.crm-l-fin, textarea.crm-l-fin, .crm-date-in { font-size: 16px; }
}
/* Install (A2HS) card — injected by install-hint.js under the header. */
.crm-install-card { margin: 10px 14px 0; padding: 11px 14px; display: flex; gap: 12px;
  align-items: center; background: var(--surface); border: 1px solid var(--border);
  border-radius: 12px; font-size: 13px; color: var(--text); }
.crm-install-btn { font: inherit; font-weight: 700; font-size: 13px; padding: 8px 14px;
  border: 0; border-radius: 8px; background: var(--accent); color: #fff; cursor: pointer; }
.crm-install-x { margin-left: auto; border: 0; background: none; color: var(--text-faint);
  font-size: 15px; cursor: pointer; padding: 4px 6px; }

/* ── Notification centre (Track D) ────────────────────────────────────────
   Appended to document.body, NOT to the header: .crm-header is sticky with
   z-index 20, which makes it a stacking context, so a panel inside it is
   clamped to layer 20 and the board's own filter dropdown (.crm-md-panel,
   z-index 30) would paint on top of it. 45 sits above the field menus (40)
   and below the choice modal (100) — a modal must still cover this. */
.crm-nc-panel { position: fixed; z-index: 45; width: 340px; max-width: calc(100vw - 16px);
  max-height: min(70vh, 480px); overflow-y: auto; background: var(--surface);
  border: 1px solid var(--border-2); border-radius: 12px;
  box-shadow: 0 8px 26px rgba(20,24,33,.16); padding: 6px; }
.crm-nc-panel[hidden] { display: none; }
/* The unread count. Same pill as the board's filter counter so it reads as
   part of the same system; it lives INSIDE the bell as a sibling of the
   label span, which is why paintBell() writes to that span and not to the
   button's textContent. */
.crm-nc-dot { background: var(--accent); color: #fff; border-radius: 999px; font-size: 11px;
  font-weight: 700; padding: 0 6px; line-height: 17px; margin-left: 6px; }
/* The panel's own header, and with it the ✕. Before it the only ways out were
   an outside click and Escape — and a phone has no Escape key, while the panel
   there is a sheet with barely any "outside" left to aim at (owner, 2026-08-19).
   Sticky, because the list scrolls inside the panel and an exit that scrolls
   away is not one. `top: -6px` and the matching negative margins: the panel
   carries 6px of padding, and content is visible inside that strip — pinned at
   0 the rows would slide past ABOVE the header. */
.crm-nc-head { position: sticky; top: -6px; z-index: 1; display: flex; align-items: center; gap: 8px;
  margin: -6px -6px 2px; padding: 12px 6px 6px 10px; background: var(--surface);
  border-radius: 12px 12px 0 0; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-faint); }
/* A BUTTON, not a bare glyph. As a borderless ✕ it read as decoration on a
   phone — the owner had to be told it was the way out (2026-08-19). It now
   carries the same frame as "Mark all read" beside it, so it looks like the
   control it is. */
.crm-nc-close { margin-left: auto; font: inherit; font-size: 15px; line-height: 1;
  min-width: 34px; min-height: 34px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2);
  color: var(--text-dim); cursor: pointer; }
.crm-nc-close:hover { background: var(--surface-3); color: var(--text); border-color: var(--border-2); }
.crm-nc-close:active { background: var(--border); }
.crm-nc-enable { display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  font-size: 12.5px; color: var(--text-dim); border-bottom: 1px solid var(--border);
  margin-bottom: 4px; }
.crm-nc-enable-btn { margin-left: auto; font: inherit; font-size: 12.5px; font-weight: 600;
  padding: 5px 10px; border: 0; border-radius: 8px; background: var(--accent); color: #fff;
  cursor: pointer; white-space: nowrap; }
.crm-nc-item { display: grid; grid-template-columns: 1fr auto; gap: 2px 8px; padding: 9px 10px;
  border-radius: 8px; text-decoration: none; color: var(--text); }
.crm-nc-item:hover { background: var(--surface-2); }
.crm-nc-item b { font-size: 13.5px; font-weight: 600; }
.crm-nc-item span { grid-column: 1; font-size: 12.5px; color: var(--text-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.crm-nc-item time { grid-row: 1; grid-column: 2; font-size: 11.5px; color: var(--text-faint); }
/* Unread: a left rule rather than a background, so a long list does not turn
   into a wall of colour. Read rows recede instead of vanishing — the panel is
   also the answer to "something buzzed, what was it?", so history stays
   legible. Unread now survives until the row is actually opened, which is the
   whole point of the 2026-08-18 rework; see crm/notify-panel.js. */
.crm-nc-item.is-new { box-shadow: inset 3px 0 0 var(--accent); }
.crm-nc-item:not(.is-new) b { font-weight: 500; color: var(--text-dim); }
.crm-nc-tools { display: flex; align-items: center; gap: 8px; padding: 4px 10px 8px;
  font-size: 12px; font-weight: 600; color: var(--text-faint); }
.crm-nc-mark { margin-left: auto; font: inherit; font-size: 12px; font-weight: 600;
  padding: 4px 8px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text-dim); cursor: pointer; white-space: nowrap; }
.crm-nc-mark:hover { color: var(--accent); border-color: var(--accent); }
/* What a push does to an OPEN panel: this line appears, and nothing else moves.
   Rebuilding the list under the reader's finger cost a swallowed click and a
   destroyed error message; re-fetching it on every push cost a page of reads. */
.crm-nc-more, .crm-nc-older { display: block; width: calc(100% - 4px); margin: 4px 2px 0; font: inherit;
  font-size: 12.5px; font-weight: 600; padding: 7px 10px; border: 1px dashed var(--border-2);
  border-radius: 8px; background: var(--surface-2); color: var(--accent); cursor: pointer; }
.crm-nc-more[hidden], .crm-nc-older[hidden] { display: none; }
.crm-nc-more:hover, .crm-nc-older:hover { border-style: solid; border-color: var(--accent); }
/* The older half of the list. Quieter than "N new — show": that one announces
   something that just arrived, this one is a way further down. */
.crm-nc-older { color: var(--text-faint); }
.crm-nc-older:hover { color: var(--accent); }
/* Something unread is down there. The quiet default means "just history". */
.crm-nc-older.has-unread { color: var(--accent); border-style: solid; border-color: var(--accent); }
.crm-nc-older:disabled { cursor: default; border-style: dashed; border-color: var(--border-2); color: var(--text-faint); }
.crm-nc-empty { padding: 14px 10px; font-size: 12.5px; color: var(--text-faint); }

@media (max-width: 640px) {
  /* The header WRAPS at ~390px and goes position:static on phones, so the
     bell has no stable position to anchor a dropdown to — it would drift or
     clip. A full-width sheet pinned to the viewport has no anchor to get
     wrong, and it is easier to hit with a thumb. */
  .crm-nc-panel { left: 8px; right: 8px; width: auto; top: 8px; max-height: 78vh; }
  /* Thumb-sized, and the word beside the glyph: on the sheet this is the only
     way out that does not involve guessing where "outside" is. */
  .crm-nc-close { min-width: 44px; min-height: 44px; font-size: 17px; padding: 0 12px; gap: 6px; }
  .crm-nc-close::after { content: 'Close'; font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .04em; }
}

/* ── composer polish (owner, 2026-07-26) ──────────────────────────────────
   The recipient and kind selects inherited .crm-l-fin, which is a full-width
   form control — so "To: Ihor" stretched across the whole card and read as a
   text field rather than a short choice. Size them to their content, with a
   floor so a one-letter name is still a comfortable tap target. */
.crm-l-cin { width: auto; min-width: 140px; max-width: 100%; flex: 0 0 auto; }
/* Confirmation that a message/task/note actually went. Travelled with Send down
   to the attach bar; clears itself, role="status" so a screen reader announces
   it. --ok is not defined anywhere, so the literal is what ships; it is named so
   a future token drops in without touching this rule. margin-left:auto is what
   pushes the pair to the right end of that bar. */
.crm-l-attbar .crm-l-csent { margin-left: auto; font-size: 12.5px; font-weight: 600;
  color: var(--ok, #15803d); white-space: nowrap; }
/* Send holds the right end of the attach bar. `margin-left:auto` on whichever
   of the two comes first, so the confirmation appearing does not shove Send off
   the line — the bar wraps, and with the chips already claiming a line of their
   own (width:100%) the pair simply drops together. */
.crm-l-attbar .crm-l-fsave { flex: 0 0 auto; }
.crm-l-attbar .crm-l-csent + .crm-l-fsave { margin-left: 8px; }
.crm-l-attbar > .crm-l-fsave:not(.crm-l-csent + .crm-l-fsave) { margin-left: auto; }

/* ── Photos (T4e) ── tiles are square, pixels arrive as blob URLs after render */
.crm-l-phgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 10px; }
/* Each tile sits in a cell of its own so an admin's ✕ can be laid ON it: a
   <button> cannot contain another button, so the delete control is a sibling
   and the cell is what holds the two together. min-width:0 keeps a long
   document name from widening the grid column it lives in. */
.crm-l-phcell { position: relative; min-width: 0; display: block; }
.crm-l-phcell > .crm-l-ph { width: 100%; }
/* Small, and deliberately so. It is the destructive control on a 110px tile —
   a 44px target here would be a third of the picture and a thumb's width from
   the tap that opens it. Everything it can cost is behind a confirmation that
   names the file, and on a phone it is drawn always (there is no hover to
   reveal it). */
.crm-l-phdel { position: absolute; top: 4px; right: 4px; width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center; padding: 0; z-index: 2;
  border: 0; border-radius: 50%; cursor: pointer; line-height: 1; font-size: 13px;
  color: #fff; background: rgba(20, 24, 33, .6); }
.crm-l-phdel:hover { background: rgba(190, 40, 40, .9); }
.crm-l-phdel:disabled { opacity: .5; pointer-events: none; }
.crm-l-ph { aspect-ratio: 1; border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
  background: var(--surface-3); padding: 0; cursor: pointer; }
.crm-l-ph img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* pixels arrive as a blob URL AFTER render — until then the tile is a quiet
   grey square, not the browser's broken-image glyph with the filename */
.crm-l-ph img:not([src]) { opacity: 0; }
/* A document tile is a CONTROL — a PDF opens in the app, Word and Excel come
   down to the device — and it shows its FIRST PAGE behind the label: a lease
   and a scan of a lease are the same word and very different files, and the
   page settles it at a glance. The name and the icon sit ON the preview, so a
   tile is complete before (or without) the image: Drive may still be baking it,
   and some files it never renders at all. */
.crm-l-ph-file { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; font-size: 11.5px; color: var(--text-dim); cursor: pointer;
  padding: 6px; text-align: center; overflow: hidden; }
.crm-l-ph-file:hover { color: var(--text); background: var(--surface-2); }
/* The page itself: top-aligned, because the top of a document is the part that
   identifies it. Empty until the loader sets src — an <img> with no src draws
   nothing, so there is no broken-image icon to hide. */
.crm-l-phpreview { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: top center; }
.crm-l-phpreview:not([src]) { display: none; }
/* Above the page, and legible over it — white paper under grey text is not. */
.crm-l-phdoc, .crm-l-phname, .crm-l-phsize { position: relative; z-index: 1; }
.crm-l-phdoc { font-size: 20px; line-height: 1;
  text-shadow: 0 1px 3px rgba(255,255,255,.9), 0 0 6px rgba(255,255,255,.9); }
.crm-l-phname { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  background: rgba(255,255,255,.88); border-radius: 4px; padding: 1px 4px; }
.crm-l-phsize { font-size: 10.5px; opacity: .75; font-variant-numeric: tabular-nums;
  background: rgba(255,255,255,.88); border-radius: 4px; padding: 0 4px; }
/* A video tile is the photo tile plus a badge — Drive bakes a real poster
   frame for video and serves it through the same render path (verified on
   live Drive before this shipped), so the picture is the picture, and the
   badge is what says it moves. */
.crm-l-ph-video { position: relative; }
.crm-l-phplay { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 26px; color: #fff; text-shadow: 0 1px 6px rgba(0, 0, 0, .55); pointer-events: none; }
/* THE CHROME STAYS ON TOP. The first cut lifted the player above it so the
   ⬇ circle would not sit on the native scrub bar — and that put the whole
   video over the ✕, which on a phone means a viewer you cannot close (found
   by the owner in one minute of real use; no DOM test sees a z-index).
   The right way round: chrome above, and while a video plays the download bar
   moves OFF the bottom edge where the native controls live — to the LEFT of
   the ✕, not under it. `background:#000` so a letterbox does not show the
   mask through it. */
.crm-ph-video { background: #000; }
.crm-ph-mask.crm-ph-playing .crm-ph-bar {
  bottom: auto; top: calc(env(safe-area-inset-top, 0px) + 12px);
  left: 0; right: 68px;                 /* clear of the ✕ in the corner */
  justify-content: flex-end;
}
.crm-l-phbtns { display: flex; gap: 8px; flex-wrap: wrap; }
.crm-l-phbtn { font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent); background: none;
  border: 1px dashed var(--border-2); border-radius: 7px; padding: 6px 10px; cursor: pointer; }
.crm-l-phbtn:hover { border-color: var(--accent); background: var(--accent-soft); }
.crm-l-phup { font-size: 12.5px; color: var(--text-dim); padding: 3px 0; }
.crm-l-phup.err { color: #b91c1c; }
.crm-l-phretry { font: inherit; font-size: 12px; font-weight: 600; color: var(--accent); background: none;
  border: none; cursor: pointer; text-decoration: underline; }
/* ── Attachments on messages ──
   A chip in the feed, not a tile: it sits inside a line of conversation, so it
   is sized by its text and wraps like text. The doors behind it are the Files
   panel's exact three — viewer for a photo or video, download for a document —
   because the same file must not behave differently in two places. */
.crm-l-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.crm-l-att { display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  font: inherit; font-size: 12px; color: var(--text-dim); text-align: left;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  padding: 4px 8px 4px 4px; cursor: pointer; }
.crm-l-att:hover { color: var(--text); border-color: var(--accent); }
.crm-l-attthumb { width: 34px; height: 34px; flex: 0 0 34px; border-radius: 5px;
  object-fit: cover; display: block; background: var(--surface-3); }
/* No src yet (still baking, or a format Drive never renders) → the chip is its
   name and its icon, which is already enough to act on. An empty box would read
   as a picture that failed. */
.crm-l-attthumb:not([src]) { display: none; }
/* Beside the poster, NOT laid over it. An overlaid badge has to be positioned
   against the thumbnail, and the thumbnail is exactly the thing that may not be
   there — Drive bakes a poster in its own time — so the badge would land on the
   filename instead. */
.crm-l-attplay { font-size: 12px; line-height: 1; pointer-events: none; }
.crm-l-attdoc { font-size: 16px; line-height: 1; }
.crm-l-attname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 190px; }
.crm-l-attsize { font-size: 10.5px; opacity: .75; font-variant-numeric: tabular-nums; }
/* A file that is gone, or named from outside this lead, is SAID — never
   silently skipped, which would read as "there was no attachment". */
.crm-l-att-gone { cursor: default; opacity: .7; font-style: italic; }
.crm-l-att-gone:hover { color: var(--text-dim); border-color: var(--border); }

/* ── Attach control in the composer ── */
.crm-l-attbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px; }
.crm-l-attbtn { font: inherit; font-size: 12.5px; font-weight: 600; color: var(--accent); background: none;
  border: 1px dashed var(--border-2); border-radius: 7px; padding: 6px 10px; cursor: pointer; }
.crm-l-attbtn:hover { border-color: var(--accent); background: var(--accent-soft); }
.crm-l-attchips { display: flex; flex-wrap: wrap; gap: 6px; width: 100%; }
.crm-l-attchip { display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  font-size: 12px; color: var(--text-dim);
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 3px 4px 3px 8px; }
.crm-l-attchip.err { color: #b91c1c; border-color: #fca5a5; }
.crm-l-attchip-n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 170px; }
.crm-l-attchip-s { font-variant-numeric: tabular-nums; opacity: .85; }
/* 26px, not the 44px of a viewer control: this ✕ sits inside a line of chips
   and a finger-sized one would push the composer around. It is also the least
   destructive button on the card — it drops the pointer, never the file. */
.crm-l-attchip-x { font: inherit; font-size: 13px; line-height: 1; color: var(--text-dim);
  background: none; border: none; border-radius: 5px; padding: 4px 6px; cursor: pointer; }
.crm-l-attchip-x:hover { color: var(--text); background: var(--surface-3); }
.crm-l-attretry { font: inherit; font-size: 12px; font-weight: 600; color: var(--accent); background: none;
  border: none; cursor: pointer; text-decoration: underline; }

/* ── Drag a file onto the card ── */
/* pointer-events:none is load-bearing, not tidiness: an overlay that took the
   pointer would swallow the very dragover events it exists to announce, and the
   zone would flicker on and off under the cursor. z-index above the drawer (61)
   and the choice mask (100) so it is never half-covered by what it sits over. */
.crm-l-dropmask { position: fixed; inset: 0; z-index: 110; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: rgba(15, 23, 42, .45); }
/* The panel carries its OWN dark ground rather than borrowing the tint. On a
   light card the white-on-tint version was legible in a screenshot and not much
   else — and this label appears for a second, over whatever the page happens to
   be showing, which is the worst case for a colour that depends on it. */
.crm-l-dropbox { display: flex; flex-direction: column; align-items: center; gap: 6px;
  font-size: 17px; font-weight: 600; color: #fff; text-align: center;
  background: rgba(15, 23, 42, .94); box-shadow: 0 10px 30px rgba(0, 0, 0, .25);
  border: 2px dashed rgba(255, 255, 255, .55); border-radius: 14px; padding: 22px 30px; }
.crm-l-dropbox span { font-size: 12.5px; font-weight: 500; opacity: .85; }

/* full-size overlay (reuses .crm-choice-mask backdrop) */
.crm-ph-mask { display: flex; align-items: center; justify-content: center; }
/* EVERYTHING SITS ABOVE THE PICTURE. The blurred stand-in carries
   `transform: scale()`, which makes it a stacking context and paints it like a
   positioned element — and being later in the DOM it won the tie, covering the
   close button, both arrows and the loading notice for the whole time a photo
   was loading. A DOM test cannot see this; a screenshot found it in one look. */
.crm-ph-x, .crm-ph-nav, .crm-ph-bar, .crm-ph-note { z-index: 2; }
/* Viewer controls. All 44px — Apple's minimum target — and offset past the
   safe-area insets so the notch, the Dynamic Island and the home indicator
   cannot sit on top of them. */
.crm-ph-x, .crm-ph-nav, .crm-ph-dl, .crm-ph-del { border: 0; cursor: pointer; color: #fff;
  background: rgba(20, 24, 33, .55); display: flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%; line-height: 1; text-decoration: none; }
.crm-ph-x:hover, .crm-ph-nav:hover, .crm-ph-dl:hover { background: rgba(20, 24, 33, .8); }
.crm-ph-x { position: absolute; top: calc(env(safe-area-inset-top, 0px) + 12px); right: 12px; font-size: 20px; }
.crm-ph-nav { position: absolute; top: 50%; transform: translateY(-50%); font-size: 26px; }
.crm-ph-prev { left: 10px; }
.crm-ph-next { right: 10px; }
.crm-ph-nav[hidden] { display: none; }
/* Counter + download, bottom centre, clear of the home indicator. */
.crm-ph-bar { position: absolute; left: 0; right: 0; bottom: calc(env(safe-area-inset-bottom, 0px) + 14px);
  display: flex; align-items: center; justify-content: center; gap: 12px; }
/* The document viewer puts a FILENAME here, in an element the photo viewer
   sized for "3 of 12". Measured at 375px: an unbroken 65-character name pushed
   the ⬇ button 50px past the right bezel and shrank it from 44 to 30px — the
   one control the design leans on when a phone will not render the frame. So
   the label yields and the button never does. */
.crm-ph-count { color: #fff; font-size: 13px; font-variant-numeric: tabular-nums;
  background: rgba(20, 24, 33, .55); border-radius: 999px; padding: 6px 12px;
  min-width: 0; max-width: calc(100vw - 96px); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.crm-ph-count:empty { display: none; }
.crm-ph-dl { font-size: 18px; flex: 0 0 auto; }
.crm-ph-dl:disabled { opacity: .4; pointer-events: none; }
/* The bin lives in the same bar as ⬇ and borrows its shape (see the
   .crm-ph-x/.crm-ph-nav/.crm-ph-dl rule above, which it joins). Full
   44px here, unlike the tile's ✕: this is the deliberate place to delete
   — the photo is on screen at full size and there is nothing to mis-tap. */
.crm-ph-del { font-size: 17px; flex: 0 0 auto; }
.crm-ph-del:hover { background: rgba(190, 40, 40, .9); }
.crm-ph-del:disabled { opacity: .4; pointer-events: none; }
/* The instant stand-in: the grid's small render, upscaled. Slightly soft on
   purpose — it reads as "loading", not as a bad photo — and it is replaced the
   moment the full one arrives. */
.crm-ph-soft { filter: blur(6px); transform: scale(1.02); }
/* Tile marker when Drive has no preview for the format — the photo still opens. */
.crm-l-phna { position: absolute; inset: auto 0 0 0; background: rgba(20, 24, 33, .6); color: #fff;
  font-size: 10px; text-align: center; padding: 2px 0; }
.crm-l-ph { position: relative; }
.crm-ph-full { max-width: 94vw; max-height: 92vh; border-radius: 8px; }
/* A document needs the page, not the picture's fit: white behind it so a PDF
   with transparent margins does not read as a hole in the dark backdrop. */
.crm-ph-pdf { width: 94vw; height: 88vh; border: none; border-radius: 8px; background: #fff; }
/* Anything the viewer has to SAY sits over a photograph, so it carries its own
   backdrop — white-on-unknown is legible only by luck. Centred, because that is
   where the eye already is. */
.crm-ph-note { color: #fff; font-size: 14px; position: absolute;
  left: 50%; top: 50%; transform: translate(-50%, -50%);
  max-width: 80vw; text-align: center; line-height: 1.35;
  background: rgba(20, 24, 33, .72); border-radius: 12px; padding: 10px 14px; }
/* The "still preparing" notice carries a Retry control: waiting must have a
   button, or the only way to ask again is close-and-reopen. Inline with the
   words, big enough for a thumb. */
.crm-ph-again { margin-left: 8px; padding: 6px 14px; border: 0; border-radius: 8px;
  background: #3b82f6; color: #fff; font-size: 14px; font-weight: 600; cursor: pointer; }
.crm-ph-again:active { background: #2563eb; }
/* The loading state pulses. A blurred stand-in on its own reads as a BAD
   PHOTO, not as a photo on its way — the words say which, and the movement
   says the app is still working rather than stuck. */
.crm-ph-load { animation: crm-ph-pulse 1.3s ease-in-out infinite; }
@keyframes crm-ph-pulse { 0%, 100% { opacity: .6; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .crm-ph-load { animation: none; } }

/* ── /crm/tasks.html — задачи по всем лидам ──
   Строки, не карточки. Карточка тратит ~120px на задачу (тень, радиус, внешний
   отступ), и на телефон влезает пять штук; строка с разделителем — ~60px и
   одиннадцать. Просрочка обозначается ЛЕВОЙ ГРАНИЦЕЙ и токеном возраста, а не
   заливкой: когда красное всё, красное не значит ничего. */
.crm-nav { display: flex; gap: 4px; margin-left: 8px; }
.crm-nav a { font-size: 13px; font-weight: 600; color: var(--text-dim); text-decoration: none;
  padding: 6px 12px; border-radius: 8px; min-height: 44px; display: inline-flex; align-items: center; }
.crm-nav a:hover { background: var(--surface-2); color: var(--text); }
.crm-nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); }

.crm-t-bar { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 10px; }
.crm-t-search { flex: 1; min-width: 200px; }
.crm-t-chips { display: flex; gap: 6px; margin-bottom: 12px; overflow-x: auto; padding-bottom: 2px;
  scrollbar-width: thin; }
.crm-t-chip { flex: 0 0 auto; min-height: 34px; padding: 0 11px; border: 1px solid var(--border-2);
  border-radius: 999px; background: var(--surface); color: var(--text-dim); font: 600 12.5px/1 inherit;
  cursor: pointer; display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.crm-t-chip:hover { border-color: var(--accent); color: var(--text); }
.crm-t-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
.crm-t-chip-n { font-weight: 700; opacity: .75; }
.crm-t-note { margin-bottom: 10px; color: var(--text-dim); font-size: 12px; }

.crm-t-group { margin-bottom: 16px; }
.crm-t-group h2 { position: sticky; top: 0; z-index: 1; margin: 0 0 6px; padding: 6px 0;
  background: var(--bg); font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text-dim); }
.crm-t-row { display: flex; align-items: center; gap: 10px; min-height: 56px; padding: 8px 12px;
  border-bottom: 1px solid var(--border); border-left: 3px solid transparent; background: var(--surface);
  text-decoration: none; color: inherit; }
.crm-t-row:first-of-type { border-top-left-radius: 10px; border-top-right-radius: 10px; }
.crm-t-row:last-of-type { border-bottom: 0; border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; }
.crm-t-row:hover { background: var(--surface-2); }
.crm-t-row.late { border-left-color: #b91c1c; }
.crm-t-copy { flex: 1; min-width: 0; }
/* TWO lines, not one. 43% of open tasks are longer than a single line at 375px,
   and the longest are checklists — clipping them at one line hid the actual
   instruction and left search matching text nobody could see. Two lines cost
   ~14px and keep the row tappable; the full text is in the row's tooltip. */
.crm-t-txt { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  font-weight: 650; font-size: 14.5px; line-height: 1.3; color: var(--text); overflow: hidden; }
/* Вторичный текст — #4b5563, а не серый-400: на солнце светло-серое исчезает. */
.crm-t-meta { display: block; margin-top: 2px; font-size: 12px; color: #4b5563; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.crm-t-age { flex: 0 0 auto; min-width: 42px; text-align: right; font-size: 12px; font-weight: 700;
  color: var(--text-dim); font-variant-numeric: tabular-nums; }
.crm-t-row.late .crm-t-age { color: #b91c1c; }

/* ── Location panel (pin + Google Maps link) ───────────────────────────────
   The map element is re-parented across renders by lead.html, so its box must
   keep a stable size here — a zero-height container makes Google Maps render
   a grey square that never recovers. */
.crm-l-loc-map { aspect-ratio: 1 / 1; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg-soft); overflow: hidden; }
.crm-l-loc-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 8px; }
.crm-l-loc-xy { font-size: 13px; color: var(--text-dim); }
.crm-l-loc-none { color: var(--text-dim); font-style: italic; }
.crm-l-loc-btn { font: inherit; font-size: 13px; padding: 5px 10px; cursor: pointer;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 6px; }
.crm-l-loc-btn:hover { border-color: var(--accent); color: var(--accent); }
.crm-l-loc-linkwrap { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
.crm-l-loc-label { font-size: 12px; color: var(--text-dim); margin-bottom: 6px; }
.crm-l-feedmore { display: flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.crm-l-feederr { font-size: 12px; color: #b91c1c; }
.crm-l-loc-find { position: relative; margin-bottom: 8px; }
.crm-l-loc-find .crm-bizmenu { left: 0; right: 0; }
.crm-l-loc-linkrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.crm-l-loc-link { flex: 1 1 200px; min-width: 0; }
.crm-l-loc-msg:not(:empty) { margin-top: 8px; font-size: 13px; padding: 8px 10px; border-radius: 6px;
  background: #fef3c7; color: #92400e; }
.crm-l-loc-foot { display: flex; gap: 8px; margin-top: 12px; }
.crm-l-loc-ok { background: var(--accent); border-color: var(--accent); color: #fff; font-weight: 600; }
.crm-l-loc-ok:hover { filter: brightness(1.06); color: #fff; }
/* The editor is a distinct state, not a subtle one: a person must be able to
   tell at a glance whether the map under their finger is live. */
.crm-l-loc-editing { box-shadow: inset 0 0 0 2px var(--accent); }

/* "Delete lead" in the header — admin-only. Quiet until pointed at: it sits in
   the same row as the stage and the owner, and must never be the loudest thing
   there. */
.crm-l-del { border: 1px solid #e6c9c6; background: #fff; color: #b3261e; border-radius: 7px;
  padding: 3px 9px; font-size: 12px; font-weight: 600; cursor: pointer; margin-left: auto; }
.crm-l-del:hover { background: #b3261e; border-color: #b3261e; color: #fff; }
.crm-l-del[disabled] { opacity: .55; cursor: default; }
