/* ═══════════════════════════════════════════════════════════════════════
   TASKS MODULE
   Site-scoped task management. List + detail + modals. Mobile-first.
   ═══════════════════════════════════════════════════════════════════════ */

.tasks-page {
  padding: 0 0 96px;
}

/* ─── Split-pane layout (list + inline detail) ─── */
.tasks-split {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  min-height: calc(100vh - 100px);
  margin-top: 18px;
}
/* Inside the split, neutralise the list-wrap's own top margin so both
   panes share the same top edge (aligned with the first card). */
.tasks-split .tasks-list-wrap { margin-top: 0; }
.tasks-list-pane {
  flex: 0 0 360px;
  min-width: 0;
  position: relative;
  padding-bottom: 96px;
}
.tasks-detail-pane {
  flex: 1 1 auto;
  min-width: 0;
  align-self: stretch;
  position: sticky;
  top: 0;
  max-height: calc(100vh - 94px);
  overflow-y: auto;
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
  border: 1px solid var(--border);
  scroll-behavior: smooth;
}
/* Inner padding for detail content (overrides .tasks-detail bare layout) */
.tasks-detail-pane .tasks-detail {
  padding: 24px 28px 28px;
  gap: 18px;
}
/* Inside the side pane, info grid is 2-col (narrower than full-screen modal) */
.tasks-detail-pane .tasks-detail-info-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
/* Tighter info tiles for the side pane — smaller icon, smaller label */
.tasks-detail-pane .tasks-detail-info-tile {
  padding: 9px 12px;
  gap: 8px;
}
.tasks-detail-pane .tasks-detail-info-tile-icon {
  width: 26px; height: 26px;
}
.tasks-detail-pane .tasks-detail-info-tile-label {
  font-size: 10px;
  letter-spacing: 0.06em;
}
.tasks-detail-pane .tasks-detail-info-tile-value {
  font-size: var(--text-sm);
}
/* Title row in the pane — tighter, cleaner */
.tasks-detail-pane .tasks-detail-title {
  font-size: var(--text-2xl);
  line-height: 1.25;
}
.tasks-detail-pane .tasks-icon-btn {
  width: 28px; height: 28px;
}
.tasks-detail-pane .tasks-icon-btn svg { width: 14px; height: 14px; }
/* Mark complete button — less vertically heavy */
.tasks-detail-pane .tasks-detail-complete-btn {
  padding: 12px 16px;
  font-size: var(--text-sm);
}
/* Narrower screens (laptops): make list a touch smaller, keep detail dominant */
@media (max-width: 1100px) and (min-width: 641px) {
  .tasks-list-pane { flex-basis: 320px; }
  .tasks-detail-pane .tasks-detail { padding: 20px; }
}
/* Empty state inside detail pane */
.tasks-detail-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 60px 24px;
  color: var(--text-muted);
  text-align: center;
}
.tasks-detail-empty-state svg {
  width: 36px; height: 36px;
  opacity: 0.35;
  stroke: var(--text-muted);
}
.tasks-detail-empty-state p {
  font-size: var(--text-sm);
  margin: 0;
}
/* Selected card highlight */
.tasks-card.is-selected {
  border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface));
}
.tasks-card.is-selected .tasks-card-priority-bar {
  opacity: 1;
}

.tasks-page-header,
.tasks-toolbar,
.tasks-list-wrap {
  max-width: 960px;
}
.tasks-page-header { padding-top: 8px; }
.tasks-header-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: 16px;
}
.tasks-header-row h1 { margin-bottom: 4px; }
.tasks-header-row p {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-md);
}
@media (max-width: 1023px) {
  .tasks-header-row p { display: none; }
  .tasks-header-row h1 { margin-bottom: 0; }
}

/* "New task" button — used in header and empty state */
.tasks-new-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: var(--accent);
  color: var(--accent-text, #fff);
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--text-md);
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.12s, transform 0.05s;
  touch-action: manipulation;
  white-space: nowrap;
  flex-shrink: 0;
}
.tasks-new-btn svg { width: 18px; height: 18px; }
.tasks-new-btn:hover  { filter: brightness(1.1); }
.tasks-new-btn:active { transform: scale(0.97); }

/* Toolbar (search + filters) */
.tasks-toolbar {
  margin-top: 22px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.tasks-search {
  position: relative;
  display: flex;
  align-items: center;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0 12px;
  transition: border-color 0.12s;
}
.tasks-search:focus-within { border-color: var(--accent); }
.tasks-search > svg {
  width: 18px; height: 18px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-search input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  padding: 12px 8px;
  font-size: var(--text-lg);
  color: var(--text-primary);
  min-width: 0;
}
.tasks-search input::placeholder { color: var(--text-muted); }
.tasks-search-clear {
  background: transparent;
  border: none;
  width: 28px; height: 28px;
  display: grid; place-items: center;
  color: var(--text-muted);
  cursor: pointer;
  border-radius: var(--radius-md);
  flex-shrink: 0;
}
.tasks-search-clear:hover { background: var(--bg-surface); color: var(--text-primary); }
.tasks-search-clear svg { width: 16px; height: 16px; }

.tasks-filter-row {
  display: flex;
  flex-wrap: nowrap;
  gap: 8px;
  align-items: center;
  justify-content: flex-start;
}
/* Status dropdown — identical shape to priority dropdown; menu opens left-aligned */
.tasks-status-dropdown {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
}
.tasks-status-dropdown .tasks-priority-trigger { min-width: 120px; }
.tasks-status-dropdown .tasks-priority-menu    { left: 0; right: auto; }
.tasks-priority-select {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-base);
  color: var(--text-muted);
}
.tasks-priority-select select {
  padding: 7px 28px 7px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--text-base);
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 8px center;
  background-size: 14px;
}

/* ─── Custom priority dropdown ───
   Replaces the native <select> so the open menu can be styled to match
   the app (native popups are OS-controlled and look out of place). */
.tasks-priority-dropdown {
  position: relative;
  display: inline-flex;
  flex-shrink: 0;
}
.tasks-priority-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px 6px 12px;
  min-width: 92px; /* fits "Medium" + chevron without resizing on change */
  height: 32px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--body);
  font-size: var(--text-base);
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.12s, background 0.12s;
}
.tasks-priority-trigger:hover { border-color: var(--border-strong); }
.tasks-priority-trigger[aria-expanded="true"] {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.tasks-priority-trigger-label {
  flex: 1;
  text-align: left;
  white-space: nowrap;
}
.tasks-priority-trigger-chev {
  width: 14px; height: 14px;
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-priority-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  min-width: 160px;
  padding: 4px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.tasks-priority-menu[hidden] { display: none !important; }
/* Flip the menu UPWARD when opened from a low field (JS toggles --up) so it uses
   the space ABOVE the trigger instead of being cramped against / hidden by the
   on-screen keyboard. The search stays at the menu's top either way. */
.tasks-priority-menu.tasks-priority-menu--up { top: auto; bottom: calc(100% + 4px); }
/* Keep the search pinned at the menu's top while the options scroll under it. */
.tasks-priority-menu .acat-search { position: sticky; top: 0; z-index: 2; background: var(--bg-surface); }
.tasks-priority-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  background: transparent;
  border: 0;
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font-family: var(--body);
  font-size: var(--text-base);
  font-weight: 500;
  text-align: left;
  cursor: pointer;
  white-space: nowrap;
}
.tasks-priority-option:hover { background: var(--bg-hover); }
.tasks-priority-option.is-active {
  background: var(--accent-soft);
  color: var(--accent);
}
.tasks-priority-option-dot {
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--text-muted);
  flex-shrink: 0;
}
.tasks-priority-option-dot--all { background: var(--text-muted); opacity: 0.5; }
.tasks-priority-option-dot[data-pri="urgent"] { background: var(--danger); }
.tasks-priority-option-dot[data-pri="high"]   { background: var(--warning); }
.tasks-priority-option-dot[data-pri="medium"] { background: var(--accent); }
.tasks-priority-option-dot[data-pri="low"]    { background: var(--success); }

/* List + cards */
.tasks-list-wrap { margin-top: 18px; }
.tasks-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* ─── Completed: monthly groups ─── */
.tasks-month-group {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.tasks-month-group + .tasks-month-group { margin-top: 6px; }
.tasks-month-header {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
  touch-action: manipulation;
  color: var(--text-primary);
}
.tasks-month-header:hover { border-color: var(--accent); background: var(--bg-surface); }
.tasks-month-chevron {
  width: 16px; height: 16px;
  color: var(--text-muted);
  flex-shrink: 0;
  transition: transform 0.18s;
  transform: rotate(-90deg);
}
.tasks-month-chevron.is-open { transform: rotate(0deg); }
.tasks-month-label {
  flex: 1;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
}
.tasks-month-count {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--text-muted);
  background: var(--bg-surface);
  padding: 2px 8px;
  border-radius: var(--radius-md);
}
.tasks-card {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0;
  cursor: pointer;
  transition: border-color 0.12s, transform 0.05s, box-shadow 0.12s;
  overflow: hidden;
  text-align: left;
  position: relative;
  /* Long-list / mobile perf: let the browser skip layout + paint for cards
     that are scrolled off-screen. Big win on the Completed view, which can
     hold hundreds of cards over time. The `auto` keyword makes the browser
     remember each card's REAL height after it's painted once, so the
     scrollbar stays accurate and there's no scroll jump. Gracefully ignored
     on iOS Safari < 18 — pure progressive enhancement, no behaviour change. */
  content-visibility: auto;
  contain-intrinsic-size: auto 104px;
}
.tasks-card:hover {
  border-color: var(--accent);
  box-shadow: 0 2px 10px rgba(0,0,0,0.07);
}
[data-theme="dark"] .tasks-card:hover { box-shadow: 0 1px 8px rgba(0,0,0,0.4); }
.tasks-card:active { transform: scale(0.997); }
.tasks-card.is-dim { opacity: 0.65; }

/* ── 1-tap complete — the tick circle in the card's bottom-right corner ──
   Always visible (hover-reveal breaks iOS double-tap — the pin's lesson).
   The tick takes the pin star's old corner spot and the star shifts left of
   it, so the two controls form one calm bottom-right cluster and the top
   strip (dept chip) keeps the full card width (Radek 2026-07-03). Geometry
   follows the KB step circle; the done state uses the canonical
   --rag-pass-fill green. Only the bottom rows need clearance — the last
   content row (chips / reminder) shares the controls' vertical band. */
.tasks-card.has-tick .tasks-card-chip-row,
.tasks-card.has-tick .tasks-card-meta {
  padding-right: 78px;
}
/* The pin star moves left of the tick on cards that have one; cards without
   a tick (viewer role / cancelled) keep the star in its usual corner. */
.tasks-card.has-tick .tasks-card-pin { right: 48px; }
.tasks-card-tick {
  position: absolute;
  bottom: 9px;
  right: 9px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  background: var(--bg-surface);
  border: 2px solid var(--border-strong);
  border-radius: var(--radius-full);
  color: transparent;
  cursor: pointer;
  padding: 0;
  z-index: 2;
  transition: border-color 0.12s, background 0.15s, transform 0.12s;
}
.tasks-card-tick svg {
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.15s, transform 0.15s;
}
/* Hover preview gated to real pointers — sticky :hover on touch would leave
   ghost green ticks after a scroll (the .cl-tick-pass-btn lesson). */
@media (hover: hover) {
  .tasks-card-tick:hover {
    border-color: var(--rag-pass-fill);
    background: color-mix(in srgb, var(--rag-pass-fill) 10%, transparent);
  }
  .tasks-card-tick:hover svg {
    opacity: 0.6;
    transform: scale(1);
    color: var(--rag-pass-fill);
  }
}
.tasks-card-tick:active { transform: scale(0.92); }
.tasks-card-tick.is-done {
  background: var(--rag-pass-fill);
  border-color: var(--rag-pass-fill);
  color: #fff;
}
.tasks-card-tick.is-done svg { opacity: 1; transform: scale(1); }

/* Urgent is near-black in light mode — flip to near-white in dark mode */
[data-theme="dark"] [style*="color:#18181b"],
[data-theme="dark"] [style*="color: #18181b"] { color: #e4e4e7 !important; }
[data-theme="dark"] [style*="background:#18181b"],
[data-theme="dark"] [style*="background: #18181b"] { background: #e4e4e7 !important; }
.tasks-card-priority-bar {
  width: 4px;
  flex-shrink: 0;
  align-self: stretch;
}
.tasks-card-main {
  flex: 1;
  min-width: 0;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.tasks-card-top {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
/* Department chip pinned to the top-right (the KB card pattern). The pin/star
   lives bottom-right, so the top row needs no clearance. The location chip
   styling is now shared — see .card-loc-chip in css/04-dashboard-shared.css. */
.tasks-card-top .tasks-card-dept { margin-left: auto; flex-shrink: 0; }

/* Location chip is now neutral app-wide — see .card-loc-chip in
   css/04-dashboard-shared.css (the two-tier card-chip rule). */
.tasks-card-ref {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  padding: 3px 7px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
/* CALM card chips (2026-06-29) — status + priority on the card now read neutral
   with the colour kept on the dot, matching the detail. The card's coloured left
   bar (.tasks-card-priority-bar) keeps the at-a-glance scan. */
.tasks-card-status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 4px 9px;
  border-radius: var(--radius-sm);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  color: var(--text-primary);
  letter-spacing: 0.01em;
}
.tasks-card-status-dot {
  width: 6px; height: 6px; border-radius: var(--radius-full);
  flex-shrink: 0;
}
.tasks-card-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.35;
  word-wrap: break-word;
}
.tasks-card-desc {
  font-size: var(--text-base);
  color: var(--text-muted);
  line-height: 1.45;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tasks-card-meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: var(--text-sm);
  color: var(--text-muted);
  margin-top: 2px;
}

/* Chip row — location + priority, side by side at the bottom of the
   card. Both styled as bordered chips so they read as identification
   metadata (the "what kind / where" of the task) rather than blending
   into the muted footnote row underneath. Wraps on narrow viewports. */
.tasks-card-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-self: flex-start;
  max-width: 100%;
}

/* Location chip styling moved to .card-loc-chip in css/04-dashboard-shared.css
   (shared by Tasks / Call-outs / Assets). .tasks-card-location is kept there as
   a co-selector, so this module + the Projects plan tree keep using it. */

/* Priority chip — mirrors the location chip's shape (padding, border,
   radius, font) but uses the priority's own colour palette passed via
   inline CSS vars (--pri-color, --pri-tint, --pri-border). priorityMeta
   handles the dark-mode swap for Urgent, so the chip stays legible on
   both themes. */
.tasks-card-priority-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 9px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-primary);
  font: 500 var(--text-xs) / 1 var(--body);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.tasks-card-priority-chip-dot {
  width: 6px;
  height: 6px;
  border-radius: var(--radius-full);
  background: var(--pri-color);
  flex-shrink: 0;
}

/* Created-at timestamp inline with the chip row — muted text floated
   to the far right. On narrow viewports it wraps to its own line still
   anchored right, so it never crowds long location names. */
.tasks-card-time-inline {
  margin-left: auto;
  align-self: center;
  font: 500 var(--text-xs) / 1 var(--body);
  color: var(--text-muted);
  white-space: nowrap;
  letter-spacing: 0.01em;
}
.tasks-card-priority {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-weight: 600;
  font-size: var(--text-sm);
}
.tasks-card-priority-dot {
  width: 7px; height: 7px;
  border-radius: var(--radius-full);
  display: inline-block;
}
.tasks-card-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.tasks-card-meta-item svg {
  width: 12px; height: 12px;
}
.tasks-card-cost {
  font-weight: 600;
  color: var(--text-secondary);
}
.tasks-card-reminder {
  font-weight: 600;
  color: var(--text-secondary);
}
.tasks-card-reminder.is-overdue {
  color: rgb(220, 38, 38);
}
.tasks-card-arrow {
  display: grid;
  place-items: center;
  width: 36px;
  flex-shrink: 0;
  color: var(--text-muted);
}
.tasks-card-arrow svg { width: 16px; height: 16px; }

/* Empty + skeleton + error states */
.tasks-empty {
  text-align: center;
  padding: 56px 20px;
  border: 1px dashed var(--border);
  border-radius: var(--radius-lg);
  background: var(--bg-surface);
}
.tasks-empty-icon {
  width: 56px; height: 56px;
  margin: 0 auto 14px;
  display: grid; place-items: center;
  border-radius: var(--radius-lg);
  background: var(--bg-elevated);
  color: var(--text-muted);
}
.tasks-empty-icon svg { width: 28px; height: 28px; }
.tasks-empty-title {
  font-size: var(--text-2xl);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 4px;
}
.tasks-empty-sub {
  font-size: var(--text-base);
  color: var(--text-muted);
  max-width: 360px;
  margin: 0 auto;
  line-height: 1.5;
}
.tasks-skeleton { display: flex; flex-direction: column; gap: 8px; }
.tasks-skeleton-card {
  height: 88px;
  border-radius: var(--radius-md);
  background: linear-gradient(90deg, var(--bg-elevated), var(--bg-surface), var(--bg-elevated));
  background-size: 200% 100%;
  animation: tasksShimmer 1.4s linear infinite;
}
@keyframes tasksShimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
.tasks-error {
  background: rgba(220,38,38,0.08);
  border: 1px solid rgba(220,38,38,0.25);
  color: rgb(220,38,38);
  padding: 14px 16px;
  border-radius: var(--radius-md);
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: var(--text-base);
}

/* Floating action button — mobile only */
.tasks-fab {
  display: none;
  position: fixed;
  right: 18px;
  bottom: 18px;
  width: 56px; height: 56px;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-text, #fff);
  border: none;
  cursor: pointer;
  box-shadow: 0 6px 20px rgba(0,0,0,0.18);
  z-index: 50;
  align-items: center; justify-content: center;
  touch-action: manipulation;
  transition: filter 0.12s, transform 0.05s;
}
.tasks-fab svg { width: 24px; height: 24px; }
.tasks-fab:hover  { filter: brightness(1.1); }
.tasks-fab:active { transform: scale(0.95); }

/* ─── Modal system ─── */
.tasks-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.45);
  z-index: 9000; /* above asset/callout detail (8500) and all sub-overlays */
  /* iOS paint-order fix — see css/12 .callout-modal-overlay: self-composite so
     this fixed overlay's z-index is honoured against the GPU-composited
     .app-shell on iOS WebKit, instead of being painted behind the view. */
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
  will-change: transform;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Honour iOS safe areas: on iPhones with a notch the modal otherwise
     sits behind the battery indicator at the top (and the home-bar at
     the bottom for the fullscreen variant). Longhand because Safari
     silently drops the shorthand when max()/calc()/env() are nested. */
  padding-top: max(20px, calc(env(safe-area-inset-top) + 12px));
  padding-bottom: max(20px, calc(env(safe-area-inset-bottom) + 12px));
  padding-left: 20px;
  padding-right: 20px;
  opacity: 0;
  transition: opacity 0.18s;
}
.tasks-modal-overlay.is-open { opacity: 1; }
body.tasks-modal-open { overflow: hidden; }

.tasks-modal {
  background: var(--bg-surface);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  width: 100%;
  max-width: 560px;
  max-height: calc(100vh - 40px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transform: translateY(8px);
  transition: transform 0.18s;
  box-shadow: 0 20px 50px rgba(0,0,0,0.18);
}
.tasks-modal-overlay.is-open .tasks-modal { transform: translateY(0); }
.tasks-modal-sm { max-width: 440px; }
.tasks-modal-md { max-width: 560px; }

.tasks-modal.is-fullscreen {
  max-width: 720px;
  /* Subtract the overlay padding (now safe-area aware) so the modal
     never extends behind the notch / home indicator. */
  max-height: calc(100vh - 40px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
  height: calc(100vh - 40px - env(safe-area-inset-top) - env(safe-area-inset-bottom));
}

.tasks-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 6px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.tasks-modal-head-spacer { flex: 1; }
.tasks-modal-head h2 {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 600;
  color: var(--text-primary);
}
.tasks-modal-close {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: transparent;
  border: none;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.1s, color 0.1s;
  touch-action: manipulation;
}
.tasks-modal-close:hover { background: var(--bg-elevated); color: var(--text-primary); }
.tasks-modal-close svg { width: 18px; height: 18px; }

.tasks-modal-body {
  flex: 1;
  overflow-y: auto;
  /* Never chain an edge-scroll out to the page — with the iOS keyboard open
     that chain becomes a visual-viewport pan over the page behind the sheet
     (the "background scrolls + sheet flickers" bug, Radek 2026-07-03). The
     touchmove guard in tasks.js openModal_ covers non-scrollable regions. */
  overscroll-behavior: contain;
  padding: 20px;
}
.tasks-modal-foot {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}

/* Buttons inside modals */
.tasks-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 10px 16px;
  border-radius: var(--radius-md);
  font-size: var(--text-md);
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  touch-action: manipulation;
  transition: filter 0.12s, background 0.12s, border-color 0.12s, transform 0.05s;
}
.tasks-btn:active { transform: scale(0.97); }
.tasks-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.tasks-btn-primary { background: var(--accent); color: var(--accent-text, #fff); }
.tasks-btn-primary:hover:not(:disabled) { filter: brightness(1.1); }
/* Secondary — the app-wide "Try again" / calm-action button. Was referenced by
   every module's error-retry card but never defined, so those buttons rendered
   as bare text (2026-07-03 fix). */
.tasks-btn-secondary {
  background: var(--bg-elevated);
  color: var(--text-primary);
  border-color: var(--border-strong, var(--border));
}
.tasks-btn-secondary:hover:not(:disabled) { background: var(--bg-hover); }
.tasks-btn-ghost {
  background: transparent;
  color: var(--text-secondary);
  border-color: var(--border);
}
.tasks-btn-ghost:hover { background: var(--bg-elevated); color: var(--text-primary); }
.tasks-btn-danger {
  background: rgba(220,38,38,0.10);
  color: rgb(220,38,38);
  border-color: rgba(220,38,38,0.30);
}
.tasks-btn-danger:hover { background: rgba(220,38,38,0.18); border-color: rgba(220,38,38,0.5); }

.tasks-btn .spin svg {
  width: 16px; height: 16px;
  animation: tasksSpin 0.9s linear infinite;
}
@keyframes tasksSpin { to { transform: rotate(360deg); } }

/* Forms */
.tasks-form { display: flex; flex-direction: column; gap: 14px; }
/* Badge shown at the top of the create modal when opened from an asset */
.tasks-asset-prefill-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--text-secondary);
}
.tasks-asset-prefill-badge strong { color: var(--accent); font-weight: 600; }
/* Personal-task badge — same footprint as the asset badge; signals the new task is
   private (assigned to you, no department). */
.tasks-personal-badge {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--accent);
}

/* "Needs claiming now" toggle — shown only when a task is raised from a chat group
   (prefill.claimToggle). Reuses the canonical .ah-switch. */
.tasks-claim { padding: 12px 14px; background: var(--bg-elevated); border: 1px solid var(--border); border-radius: var(--radius-md); }
.tasks-claim-row { display: flex; align-items: center; gap: 12px; cursor: pointer; margin: 0; }
.tasks-claim-text { flex: 1 1 auto; min-width: 0; }
.tasks-claim-t { display: block; font: 600 var(--text-base)/1.3 var(--body); color: var(--text-primary); }
.tasks-claim-s { display: block; font: 500 var(--text-sm)/1.4 var(--body); color: var(--text-secondary); margin-top: 2px; }
.tasks-form-context {
  font-size: var(--text-base);
  color: var(--text-muted);
  background: var(--bg-elevated);
  padding: 10px 12px;
  border-radius: var(--radius-md);
}
.tasks-form-context strong { color: var(--text-primary); }
.tasks-form-hint {
  font-size: var(--text-base);
  color: var(--text-muted);
  margin: 0 0 4px;
  line-height: 1.45;
}
.tasks-form-error {
  font-size: var(--text-base);
  color: rgb(220,38,38);
  background: rgba(220,38,38,0.08);
  padding: 10px 12px;
  border-radius: var(--radius-md);
}
.tasks-field { display: flex; flex-direction: column; gap: 6px; }
.tasks-field-label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  display: flex;
  align-items: center;
  gap: 6px;
}
.tasks-field-label em {
  font-style: normal;
  font-weight: 500;
  color: var(--text-muted);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--text-xs);
  opacity: 0.75;
}
.tasks-field input[type="text"],
.tasks-field input[type="number"],
.tasks-field textarea,
.tasks-field select {
  width: 100%;
  padding: 10px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-lg);
  color: var(--text-primary);
  font-family: inherit;
  transition: border-color 0.12s, background 0.12s;
}
.tasks-field input:focus,
.tasks-field textarea:focus,
.tasks-field select:focus {
  outline: none;
  border-color: var(--accent);
}
.tasks-field textarea { resize: vertical; min-height: 70px; line-height: 1.45; }
/* Use the app's muted placeholder colour (like .tasks-search / .photo-deck-caption)
   instead of the browser default grey. Without this, the Location placeholder fell
   back to the UA grey, which reads warm/"golden" next to the blue-grey --text-muted
   placeholders on the KB-style Title/Description. */
.tasks-field input::placeholder,
.tasks-field textarea::placeholder { color: var(--text-muted); }
.tasks-field input.has-error,
.tasks-field textarea.has-error {
  border-color: rgb(220,38,38);
  animation: tasksShake 0.3s;
}
@keyframes tasksShake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* New-task composer — Title + Description REUSE KB Capture's compact inputs
   (.kbcap-title / .kbcap-note-wrap / .kbcap-note / .kbcap-mic in css/19) so they
   look + feel exactly like the Capture composer. The only extra here is a small,
   always-visible "required" hint pinned to the right of the title that never
   collides with the typed text (the input reserves room for it). */
.tasks-compose-title { position: relative; }
.tasks-compose-title .kbcap-title { padding-right: 76px; }
.tasks-title-req {
  position: absolute;
  right: 2px;
  bottom: 9px;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--text-muted);
  pointer-events: none;
  white-space: nowrap;
}
/* Keep the empty-title error affordance readable on the borderless input. The
   :focus variant is needed so the red error underline wins over .kbcap-title:focus
   (css/19, equal specificity but loaded later) while the field is focused. */
.kbcap-title.has-error,
.kbcap-title.has-error:focus { border-bottom-color: rgb(220,38,38); animation: tasksShake 0.3s; }
.kbcap-title.has-error::placeholder { color: rgb(220,38,38); }
.kbcap-title.has-error ~ .tasks-title-req { color: rgb(220,38,38); }

/* Priority pills inside the quick-add form */
.tasks-priority-pills {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
}
.tasks-priority-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-secondary);
  cursor: pointer;
  transition: all 0.12s;
  touch-action: manipulation;
}
.tasks-priority-pill-dot {
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  background: var(--p-dot, var(--text-muted));
}
.tasks-priority-pill:hover { border-color: var(--text-muted); }
.tasks-priority-pill.is-active {
  background: var(--p-tint, var(--bg-elevated));
  border-color: var(--p-dot, var(--accent));
  color: var(--p-dot, var(--text-primary));
}

/* Picker list (status / priority) */
.tasks-picker-list { display: flex; flex-direction: column; gap: 4px; }
.tasks-picker-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 12px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  font-size: var(--text-md);
  color: var(--text-primary);
  transition: background 0.1s, border-color 0.1s;
  touch-action: manipulation;
}
.tasks-picker-item:hover { background: var(--bg-elevated); }
.tasks-picker-item.is-active { background: var(--bg-elevated); border-color: var(--border); }
.tasks-picker-dot {
  width: 10px; height: 10px;
  border-radius: var(--radius-full);
  flex-shrink: 0;
}
.tasks-picker-label { flex: 1; font-weight: 500; }
.tasks-picker-item svg {
  width: 16px; height: 16px;
  color: var(--accent);
}
.tasks-picker-search { margin-bottom: 10px; }
.tasks-picker-empty {
  padding: 16px 12px;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* Cost form */
.tasks-cost-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}
.tasks-cost-input {
  display: flex;
  align-items: center;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 0 12px;
  transition: border-color 0.12s;
}
.tasks-cost-input:focus-within { border-color: var(--accent); }
.tasks-cost-currency {
  font-size: var(--text-lg);
  color: var(--text-muted);
  margin-right: 4px;
}
.tasks-cost-input input {
  border: none !important;
  background: transparent !important;
  padding: 10px 0 !important;
  width: 100%;
  font-size: var(--text-lg);
  color: var(--text-primary);
  outline: none;
}
.tasks-cost-clear {
  align-self: flex-start;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: var(--text-base);
  text-decoration: underline;
  cursor: pointer;
  padding: 4px 0;
}
.tasks-cost-clear:hover { color: rgb(220,38,38); }

.tasks-confirm-text {
  font-size: var(--text-md);
  color: var(--text-primary);
  line-height: 1.55;
  margin: 0;
}
.tasks-confirm-text em {
  font-style: italic;
  color: var(--text-muted);
}

/* ─── Detail view ─── */
.tasks-detail { display: flex; flex-direction: column; gap: 14px; }

.tasks-detail-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}
.tasks-detail-ids {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.tasks-detail-ref {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  padding: 4px 9px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
}
/* Detail status + priority chips read CALM here — neutral chip + the colour kept
   only on the dot (so it matches the picker you open from them, and lets the
   Complete CTA be the one pop of colour). The LIST CARDS keep the loud tinted
   signal for scanning — this restraint is detail-only. (Radek 2026-06-29) */
.tasks-detail-priority {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  transition: border-color 0.12s;
  touch-action: manipulation;
}
.tasks-detail-priority:hover { border-color: var(--border-strong, var(--text-muted)); }
.tasks-detail-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-xs);
  font-weight: 500;
  padding: 5px 9px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  background: var(--bg-elevated);
  color: var(--text-primary);
  border: 1px solid var(--border);
  transition: border-color 0.12s;
  touch-action: manipulation;
}
.tasks-detail-status:hover { border-color: var(--border-strong, var(--text-muted)); }
.tasks-detail-status svg { width: 13px; height: 13px; color: var(--text-muted); }

.tasks-detail-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tasks-detail-section-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.tasks-detail-section-head h4 {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.tasks-detail-hero {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.tasks-detail-title-row,
.tasks-detail-desc-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
}
.tasks-detail-title {
  flex: 1;
  margin: 0;
  font-size: var(--text-3xl);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.02em;
  line-height: 1.25;
  word-wrap: break-word;
}
.tasks-detail-desc {
  flex: 1;
  margin: 0;
  font-size: var(--text-base);
  color: var(--text-secondary);
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
}
.tasks-detail-desc.is-empty {
  color: var(--text-muted);
  font-style: italic;
  font-size: var(--text-base);
}
.tasks-icon-btn {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.1s, color 0.1s;
}
.tasks-icon-btn:hover {
  background: var(--bg-elevated);
  color: var(--text-primary);
}
.tasks-icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.tasks-icon-btn svg { width: 14px; height: 14px; }
.tasks-icon-btn.is-danger { color: #dc2626; }
.tasks-icon-btn.is-danger:hover { background: rgba(220,38,38,0.1); color: #dc2626; }

.tasks-detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.tasks-detail-tile {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
  touch-action: manipulation;
}
.tasks-detail-tile:hover {
  border-color: var(--accent);
}
.tasks-detail-tile-icon {
  width: 38px; height: 38px;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-detail-tile-icon svg { width: 18px; height: 18px; }
.tasks-detail-tile-body {
  min-width: 0;
  flex: 1;
}
.tasks-detail-tile-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  margin-bottom: 3px;
}
.tasks-detail-tile-value {
  font-size: var(--text-md);
  font-weight: 600;
  color: var(--text-primary);
  word-wrap: break-word;
}
.tasks-detail-tile-value em {
  font-style: italic;
  font-weight: 500;
  color: var(--text-muted);
}
.tasks-cost-summary {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.tasks-cost-summary span {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-size: var(--text-md);
}
.tasks-cost-summary small {
  color: var(--text-muted);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-weight: 500;
}

/* History timeline */
.tasks-history {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* No nested scrollbox — the activity flows in the modal's single scroll
     (.tasks-modal-body), so it reads full-screen like the asset register
     instead of a cramped 320px inner scroll. */
}
.tasks-history-loading,
.tasks-history-empty {
  font-size: var(--text-base);
  color: var(--text-muted);
  text-align: center;
  padding: 20px;
}
.tasks-history-entry {
  display: flex;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.tasks-history-entry:last-child { border-bottom: none; }
.tasks-history-icon {
  width: 28px; height: 28px;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-history-icon svg { width: 14px; height: 14px; }
.tasks-history-icon[data-kind="created"]        { background: rgba(8,145,178,0.10); color: rgb(8,145,178); }
.tasks-history-icon[data-kind="status_changed"] { background: rgba(124,58,237,0.10); color: rgb(124,58,237); }
.tasks-history-icon[data-kind="cost_updated"]   { background: rgba(22,163,74,0.10); color: rgb(22,163,74); }
.tasks-history-content {
  flex: 1;
  min-width: 0;
}
.tasks-history-note {
  font-size: var(--text-base);
  color: var(--text-primary);
  line-height: 1.45;
  word-wrap: break-word;
}
.tasks-history-meta {
  font-size: var(--text-xs);
  color: var(--text-muted);
  margin-top: 3px;
  display: flex;
  align-items: center;
  gap: 6px;
}

.tasks-history-add {
  display: flex;
  gap: 8px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.tasks-history-add input {
  flex: 1;
  padding: 9px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-md);
  color: var(--text-primary);
  outline: none;
  transition: border-color 0.12s;
}
.tasks-history-add input:focus { border-color: var(--accent); }

.tasks-detail-danger {
  border-top: 1px solid var(--border);
  padding-top: 14px;
  margin-top: 4px;
  display: flex;
  justify-content: flex-end;
}
.tasks-detail-danger .tasks-btn-danger {
  padding: 7px 14px;
  font-size: var(--text-base);
  font-weight: 500;
  border-radius: var(--radius-md);
  gap: 6px;
}
.tasks-detail-danger .tasks-btn-danger svg {
  width: 14px;
  height: 14px;
}

/* ─── Detail: chips row (status + priority + dept) ─── */
.tasks-detail-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}

/* ─── Detail: mark complete button ─── */
.tasks-detail-complete-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 14px 20px;
  background: var(--accent);
  color: var(--accent-text, #fff);
  border: none;
  border-radius: var(--radius-md);
  font-size: var(--text-lg);
  font-weight: 700;
  cursor: pointer;
  transition: filter 0.12s, transform 0.05s;
  touch-action: manipulation;
  letter-spacing: 0.01em;
}
.tasks-detail-complete-btn svg { width: 20px; height: 20px; flex-shrink: 0; }
.tasks-detail-complete-btn:hover { filter: brightness(1.08); }
.tasks-detail-complete-btn:active { transform: scale(0.99); }
.tasks-detail-complete-btn.is-completed {
  background: rgba(22,163,74,0.12);
  color: rgb(22,163,74);
}

/* Compact variant — the complete button now lives INLINE on the status row,
   tucked right (margin-left:auto), so it's one tap + always visible without the
   old full-width bar eating ~50px. Filled accent = the primary action; the
   is-completed state flips to the calm green tint (with a matching border so it
   reads like the status pills). (Radek 2026-06-29) */
.tasks-detail-chips .tasks-detail-complete-btn {
  width: auto;
  margin-left: auto;
  padding: 7px 12px;
  gap: 6px;
  font-size: var(--text-sm);
  font-weight: 600;
  border-radius: var(--radius-md);
  letter-spacing: 0;
}
.tasks-detail-chips .tasks-detail-complete-btn svg { width: 16px; height: 16px; }
.tasks-detail-chips .tasks-detail-complete-btn.is-completed {
  border: 1px solid color-mix(in srgb, rgb(22,163,74) 35%, transparent);
}
/* Smallest phones: collapse to just the check so the row never wraps. */
@media (max-width: 360px) {
  .tasks-detail-chips .tasks-detail-complete-label { display: none; }
  .tasks-detail-chips .tasks-detail-complete-btn { padding: 7px 9px; }
}

/* ─── Detail: info grid (3-col, read-only + editable tiles) ─── */
.tasks-detail-info-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
}
.tasks-detail-info-tile {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  text-align: left;
  font-family: inherit;
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 0.12s;
  touch-action: manipulation;
  min-width: 0;
  /* Always fill the container cell. A flex <button> otherwise shrinks to its
     content width (e.g. an empty "Not linked" card half-fills its grid cell),
     so the shared card never assumes its parent stretches it. */
  width: 100%;
  box-sizing: border-box;
  position: relative;
}
.tasks-detail-info-tile:not(.is-readonly):hover { border-color: var(--accent); }
.tasks-detail-info-tile.is-readonly { cursor: default; }
.tasks-detail-info-tile-icon {
  width: 26px; height: 26px;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-detail-info-tile-icon svg { width: 14px; height: 14px; }
.tasks-detail-info-tile-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.tasks-detail-info-tile-label {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  line-height: 1.2;
  margin-bottom: 1px;
}
.tasks-detail-info-tile-value {
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: 1.25;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tasks-detail-info-tile-value em {
  font-style: normal;
  font-weight: 400;
  color: var(--text-muted);
}

/* Callout ref chip inside the task's "Callout" link tile. Same shape
   as the callout card's ref pill (mono font, accent-tinted background,
   accent border). Indicates the value IS an identifier you can grab. */
.tasks-detail-callout-ref-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 7px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  /* If we ever fall back to showing a raw id (lazy fetch not yet
     resolved, or callout doc missing), truncate cleanly so the chip
     doesn't blow up the tile width. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Callout ref chip inside activity timeline notes. Same chip shape as
   above but inline within a sentence ("Linked to callout [CHIP]"). The
   button reset (transparent background → accent-soft, no native border)
   lives here so it can sit inside flowing text. */
.tasks-event-callout-chip {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  margin: 0 2px;
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  border-radius: var(--radius-sm);
  color: var(--accent);
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 0.9em;
  font-weight: 600;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 0.12s ease, transform 0.06s ease;
  vertical-align: baseline;
}
.tasks-event-callout-chip:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}
.tasks-event-callout-chip:active { transform: scale(0.97); }
.tasks-detail-info-tile-edit {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity 0.12s;
}
.tasks-detail-info-tile-edit svg {
  width: 12px; height: 12px;
  color: var(--text-muted);
}
.tasks-detail-info-tile:not(.is-readonly):hover .tasks-detail-info-tile-edit { opacity: 1; }
/* Touch: keep the card plain — the DECORATIVE pencil stays hidden (tapping the
   whole tile already opens its editor, so a permanent pencil is redundant and
   just eats space). Desktop still reveals it on hover above. This is ONLY the
   decorative pencil; the split-button MANAGE pencil
   (.tasks-detail-info-tile-edit-btn, below) stays always-visible — it's the sole
   Change/Remove affordance for a linked entity. (Radek 2026-06-29) */

/* Active state — matches .tasks-detail-action-btn.is-active pattern */
.tasks-detail-info-tile.is-active {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.tasks-detail-info-tile.is-active .tasks-detail-info-tile-icon {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
}
.tasks-detail-info-tile.is-active .tasks-detail-info-tile-value {
  color: var(--accent);
}
.tasks-detail-info-tile.is-active .tasks-detail-info-tile-edit svg {
  color: var(--accent);
}

/* Split-button tile variant — used when the tile has both a main click area
   and a separate manage (pencil) button. The outer shell is a <div> rather
   than a <button> to keep the HTML valid. */
.tasks-detail-info-tile.has-split-btn {
  cursor: default;
  padding: 0;
  overflow: hidden;
}
.tasks-detail-info-tile-click {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1;
  min-width: 0;
  padding: 9px 8px 9px 9px;
  background: none;
  border: none;
  font-family: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.tasks-detail-info-tile-click:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
/* Static body — a linked tile that isn't itself openable (e.g. a free-text
   contractor, or an editor/form card where managing happens via the pencil).
   No pointer affordance; the pencil still opens the Change/Remove modal. */
.tasks-detail-info-tile-click.is-static { cursor: default; }
.tasks-detail-info-tile-click.is-static:hover { background: none; }
.tasks-detail-info-tile-edit-btn {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  width: 36px;
  height: 100%;
  min-height: 44px;
  background: none;
  border: none;
  border-left: 1px solid transparent;
  /* Always visible — this is the ONLY way to change/remove a linked asset
     or KB doc, so it must never depend on hover (invisible + untappable on
     touch devices, which broke "edit/change asset" on mobile). Hover just
     adds the accent border highlight below. */
  opacity: 1;
  transition: opacity 0.12s, border-color 0.12s;
  cursor: pointer;
  color: var(--text-muted);
}
.tasks-detail-info-tile-edit-btn svg { width: 12px; height: 12px; }
.tasks-detail-info-tile.is-active .tasks-detail-info-tile-edit-btn { color: var(--accent); }
.tasks-detail-info-tile:hover .tasks-detail-info-tile-edit-btn {
  opacity: 1;
  border-left-color: color-mix(in srgb, var(--accent) 30%, transparent);
}
@media (hover: none) { .tasks-detail-info-tile .tasks-detail-info-tile-edit-btn { opacity: 1; } }

/* Reminder tile shows relative time on top + absolute time below */
.tasks-detail-reminder-relative {
  display: block;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}
.tasks-detail-reminder-relative.is-overdue { color: rgb(220, 38, 38); }
.tasks-detail-reminder-absolute {
  display: block;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-muted);
  margin-top: 2px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Reminder picker — preset chips + date/time inputs */
.tasks-reminder-form { gap: 16px; }
.tasks-reminder-section { display: flex; flex-direction: column; gap: 8px; }
.tasks-reminder-section-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.tasks-reminder-presets {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
}
.tasks-reminder-preset {
  padding: 10px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  transition: all 0.12s;
  touch-action: manipulation;
  font-family: inherit;
  text-align: center;
}
.tasks-reminder-preset:hover { border-color: var(--accent); }
.tasks-reminder-preset.is-active {
  background: var(--accent);
  color: var(--accent-text, #fff);
  border-color: var(--accent);
}
.tasks-reminder-datetime {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* ─── Detail: action grid (cost, call out, order, equipment) ─── */
.tasks-detail-actions-section { display: flex; flex-direction: column; gap: 10px; }
.tasks-detail-section-label {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.tasks-detail-action-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.tasks-detail-action-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
  font-family: inherit;
  color: var(--text-primary);
  transition: border-color 0.12s;
  touch-action: manipulation;
  min-width: 0;
}
.tasks-detail-action-btn:hover { border-color: var(--accent); }
.tasks-detail-action-btn-ico {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--bg-surface);
  color: var(--text-muted);
  flex-shrink: 0;
}
.tasks-detail-action-btn-ico svg { width: 15px; height: 15px; }
.tasks-detail-action-btn-body { flex: 1; min-width: 0; }
.tasks-detail-action-btn-label {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 3px;
}
.tasks-detail-action-btn-value {
  display: block;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tasks-detail-action-btn-value em {
  font-style: normal;
  font-weight: 400;
  color: var(--text-muted);
}
/* Active state — card has a value set */
.tasks-detail-action-btn.is-active {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.tasks-detail-action-btn.is-active .tasks-detail-action-btn-ico {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
}
.tasks-detail-action-btn.is-active .tasks-detail-action-btn-value {
  color: var(--accent);
}

/* ─── Task detail: meta row (callout + asset chips) ─── */
.tasks-detail-meta-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
}
.tasks-detail-meta-cell { min-width: 0; display: flex; }
.tasks-detail-link-chip {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--text-secondary);
  text-align: left;
  transition: border-color 0.12s, background 0.12s;
}
.tasks-detail-link-chip svg { width: 14px; height: 14px; flex-shrink: 0; }
.tasks-detail-link-chip.is-empty { font-style: italic; color: var(--text-muted); }
.tasks-detail-link-chip.is-linked { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
button.tasks-detail-link-chip.is-empty:hover { border-color: var(--accent); color: var(--text-primary); }
button.tasks-detail-link-chip.is-linked:hover { background: var(--accent-soft); }
.tasks-detail-link-chip-badge {
  font-family: var(--mono, ui-monospace, SFMono-Regular, monospace);
  font-size: 10px; font-weight: 700; letter-spacing: 0.05em;
  color: var(--accent); flex-shrink: 0;
}
.tasks-detail-link-chip-value { font-weight: 600; color: var(--text-primary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ─── Task detail: tab bar + panels ─── */
.tasks-detail-tabs { margin: 4px -18px 0; padding: 0 18px; }
/* Tasks tabs scroll AWAY with the content (no sticky pin) — matching the
   Call-outs detail, so scrolling into a panel frees the reading room. The tab
   bar + panels are siblings inside one .tasks-detail box, so dropping sticky
   lets the bar scroll off (in Call-outs the tab bar is the last child of its
   header-body, which is why it scrolls away there too). Double-class beats the
   shared .callout-detail-tabs sticky rule (css/12) regardless of source order —
   a specificity trap (CLAUDE.md §4). All viewports, because Call-outs is
   scroll-away on both mobile and desktop (Radek 2026-07-01). */
.tasks-detail-tabs.callout-detail-tabs { position: static; z-index: auto; }
.tasks-detail-tab-panels { flex: 1; }
.tasks-detail-tab-panel { padding: 16px 0 24px; }
.tasks-detail-doc-photos { display: flex; flex-wrap: wrap; gap: 8px; }

/* ─── Task detail: file attachments (Documents tab) ─── */
.tasks-detail-doc-files { display: flex; flex-direction: column; gap: 8px; }
.tasks-detail-doc-photos:not(:empty) + .tasks-detail-doc-files { margin-top: 10px; }
.tasks-detail-file-chip {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: border-color .15s ease, background .15s ease;
}
.tasks-detail-file-chip:hover { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.tasks-detail-file-chip.is-uploading { opacity: .6; }
.tasks-detail-file-link {
  display: flex; align-items: center; gap: 10px;
  flex: 1 1 auto; min-width: 0;
  text-decoration: none; color: inherit;
}
.tasks-detail-file-link[aria-disabled="true"] { pointer-events: none; }
.tasks-detail-file-ico { font-size: 20px; line-height: 1; flex-shrink: 0; }
/* Doc-type chip leads the file row (matches Callouts) — keep it from squashing
   when the filename is long. */
.tasks-detail-file-link .callout-doc-type { flex-shrink: 0; }
.tasks-detail-file-meta { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tasks-detail-file-name {
  font-size: var(--text-sm); font-weight: 600; color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tasks-detail-file-size { font-size: var(--text-xs); color: var(--text-muted); }
.tasks-detail-file-del {
  flex-shrink: 0;
  width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; cursor: pointer;
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  transition: color .15s ease, background .15s ease;
}
.tasks-detail-file-del:hover { color: #dc2626; background: color-mix(in srgb, #dc2626 10%, transparent); }
.tasks-detail-file-del svg { width: 16px; height: 16px; }
.tasks-detail-file-spinner {
  flex-shrink: 0; width: 18px; height: 18px; color: var(--text-muted);
  animation: tasksSpin 0.9s linear infinite;
}
.tasks-detail-file-spinner svg { width: 18px; height: 18px; }
.tasks-detail-doc-empty {
  padding: 24px 12px; text-align: center;
  font-size: var(--text-sm); color: var(--text-muted);
}
.tasks-detail-doc-empty strong { color: var(--text-secondary); font-weight: 600; }

/* ─── Delegate picker: user avatar initials ─── */
.tasks-picker-avatar {
  width: 28px; height: 28px;
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--accent-text, #fff);
  display: grid; place-items: center;
  font-size: var(--text-sm);
  font-weight: 700;
  flex-shrink: 0;
}

/* ─── Task photo: create modal capture card ─── */
.tasks-photo-capture {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.tasks-photo-capture-cta {
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 18px 18px;
  background: var(--bg-elevated);
  border: 1.5px solid var(--border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s, transform 0.05s;
  touch-action: manipulation;
  font-family: inherit;
  text-align: left;
  color: var(--text-primary);
}
/* Hover matches the app's interactive cards: solid accent border + subtle
   accent-tinted fill (same recipe as the linked-entity tiles). */
.tasks-photo-capture-cta:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 6%, var(--bg-elevated));
}
.tasks-photo-capture-cta:active { transform: scale(0.995); }
.tasks-photo-capture-icon {
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border-radius: var(--radius-md);
  background: var(--accent);
  color: var(--accent-text, #fff);
  flex-shrink: 0;
}
.tasks-photo-capture-icon svg { width: 24px; height: 24px; }
.tasks-photo-capture-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.tasks-photo-capture-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.3;
}
.tasks-photo-capture-sub {
  font-size: var(--text-sm);
  color: var(--text-muted);
  line-height: 1.3;
}

/* When photos exist, shrink the capture CTA into a compact "+" button */
.tasks-photo-capture.has-photos .tasks-photo-capture-cta {
  padding: 10px 14px;
  border-style: solid;
  background: var(--bg-elevated);
}
.tasks-photo-capture.has-photos .tasks-photo-capture-icon {
  width: 32px; height: 32px;
  border-radius: var(--radius-md);
}
.tasks-photo-capture.has-photos .tasks-photo-capture-icon svg {
  width: 18px; height: 18px;
}
.tasks-photo-capture.has-photos .tasks-photo-capture-title { font-size: var(--text-base); }
.tasks-photo-capture.has-photos .tasks-photo-capture-sub { display: none; }

.tasks-photo-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.tasks-photo-thumb {
  position: relative;
  width: 80px; height: 80px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  flex-shrink: 0;
}
.tasks-photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
}
.tasks-photo-thumb.is-uploading img { opacity: 0.5; }
.tasks-photo-thumb-remove {
  position: absolute;
  top: 0; right: 0;
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  background: transparent;
  border: 0;
  color: #fff;
  cursor: pointer;
  touch-action: manipulation;
  padding: 0;
}
.tasks-photo-thumb-remove::before {
  content: "";
  position: absolute;
  top: 5px; right: 5px;
  width: 24px; height: 24px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  backdrop-filter: blur(12px) saturate(160%);
  border: 0.5px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: background 0.15s, transform 0.08s;
}
.tasks-photo-thumb-remove:hover::before { background: rgba(220, 38, 38, 0.85); }
.tasks-photo-thumb-remove:active::before { transform: scale(0.92); }
.tasks-photo-thumb-remove:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
.tasks-photo-thumb-remove svg {
  position: absolute;
  z-index: 1;
  top: 11px; right: 11px;
  width: 12px; height: 12px;
  stroke-width: 2.4;
  pointer-events: none;
}

.tasks-photo-thumb-annotate {
  position: absolute;
  top: 0; left: 0;
  width: 44px; height: 44px;
  border-radius: var(--radius-full);
  background: transparent;
  border: 0;
  color: #fff;
  cursor: pointer;
  touch-action: manipulation;
  padding: 0;
}
.tasks-photo-thumb-annotate::before {
  content: "";
  position: absolute;
  top: 5px; left: 5px;
  width: 24px; height: 24px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  backdrop-filter: blur(12px) saturate(160%);
  border: 0.5px solid rgba(255, 255, 255, 0.18);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
  transition: background 0.15s, transform 0.08s;
}
.tasks-photo-thumb-annotate:hover::before { background: var(--accent); border-color: rgba(255, 255, 255, 0.3); }
.tasks-photo-thumb-annotate:active::before { transform: scale(0.92); }
.tasks-photo-thumb-annotate:focus-visible { outline: 2px solid #fff; outline-offset: -4px; }
.tasks-photo-thumb-annotate svg {
  position: absolute;
  z-index: 1;
  top: 11px; left: 11px;
  width: 12px; height: 12px;
  stroke-width: 2.2;
  pointer-events: none;
}

/* Two 44px corner actions plus the tile's 1px borders need 90px to remain
   distinct. Current Annotate callers use the wider deck; this keeps the
   shared strip variant safe too. */
.tasks-photo-thumb:has(> .tasks-photo-thumb-annotate) {
  width: 90px; height: 90px;
}

/* annotation-count badge → shared .ah-annot-badge.is-thumb (Prompt 106) */

.tasks-photo-thumb-spinner {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: rgba(0,0,0,0.35);
}
.tasks-photo-thumb-spinner svg {
  width: 20px; height: 20px;
  color: #fff;
  animation: tasksSpin 0.9s linear infinite;
}

/* Failed upload (ui/photo-capture.js): an unmistakable state + an ALWAYS-visible
   Retry (never hover-only — §4) so a photo that didn't upload can't be silently
   saved. The deck pill shows an icon + label; the compact 80px strip thumb shows
   an icon-only 44px tap target. Tokens only → both themes. */
.tasks-photo-thumb.is-failed,
.photo-deck-media.is-failed { border-color: var(--danger, #dc2626); }
.tasks-photo-thumb-failed {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  background: color-mix(in srgb, #dc2626 30%, rgba(0, 0, 0, 0.5));
}
.tasks-photo-thumb-retry {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 8px 15px;
  border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(12px) saturate(160%);
  backdrop-filter: blur(12px) saturate(160%);
  border: 0.5px solid rgba(255, 255, 255, 0.28);
  color: #fff;
  font-family: inherit; font-weight: 700; font-size: var(--text-xs);
  cursor: pointer;
  touch-action: manipulation;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  transition: background 0.15s, transform 0.08s;
}
.tasks-photo-thumb-retry:hover { background: rgba(0, 0, 0, 0.78); }
.tasks-photo-thumb-retry:active { transform: scale(0.96); }
.tasks-photo-thumb-retry:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
.tasks-photo-thumb-retry svg { width: 15px; height: 15px; stroke-width: 2.2; flex-shrink: 0; }
/* Compact strip thumbnail (80px): icon-only circular retry, label hidden. */
.tasks-photo-thumb .tasks-photo-thumb-retry {
  min-width: 44px; min-height: 44px; padding: 0; gap: 0;
  border-radius: var(--radius-full);
}
.tasks-photo-thumb .tasks-photo-thumb-retry span { display: none; }

/* Offline-QUEUED upload (ui/photo-capture.js, queuedAware consumers — the
   shared photo picker): a calm settled badge, deliberately NOT a Retry —
   retrying a queued photo would enqueue it again (duplicate on reconnect). */
.tasks-photo-thumb-queued {
  position: absolute; left: 4px; right: 4px; bottom: 4px;
  padding: 3px 6px; border-radius: var(--radius-full);
  background: rgba(0, 0, 0, 0.62); color: #fff;
  font-family: inherit; font-weight: 600; font-size: 10px;
  text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  pointer-events: none;
}

/* ─── Photo capture: DECK mode (ui/photo-capture.js, mode:'deck') ───
   One photo per slide with an editable description below; swipe + dots.
   Shared control, styled here alongside .tasks-photo-capture. Reuses the
   .tasks-photo-thumb-remove / -annotate / -spinner overlay buttons. */
.tasks-photo-capture.is-deck .tasks-photo-strip { display: block; }
.photo-deck { display: flex; flex-direction: column; gap: 10px; }
.photo-deck-track {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.photo-deck-track::-webkit-scrollbar { display: none; }
.photo-deck-slide {
  flex: 0 0 100%;
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 0 1px;
}
.photo-deck-media {
  position: relative;
  width: 100%;
  aspect-ratio: 4 / 3;
  max-height: 440px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
}
.photo-deck-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-deck-caption {
  width: 100%;
  min-height: 58px;
  resize: vertical;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 10px 12px;
  color: var(--text-primary);
  font-family: inherit;
  font-size: var(--text-md);
  line-height: 1.45;
}
.photo-deck-caption::placeholder { color: var(--text-muted); }
.photo-deck-caption:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
}
.photo-deck-dots {
  display: flex;
  justify-content: center;
  gap: 6px;
  padding-top: 2px;
}
.photo-deck-dot {
  width: 7px; height: 7px;
  border-radius: var(--radius-full);
  background: var(--border);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background 0.15s, width 0.15s;
}
.photo-deck-dot.is-active { background: var(--accent); width: 18px; border-radius: 4px; }

/* Whole-photo deck variant + read-only viewer. `is-media-contain` is an
   opt-in editor presentation (Noticeboard); the default editor remains cover.
   Read-only stays contain so annotations align with xMidYMid-meet. */
.tasks-photo-capture.is-media-contain .photo-deck-media img,
.tasks-photo-capture.is-readonly .photo-deck-media img { object-fit: contain; }
.photo-deck-caption.is-readonly {
  min-height: 0;
  resize: none;
  white-space: pre-wrap;
  font-weight: 500;
}

/* ─── Task photo: detail view strip ─── */
.tasks-detail-photos-wrap {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  flex-wrap: wrap;
}
.tasks-detail-photo-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  flex: 1;
}
/* Flat row: thumbnails + camera tile all on one line */
.tasks-detail-photo-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 8px;
}
.tasks-detail-photos-empty {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--text-base);
  color: var(--text-muted);
  padding: 6px 0;
}
.tasks-detail-photos-empty svg { width: 16px; height: 16px; flex-shrink: 0; }
.tasks-detail-photo-thumb {
  position: relative;
  width: 80px; height: 80px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 0.12s, transform 0.08s;
}
.tasks-detail-photo-thumb:hover { border-color: var(--accent); transform: scale(1.03); }
.tasks-detail-photo-thumb img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
/* Show the delete overlay on task thumbnails — mirrors .callout-photo-tile behaviour */
.tasks-detail-photo-thumb:hover .callout-photo-tile-actions,
.tasks-detail-photo-thumb:focus-within .callout-photo-tile-actions { opacity: 1; }
@media (hover: none) {
  .tasks-detail-photo-thumb .callout-photo-tile-actions { opacity: 1; }
}
/* annotation-count badge → shared .ah-annot-badge.is-thumb (Prompt 106) */
.tasks-detail-photo-add-btn {
  width: 80px; height: 80px;
  border-radius: var(--radius-md);
  background: var(--bg-elevated);
  border: 1px dashed var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  color: var(--text-muted);
  cursor: pointer;
  flex-shrink: 0;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
  touch-action: manipulation;
  font-family: inherit;
}
.tasks-detail-photo-add-btn svg { width: 20px; height: 20px; flex-shrink: 0; }
.tasks-detail-photo-add-btn span {
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}
.tasks-detail-photo-add-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}
.tasks-detail-photo-add-btn:hover span { color: var(--accent); }

/* ─── Task photo lightbox ─── */
.tasks-lightbox-overlay {
  position: fixed; inset: 0;
  background: rgba(0,0,0,0.92);
  z-index: 500;
  display: flex;
  flex-direction: column;
  opacity: 0;
  transition: opacity 0.18s;
}
.tasks-lightbox-overlay.is-open { opacity: 1; }
.tasks-lightbox {
  display: flex;
  flex-direction: column;
  height: 100%;
}
.tasks-lightbox-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Full-screen photo lightbox → clear the status bar robustly (see --safe-top). */
  padding: var(--safe-top) 16px 12px;
  flex-shrink: 0;
  gap: 12px;
}
.tasks-lightbox-title {
  font-size: var(--text-md);
  font-weight: 600;
  color: rgba(255,255,255,0.8);
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tasks-lightbox-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}
.tasks-lightbox-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 12px;
  background: rgba(255,255,255,0.1);
  border: 1px solid rgba(255,255,255,0.15);
  border-radius: var(--radius-md);
  color: #fff;
  font-size: var(--text-base);
  font-weight: 500;
  cursor: pointer;
  transition: background 0.12s;
  touch-action: manipulation;
  font-family: inherit;
}
.tasks-lightbox-btn svg { width: 15px; height: 15px; }
.tasks-lightbox-btn:hover { background: rgba(255,255,255,0.18); }
.tasks-lightbox-btn-danger:hover { background: rgba(220,38,38,0.5); border-color: rgba(220,38,38,0.5); }
.tasks-lightbox-close {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: rgba(255,255,255,0.1);
  border: none;
  border-radius: var(--radius-md);
  color: #fff;
  cursor: pointer;
  transition: background 0.12s;
}
.tasks-lightbox-close:hover { background: rgba(255,255,255,0.2); }
.tasks-lightbox-close svg { width: 18px; height: 18px; }
.tasks-lightbox-img-wrap {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  position: relative;
  overflow: hidden;
}
.tasks-lightbox-img {
  max-width: 100%; max-height: 100%;
  object-fit: contain;
  border-radius: var(--radius-md);
  display: block;
}

/* ─── Mobile tweaks ─── */
@media (max-width: 640px) {
  .tasks-page { padding: 0 0 96px; }
  .tasks-page-header,
  .tasks-toolbar,
  .tasks-list-wrap { max-width: 100%; padding-left: 0; padding-right: 0; }
  .tasks-header-row {
    flex-direction: column;
    align-items: stretch;
  }
  /* Hide the header "New task" button on mobile — FAB takes over */
  .tasks-header-row .tasks-new-btn { display: none; }

  /* Mobile: split-pane collapses — only the list pane shows, detail uses
     the bottom-sheet modal (openModal_ with fullscreen: true) like before. */
  .tasks-split {
    display: block;
  }
  .tasks-list-pane { width: 100%; flex-basis: auto; }
  .tasks-detail-pane { display: none; }

  /* Bottom-sheet modals sit at the bottom, but their TOP must always clear the
     status-bar safe zone — header + X close stay tappable whether or not a photo
     made the sheet tall. The long saga: computed-height caps (92vh / 100dvh /
     --vvh) all FAILED because those units over-report the screen on an iOS
     standalone PWA, so a bottom-anchored sheet's top drifted into the notch. Then
     `max-height: 100%` ALSO failed — on iOS Safari a percentage height does NOT
     resolve against a position:fixed; inset:0 parent (the height is treated as
     indefinite), so the cap silently became `none`, the sheet grew to full content
     height, and its top spilled up again. (The keyboard path works only because it
     gives the overlay an explicit `height`, which lets the % resolve.)
     The fix that can't be dropped: don't cap with a length OR a percentage — let
     FLEXBOX clamp the sheet. Make the overlay a COLUMN flex (main axis = vertical),
     pack the sheet to the bottom (justify-content: flex-end) and centre it
     horizontally (align-items: center). A tall sheet then FLEX-SHRINKS to the
     container's real rendered height (min-height: 0 lets it shrink past its
     content) and its body scrolls — flex uses the actual box, never a % that iOS
     can drop. padding-top reserves the safe zone, so the clamped sheet's top lands
     exactly there. A SHORT sheet keeps its natural height and rests low — still a
     bottom sheet. This is the primitive the app's full-screen modals already use. */
  .tasks-modal-overlay {
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    padding: var(--safe-top) 0 0;
  }
  .tasks-modal {
    min-height: 0;          /* allow vertical flex-shrink below content height */
    max-height: 100%;       /* belt for browsers where % resolves; flex-shrink is the real cap on iOS */
    border-radius: 18px 18px 0 0;
    transform: translateY(40px);
  }

  /* iOS keyboard fix — the New-task sheet is a body-level position:fixed overlay
     anchored to the LAYOUT viewport. When you focus an input, iOS pans the visual
     viewport up to reveal it, and the whole sheet rides up with it — sliding its
     header (and the X close) under the status bar so you can't shut it. While the
     keyboard is open, re-anchor the overlay to the VISIBLE viewport using --vvtop
     / --vvh (set by the visualViewport listener in index.html, same vars the chat
     thread pins to): the sheet then always sits in view, header clear of the notch,
     base resting on the keyboard. --vvh already excludes the keyboard, so drop the
     keyboard-height bottom padding the global rule adds. */
  body.kb-open .tasks-modal-overlay {
    top: var(--vvtop, 0px);
    bottom: auto;
    height: var(--vvh, 100%);
    padding-top: max(10px, env(safe-area-inset-top)) !important;
    /* Sheet rests DIRECTLY on the keyboard — the old 10px gap showed a strip
       of the page behind and read as wasted space (Radek 2026-07-03). */
    padding-bottom: 0 !important;
  }
  body.kb-open .tasks-modal { max-height: 100%; }
  /* Skirt: iOS reserves a strip below the visual viewport for its floating
     input-assistant pill (the ↑↓✓ bar) and the page behind shows through it —
     tasks list peeking between the sheet and the keyboard. Paint that strip in
     the sheet's own surface colour so the sheet visually runs down to the
     keyboard. Sits below --vvh, so the OS keyboard covers it when no pill. */
  body.kb-open .tasks-modal-overlay::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 80px;
    background: var(--bg-surface);
    pointer-events: none;
  }

  /* While the keyboard is up you're filling fields, not submitting — hide the
     Cancel / Create footer so the form reclaims that ~70px of space above the
     keyboard. It reappears the instant the keyboard closes (the natural moment to
     tap Create). Radek 2026-06-27. */
  body.kb-open .tasks-modal-foot { display: none; }

  /* Fullscreen overlay (task detail): align to the top and push the
     entire modal below the notch at the overlay level, exactly like
     .fullscreen-settings does. This is more reliable than relying on
     env() inside a nested calc() on the modal head alone. */
  .tasks-modal-overlay.is-fullscreen-overlay {
    align-items: flex-start;
    /* Full-screen task detail → clear the status bar robustly (see --safe-top);
       the modal height subtracts the same inset so it can't overflow the bottom. */
    padding-top: var(--safe-top);
    /* White the safe-area strip so the header reads as one continuous white bar
       right up to the top edge — matching the Call-outs detail (which fills to
       the top). A full-screen takeover has nothing behind it, so the dark scrim
       is pointless here. We KEEP the robust overlay-level notch clearance above:
       padding the header alone slid under the status bar on iOS PWA (see css/15
       §audit-detail note). Also covers the Rooms detail, which reuses this same
       overlay class (Radek OK'd both, 2026-07-01). */
    background: var(--bg-surface);
  }
  .tasks-modal-overlay.is-fullscreen-overlay .tasks-modal.is-fullscreen {
    height: calc(100vh - var(--safe-top));
    max-height: calc(100vh - var(--safe-top));
    border-radius: 0;
    border: none;
  }
  /* Head matches KB reader header: spacer-based layout + same padding */
  .tasks-modal-overlay.is-fullscreen-overlay .tasks-modal-head {
    padding: 14px 18px;
    justify-content: flex-start;
    gap: 4px;
  }
  /* Close button matches kb-reader-action footprint (40×40, 20px icon) */
  .tasks-modal-overlay.is-fullscreen-overlay .tasks-modal-close {
    width: 40px; height: 40px;
  }
  .tasks-modal-overlay.is-fullscreen-overlay .tasks-modal-close svg {
    width: 20px; height: 20px;
  }

  /* Non-fullscreen bottom-sheet head: small top buffer in case the sheet
     ever slides all the way to the top of the screen */
  .tasks-modal .tasks-modal-head {
    padding-top: 16px;
  }

  /* Home-bar clearance for fullscreen modal footer */
  .tasks-modal.is-fullscreen .tasks-modal-foot {
    padding-bottom: calc(env(safe-area-inset-bottom) + 14px);
  }
  /* The detail view is fullscreen with NO footer — give its scroll body the
     same home-bar clearance so the last activity entry isn't tucked under the
     iPhone home indicator / bottom edge. */
  .tasks-modal.is-fullscreen .tasks-modal-body {
    padding-bottom: calc(env(safe-area-inset-bottom) + 24px);
  }
  .tasks-modal-body { padding: 16px; }
  .tasks-modal-foot { padding: 12px 16px; }

  .tasks-detail-title { font-size: var(--text-2xl); }
  .tasks-detail-grid { grid-template-columns: 1fr; }
  .tasks-cost-grid { grid-template-columns: 1fr; }
  .tasks-detail-info-grid { grid-template-columns: 1fr 1fr; }
  .tasks-detail-action-grid { grid-template-columns: 1fr 1fr; }

  .tasks-priority-select { width: 100%; }
  .tasks-priority-select select { flex: 1; }

  .tasks-filter-row { gap: 6px; }

  .tasks-card-main { padding: 14px; }
}

/* Mobile width pass (2026-06-25): tighten horizontal padding on mobile so content
   uses the screen width. Appended at end-of-file so it beats the base rules AND any
   wider-breakpoint (600/640/767px) blocks at equal specificity (later source wins).
   Desktop (>=600px) untouched. */
@media (max-width: 599px) {
  .tasks-modal-head { padding: 14px 16px; }
  .tasks-modal-body { padding: 14px; }
}

/* "Move to department" sheet (Radek 2026-06-26): the Site / Department pickers are
   .tasks-priority-* dropdowns whose menu opens DOWNWARD. In a short bottom sheet the
   triggers sit near the screen bottom, so an open menu was clipped behind the footer /
   iOS bottom bar. Fix: open this sheet TALL from the start (top pinned at --safe-top so
   it never slides under the status bar) — the triggers then sit near the top with room
   below for their downward menus. Always-tall (not a :has() grow-on-open) so the sheet
   doesn't jump up/down each time a dropdown is toggled (Radek 2026-06-26 follow-up).
   body.kb-open → the existing rule pins the overlay to the visible viewport, so just
   fill it. The menu is height-capped + scrollable as a safety for long lists.
   Appended at end-of-file so it beats the base .tasks-modal mobile rules. */
@media (max-width: 599px) {
  .tasks-modal.tasks-modal-deptpick {
    /* Always-tall = flex-GROW to fill the column overlay's safe-top-padded box.
       flex (not height:100%) because a % height doesn't resolve against the
       fixed overlay on iOS — same root fix as the base .tasks-modal above. */
    flex: 1 1 auto;
    min-height: 0;
  }
  body.kb-open .tasks-modal.tasks-modal-deptpick {
    flex: 1 1 auto;
    min-height: 0;
  }
  .tasks-modal-deptpick .tasks-priority-menu {
    max-height: 40vh;
    overflow-y: auto;
  }
}

/* ── Mobile: taxonomy dropdowns (location / category / dept / site) use the FULL
   field width + a tall scrollable list so the picker is easy to navigate on a
   phone — paired with the upward-flip + no-auto-focus-search (taxonomy-dropdown.js)
   so the keyboard doesn't eat the view. Appended at the end so it wins source
   order over the base rule (a media query adds no specificity). ── */
@media (max-width: 599px) {
  .tasks-priority-menu {
    left: 0;
    right: 0;
    min-width: 0;
    max-height: 56vh;
    overflow-y: auto;
  }
}


/* ── EMBEDDED task-detail diet (Radek 2026-07-05) ───────────────────────
   Inside an audit question card (AngelHubTasks.mountDetail with
   opts.embedded) the full 13-tile meta grid drowned the card — six empty
   "Not set / Not linked" tiles carried no information. Scoped to
   .tasks-detail.is-embedded so the standalone Tasks detail is untouched:
   hide the EMPTY optional tiles (the runtime .is-active class marks
   populated ones, so a tile reappears the moment it gains a value); the
   two act-now empties (Due date, Delegated to) always stay, as do the
   read-only context tiles (Date logged, Reported by). The audit link tile
   hides unconditionally — it points back at the audit the card lives in.
   The "Open full task" tile (tasks.js, embedded-only markup) is the
   one-tap route to everything hidden here. DOM stays intact — CSS-only —
   so every refresh/bind helper in bindTaskDetailHandlers_ keeps working. */
.tasks-detail.is-embedded .tasks-detail-info-tile[data-act="edit-location"]:not(.is-active),
.tasks-detail.is-embedded .tasks-detail-info-tile[data-act="edit-reminder"]:not(.is-active),
.tasks-detail.is-embedded .tasks-detail-info-tile[data-act="edit-budget"]:not(.is-active),
.tasks-detail.is-embedded #taskCalloutTile > .tasks-detail-info-tile:not(.is-active),
.tasks-detail.is-embedded #taskAssetTile > .tasks-detail-info-tile:not(.is-active),
.tasks-detail.is-embedded #taskKbTile > .tasks-detail-info-tile:not(.is-active),
.tasks-detail.is-embedded #taskAuditTile > .tasks-detail-info-tile {
  display: none;
}
