/* ============================================================================
   KIBO v2 — Tasks (list view)
   First view over the unified task domain. Token-based, see _tokens.css.
   ============================================================================ */

/* ── Contextual left panel (project scope / filter) ─────────────────────────── */
.tasks-panel { padding: .25rem 0; }
.tasks-panel-title {
    font-size: .7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--ink-soft); padding: .5rem .875rem .25rem;
}
.tasks-panel-hint {
    padding: .25rem .875rem; margin: 0; font-size: .8rem; color: var(--ink-soft);
}

/* Left-panel project list controls (status filter + search). Compact to fit the narrow panel. */
.tasks-panel-tools { display: flex; flex-direction: column; gap: .4rem; padding: .15rem .75rem .5rem; }
.tasks-panel-seg { width: 100%; }
.tasks-panel-seg button { flex: 1 1 0; padding: 4px 6px; font-size: 11px; }
.tasks-panel-search { width: 100%; }
/* Status filter (list view). Full width like the search box, and inset to the same margin as the
   project rows below it so the panel reads as one column. */
.tasks-panel-status { width: calc(100% - .75rem); margin: 0 .375rem .5rem; }
.tasks-panel-row {
    display: flex; align-items: center; gap: .5rem;
    width: calc(100% - .75rem); margin: 1px .375rem; padding: .45rem .75rem;
    text-align: left; background: none; border: none; border-radius: 10px;
    font-size: .875rem; color: var(--ink); cursor: pointer;
    transition: background .1s, color .1s;
}
.tasks-panel-row:hover { background: var(--paper); }
.tasks-panel-row.on   { background: var(--human-soft); color: var(--human-text); font-weight: 600; }
.tasks-panel-row.check input { flex: 0 0 auto; cursor: pointer; }
.tasks-panel-row.standalone { margin-top: .35rem; }
.tasks-panel-dot {
    flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%;
    background: var(--ink-soft); opacity: .5;
}
.tasks-panel-dot.project { background: var(--human); opacity: 1; }
.tasks-panel-txt { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Unified public_id chip — task/project code shown next to a name everywhere it appears (left panel,
   Gantt side, kanban card, list tree, inspector head). Small monospace, grey, darker border. */
.k-id-chip {
    flex: 0 0 auto;
    font-family: ui-monospace, monospace;
    font-size: .64rem;
    font-weight: 700;
    line-height: 1.4;
    color: var(--ink-soft);
    background: var(--paper);
    border: 1px solid color-mix(in srgb, var(--ink-soft) 45%, transparent);
    padding: .02rem .3rem;
    border-radius: 4px;
    white-space: nowrap;
}

/* ── Priority dot (KBW-0033) ─────────────────────────────────────────────────────
   One shared marker across every view (list / kanban / gantt): a small pastel dot to the LEFT of
   the id chip, present only for priority > 0 so unprioritised rows stay clean. Colour encodes the
   level by hue (blue-grey → amber → rose), never a lightness ramp, so the three stay legible at a
   glance; the element carries a title for colour-blind users. Kept tender per the spec. */
.k-prio-dot {
    flex: 0 0 auto;
    width: 8px; height: 8px;
    border-radius: 50%;
    /* A hairline ring keeps a pale dot from vanishing on same-tone backgrounds. */
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--ink) 12%, transparent);
}
.k-prio-dot.prio-lo { background: #8fb0c9; }   /* low    — calm blue-grey */
.k-prio-dot.prio-md { background: #e3b25b; }   /* medium — amber */
.k-prio-dot.prio-hi { background: #df8a86; }   /* high   — rose */

/* Open-details affordance on a multi-select (checkbox) project row, where the row click toggles the
   checkbox and so can't also open the inspector (item follow-up). */
.tasks-panel-open {
    flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; padding: 0; border: none; border-radius: 6px;
    background: none; color: var(--ink-soft); cursor: pointer; opacity: .55; transition: opacity .12s, background .12s;
}
.tasks-panel-row:hover .tasks-panel-open { opacity: 1; }
.tasks-panel-open:hover { background: var(--card); color: var(--ink); }
.tasks-panel-open svg { width: 14px; height: 14px; }

/* ── Mini-calendar (calendar view only): navigable month picker in the left panel ─── */
.kmini { padding: .25rem .5rem .75rem; margin-bottom: .5rem; border-bottom: 1px solid var(--line); }
.kmini-head {
    display: flex; align-items: center; justify-content: space-between; gap: .25rem;
    padding: .25rem .125rem .5rem;
}
.kmini-title {
    flex: 1 1 auto; text-align: center; text-transform: capitalize;
    font-size: .82rem; font-weight: 600; color: var(--ink);
}
.kmini-nav {
    width: 24px; height: 24px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    border: none; border-radius: 7px; background: none;
    color: var(--ink-soft); cursor: pointer; transition: background .12s, color .12s;
}
.kmini-nav:hover { background: var(--paper); color: var(--ink); }
.kmini-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 1px; }
.kmini-wd {
    text-align: center; padding: .15rem 0 .25rem;
    font-size: .62rem; font-weight: 600; text-transform: uppercase; color: var(--ink-soft);
}
.kmini-day {
    aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center;
    border: none; border-radius: 6px; background: none;
    font-size: .72rem; color: var(--ink); cursor: pointer;
    transition: background .1s, color .1s;
}
.kmini-day:hover { background: var(--paper); }
.kmini-day.out   { color: var(--ink-soft); opacity: .45; }
.kmini-day.range { background: var(--human-soft); color: var(--human-text); border-radius: 0; }
.kmini-day.range:hover { background: var(--human-soft); filter: brightness(.97); }
/* Festive days (weekends + holidays) — tinted; no name in the mini grid. Declared after .range so a
   festive day inside the selected period wins over the band, but before .today so the today dot wins. */
.kmini-day.holiday { background: var(--alert-soft); color: var(--alert-text); }
.kmini-day.holiday:hover { background: var(--alert-soft); filter: brightness(.97); }
/* Today is a fixed green dot (never moves); the light band marks the selected period. */
.kmini-day.today { background: var(--human); color: #fff; font-weight: 700; border-radius: 50%; }
.kmini-day.today:hover { background: var(--human); }

/* ── Action row: quick add + the current view's own buttons (KBW-0152) ─────────
   One row above the view. Desktop shows the inline quick-add form and whatever the view projects
   into `tasks-view-actions` (the Gantt's Legend); the phone rules further down swap the form for a
   button and let the view add its Period button, giving three buttons on a single row. */
.tasks-actionbar {
    display: flex; gap: .5rem; align-items: center;
    margin-bottom: .75rem; flex: 0 0 auto;
}
.tasks-add {
    display: flex; gap: .5rem; align-items: center;
    flex: 1 1 auto; min-width: 0;
}
.tasks-add .k-field { flex: 1 1 auto; }
.tasks-add-type { flex: 0 0 auto; width: auto; }

/* Phone stand-in for the inline form — hidden wherever the form itself is shown. */
.tasks-add-btn { display: none; }
.tasks-add-btn svg, .gff-act svg { width: 15px; height: 15px; flex: 0 0 auto; }
.tasks-add-btn, .gff-act { align-items: center; gap: .35rem; }

/* View pulldown — the phone's replacement for the segmented switcher (hidden on desktop). */
.tasks-viewsel { display: none; width: auto; flex: 0 0 auto; }
.tasks-titlebox { display: flex; align-items: center; gap: .5rem; min-width: 0; }

/* The old page subtitle, now only in the empty-state placeholders. */
.tasks-empty-sub { margin: .35rem 0 0; font-size: .85rem; color: var(--ink-soft); }

/* ── Active-filter chips (removable summary of the current filter, above the view) ── */
.tasks-chips {
    display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
    margin-bottom: .6rem; flex: 0 0 auto;
}
.tasks-chips-label {
    font-size: .72rem; font-weight: 600; color: var(--ink-soft);
}
.tasks-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .2rem .5rem; border-radius: 999px;
    background: var(--human-soft); color: var(--human-text);
    border: 1px solid color-mix(in srgb, var(--human) 25%, transparent);
    font-size: .78rem; font-weight: 500; cursor: pointer;
    transition: background .12s, border-color .12s;
}
.tasks-chip:hover { background: color-mix(in srgb, var(--human-soft) 80%, var(--human)); }
.tasks-chip .k-id-chip { background: transparent; }
.tasks-chip-txt { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tasks-chip-x { font-size: 1rem; line-height: 1; opacity: .6; }
.tasks-chip:hover .tasks-chip-x { opacity: 1; }

/* ── List ──────────────────────────────────────────────────────────────────── */
.tasks-empty {
    display: flex; flex-direction: column; align-items: center; gap: .75rem;
    padding: 3rem 1rem; color: var(--ink-soft);
}
.tasks-list { display: flex; flex-direction: column; gap: .35rem; overflow-y: auto; padding-bottom: 1rem; }

.tasks-group-label {
    display: flex; align-items: center; gap: .4rem;
    font-size: .72rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
    color: var(--ink-soft); margin: .9rem 0 .25rem;
}

.tasks-row {
    display: flex; align-items: center; gap: .65rem;
    padding: .5rem .65rem;
    border: 1px solid var(--line); border-radius: 10px;
    background: var(--card);
}
.tasks-row:hover { border-color: var(--ink-soft); }
.tasks-row.done .tasks-row-title { text-decoration: line-through; color: var(--ink-soft); opacity: .5; }

.tasks-check {
    flex: 0 0 auto; width: 20px; height: 20px;
    border: 1.5px solid var(--line); border-radius: 50%;
    background: var(--paper); color: transparent;
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; transition: all .12s;
}
.tasks-check:hover { border-color: var(--focus); }
.tasks-check.on { background: var(--focus); border-color: var(--focus); color: #fff; }

/* Id chip sits INLINE before the title (there's room from tablet up); the description wraps to its
   own line below. On a row, the chip keeps its intrinsic width instead of stretching to the column. */
.tasks-row-main { display: flex; flex-flow: row wrap; align-items: baseline; column-gap: .4rem; row-gap: .1rem; min-width: 0; flex: 1 1 auto; }
.tasks-row-title { flex: 1 1 auto; min-width: 0; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
.tasks-row-desc { flex: 1 1 100%; font-size: .78rem; color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tasks-row-meta { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
.tasks-status-sel { width: auto; }

.tasks-del {
    flex: 0 0 auto; border: none; background: none; color: var(--ink-soft);
    cursor: pointer; padding: .2rem; border-radius: 6px; line-height: 0;
}
.tasks-del:hover { color: var(--alert-text); background: var(--alert-soft); }

/* ── View switcher: rail icons recycled into the segmented control ───────────── */
.tasks-viewseg button { display: inline-flex; align-items: center; gap: .35rem; }
.tasks-viewseg button svg {
    width: 15px; height: 15px;
    stroke: currentColor; fill: none; stroke-width: 1.8;
    stroke-linecap: round; stroke-linejoin: round;
}

/* ── Refresh button (header, shared by every view) ────────────────────────────
   Icon-only ghost button; the icon spins while a reload is in flight. */
.tasks-refresh { display: inline-flex; align-items: center; justify-content: center; padding: .35rem .5rem; }
.tasks-refresh svg { width: 16px; height: 16px; display: block; }
.tasks-refresh.loading svg { animation: k-spin .7s linear infinite; }
.tasks-refresh:disabled { opacity: .6; cursor: default; }

/* ── Board / Kanban (02_TaskDomain §11) ──────────────────────────────────────
   Columns = the native state machine (todo/doing/done). Cards are actionable tasks;
   containers (project/phase) are the grouping axis, not cards. HTML5 drag-and-drop. */
.kboard {
    display: flex; gap: .75rem; align-items: flex-start;
    overflow-x: auto; overflow-y: hidden; padding-bottom: 1rem;
    flex: 1 1 auto; min-height: 0;
}
.kcol {
    flex: 1 1 0; min-width: 240px; max-width: 380px;
    display: flex; flex-direction: column; min-height: 0; max-height: 100%;
    background: var(--paper); border: 1px solid var(--line); border-radius: 12px;
    transition: border-color .12s, background .12s;
}
/* Active drop target while dragging a card over the column. */
.kcol.drag-over { border-color: var(--focus); background: var(--focus-soft); }

.kcol-head {
    display: flex; align-items: center; gap: .5rem;
    padding: .6rem .75rem; border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.kcol-title {
    font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .04em;
    color: var(--ink-soft);
}

.kcol-body {
    display: flex; flex-direction: column; gap: .45rem;
    padding: .6rem; overflow-y: auto; flex: 1 1 auto; min-height: 2.5rem;
}

/* Cards carry a type-coloured left edge (green task / amber milestone / focus done) so the board
   reads at a glance — see Kibo-Docs/2026_v2 demo `.card`. */
.kcard {
    border: 1px solid var(--line); border-left: 4px solid var(--human); border-radius: 12px;
    background: var(--card); padding: .6rem .7rem;
    cursor: pointer; transition: border-color .12s, box-shadow .12s, opacity .12s;
}
.kcard:hover { border-color: var(--ink-soft); box-shadow: var(--shadow); }
.kcard.dragging { opacity: .45; }
.kcard.ms   { border-left-color: var(--amber); background: var(--amber-soft); }
.kcard.done { border-left-color: var(--focus); }
.kcard.done .kcard-title { text-decoration: line-through; color: var(--ink-soft); opacity: .5; }

.kcard-title {
    font-weight: 600; color: var(--ink); font-size: .88rem; line-height: 1.3;
    overflow: hidden; text-overflow: ellipsis; display: -webkit-box;
    -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;
}
.kcard-desc {
    font-size: .78rem; color: var(--ink-soft); margin-top: .25rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kcard-meta { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; margin-top: .45rem; }

@media (max-width: 640px) {
    /* One column near-fills the screen; the ~22vw sliver of the next column is the "more columns"
       affordance, and scroll-snap parks each column cleanly instead of stopping mid-scroll (§7). */
    .kboard { scroll-snap-type: x mandatory; scroll-padding-left: .75rem; }
    .kcol { min-width: 78vw; scroll-snap-align: start; }
    /* While dragging a card, drop the snap so the board can auto-scroll to another column and the
       card can be released there — mandatory snap otherwise pins the view to the current column. */
    .kboard.dragging { scroll-snap-type: none; }
}

/* ── Gantt (Frappe vendored — lib/frappe-gantt) ───────────────────────────────
   Frappe lacks a left task column; we render one (.gff-side) and keep it vertically
   in sync with the chart's scroll from JS. Tokens are re-mapped onto Frappe's CSS
   vars so the vendored chart matches the app instead of looking bolted-on. */
.gantt-frappe-wrap {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    /* Phase-row tint. Defined on the wrap (not the host) because the left name pane (.gff-side) is a
       SIBLING of .gantt-frappe-host, not a descendant — a var scoped to the host wouldn't reach it, and
       neither would a `.gantt-frappe-host .gff-side-row` selector. Faint/translucent so the month shade
       and weekend tint still show through on the grid side. */
    --gff-phase-band: color-mix(in srgb, var(--agent) 14%, transparent);
}
.gantt-frappe { position: relative; flex: 1 1 auto; min-height: 0; display: flex; align-items: stretch; }

/* Load-failure notice. Sits ON TOP of the chart rather than replacing it: the renderer needs the
   markup underneath to stay mounted (see the comment in GanttFrappeView.razor). */
.gff-error-overlay {
    position: absolute;
    inset: 0;
    z-index: 5;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    text-align: center;
    padding: 1rem;
    background: color-mix(in srgb, var(--card) 88%, transparent);
}

.gff-error-detail {
    max-width: 90%;
    max-height: 40%;
    overflow: auto;
    text-align: left;
    font-size: .75rem;
    white-space: pre-wrap;
    color: var(--alert);
}

/* Gantt toolbar — zoom (always) + range (desktop). */
.gff-toolbar { display: flex; align-items: center; gap: .6rem; padding: 0 .15rem .5rem; flex-wrap: wrap; }
.gff-toolbar .k-btn.on { background: var(--human); color: #fff; }
/* Pushes zoom + range to the right edge of the toolbar. */
.gff-toolbar-spacer { flex: 1 1 auto; }

/* Buttons the Gantt projects into the page action row. Period is phone-only (desktop keeps the
   range inline just below); the legend button is shown everywhere. */
.gff-act { display: inline-flex; flex: 0 0 auto; }
.gff-act-period { display: none; }

/* Period modal body: the same three selects, stacked with their own labels instead of squeezed
   into one inline sentence. */
.gff-range-modal { display: flex; gap: .5rem; flex-wrap: wrap; }
.gff-range-field { display: flex; flex-direction: column; gap: .2rem; flex: 1 1 6rem; }
.gff-range-field > span { font-size: .72rem; font-weight: 600; color: var(--ink-soft); }

/* Zoom segmented (Day / Week / Month): a single pill with one active segment. */
.gff-zoom { display: inline-flex; border: 1px solid var(--line); border-radius: 7px; overflow: hidden; }
.gff-zoom-btn {
    border: none; background: var(--card); color: var(--ink-soft);
    font-size: .78rem; padding: .28rem .6rem; cursor: pointer;
    border-left: 1px solid var(--line);
}
.gff-zoom-btn:first-child { border-left: none; }
.gff-zoom-btn:hover { background: var(--card-2, var(--card)); color: var(--ink); }
.gff-zoom-btn.on { background: var(--human); color: #fff; }

/* Range window: N months from month/year + Apply. */
.gff-range { display: inline-flex; align-items: center; gap: .35rem; font-size: .78rem; color: var(--ink-soft); }
.gff-range-sel {
    border: 1px solid var(--line); border-radius: 6px; background: var(--card); color: var(--ink);
    font-size: .78rem; padding: .22rem .35rem; cursor: pointer;
}
.gff-range-lbl { white-space: nowrap; }

/* Type legend — a swatch per task type so the bar colours read. Lives inside its own modal now
   (KBW-0152), so it stacks one entry per line rather than running across the top of the chart. */
.gff-legend {
    display: flex; flex-direction: column; gap: .55rem;
    font-size: .82rem; color: var(--ink);
}
.gff-legend span { display: inline-flex; align-items: center; gap: .4rem; }
.gff-legend .lg-dot { width: 12px; height: 12px; border-radius: 4px; flex: 0 0 auto; }
/* Task bar track is grey by default, with a green progress fill — so the legend dot mixes both:
   a grey base with a green left half reading as "done part green / remaining grey". The grey is
   inlined here (the --gf-track var is scoped to .gantt-frappe-host, out of this legend's reach). */
.gff-legend .lg-dot.task      { background: linear-gradient(90deg, var(--human) 0 50%, color-mix(in srgb, var(--ink-soft) 28%, var(--card)) 50% 100%); border: 1.5px solid var(--ink-soft); }
.gff-legend .lg-dot.late      { background: linear-gradient(90deg, var(--human) 0 50%, var(--alert) 50% 100%); border: 1.5px solid var(--alert); }
/* Half-filled to mirror the rollup fill inside the bracket (solid = 100%). */
.gff-legend .lg-dot.phase     { background: linear-gradient(90deg, var(--agent) 50%, var(--agent-soft) 50%); border: 1.5px solid var(--agent); }
.gff-legend .lg-dot.milestone { background: var(--amber-soft); border: 1.5px solid var(--amber); border-radius: 2px; transform: rotate(45deg); }
.gff-legend .lg-dot.done      { background: var(--focus-soft); border: 1.5px solid var(--focus); }
/* Left task-name pane: header spacer + a translated rows list (scroll-synced in JS). */
.gff-side {
    /* Width is a custom property, not an inline flex-basis: inline styles beat media queries,
       so the persisted desktop width would have overridden the narrow phone pane (KBW-0034). */
    flex: 0 0 var(--gff-side-w, 210px); position: relative; overflow: hidden;
    background: var(--card); border: 1px solid var(--line); border-right: none;
    border-radius: 12px 0 0 12px;
}
.gff-side-head {
    height: var(--gff-head-h, 50px); position: relative; z-index: 2;
    background: var(--paper); border-bottom: 1px solid var(--line);
}
.gff-side-rows { will-change: transform; }
.gff-side-row {
    /* Fallback matches Frappe's row stride (bar_height 18 + padding 6 = 24). The JS sets the
       real value; keeping the default in sync means a miss degrades to aligned, not to 38px. */
    height: var(--gff-row-h, 24px); box-sizing: border-box;
    display: flex; align-items: center; gap: .35rem;
    /* KBW-0120: .5rem per level, and no base gutter. The pane is narrow and the indent was eating it —
       at depth 3 over half the width was gutter, leaving nothing for the title. Top-level rows sit at
       0 deliberately: KiboKebab leads EVERY row and brings its own padding, so the breathing room is
       already there and adding more would just be paying for it twice. */
    padding: 0 .5rem 0 calc(var(--depth, 1) * .5rem);
    border-bottom: 1px solid var(--line); cursor: pointer;
}
.gff-side-row:hover { background: var(--paper); }
.gff-side-row.container { font-weight: 600; }
/* drag-to-reparent: highlight the container row a task is being dropped onto */
.gff-side-row.droptarget { background: var(--human-soft); outline: 2px dashed var(--human); outline-offset: -2px; border-radius: 4px; }
/* Completed leaf: grey + struck-through name (group C — matches the kanban "done" treatment). */
.gff-side-row.done .gff-side-txt { color: var(--ink-soft); text-decoration: line-through; opacity: .5; }
.gff-side-txt { font-size: var(--gff-side-font, .82rem); color: var(--ink); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Assignee avatar pushed to the row's right edge (demo kibo-refactor-5 .grow38 .av). */
.gff-side-av { flex: 0 0 auto; margin-left: auto; display: inline-flex; line-height: 0; }
.gff-side-toggle {
    flex: 0 0 auto; width: 16px; height: 16px; padding: 0; border: none; background: none;
    color: var(--ink-soft); cursor: pointer; line-height: 0; border-radius: 4px;
    display: inline-flex; align-items: center; justify-content: center;
    transition: transform .12s, color .12s;
}
.gff-side-toggle:hover { color: var(--ink); }
.gff-side-toggle.open { transform: rotate(90deg); }

/* Touch surfaces (KBW-0096): the pane keeps a fixed width and scrolls horizontally to reveal long
   titles. This must come BEFORE the phone rule below, so 640px-and-under still wins with 108px.
   flex-basis is pinned here on purpose: it discards the resizer width persisted from the desktop
   (KBW-0034, up to 600px), which on a tablet would leave almost no room for the chart — and with the
   horizontal scroll there's nothing to gain from a wider pane anyway.
   Vertical scroll stays driven by the JS transform on .gff-side-rows, so overflow-y stays hidden. */
@media (pointer: coarse) {
    .gff-side { flex-basis: 210px; overflow-x: auto; overflow-y: hidden; }
    /* Every row must be as wide as the WIDEST row, not as wide as its own text. `min-width: 100%` on
       a row resolves against the pane's VISIBLE width, so a short title stopped there: its background
       didn't reach the scrolled area (obvious on the violet phase rows) and the empty space to the
       right of the title wasn't part of the row, so it wasn't clickable either.
       Sizing the rows CONTAINER to max-content gives them one shared width to fill. */
    .gff-side-rows { width: max-content; min-width: 100%; }
    .gff-side-row  { width: max-content; min-width: 100%; }
    /* The head is empty, so max-content is 0 and it could never stretch — it just scrolled out of
       view. Pin it to the left edge so it stays over the visible area, like a frozen column header. */
    .gff-side-head { position: sticky; left: 0; }
    .gff-side-txt { overflow: visible; text-overflow: clip; }
}

/* Phones (§14.3): halve the pane so the chart keeps most of the screen. */
@media (max-width: 640px) {
    .gff-side { flex-basis: 108px; }
}

.gantt-frappe-host {
    flex: 1 1 auto; min-width: 0; overflow: hidden;
    border: 1px solid var(--line); border-radius: 0 12px 12px 0;
    /* Frappe reads these CSS vars (frappe-gantt.css :root); scope them to our instance.
       Group C: leaf task bars read "grey track + green completed part". The track (the whole bar)
       is a neutral grey; the progress overlay (the done part) is solid green. An overdue open leaf
       swaps its track to red via .gf-late below. */
    --gf-track: color-mix(in srgb, var(--ink-soft) 28%, var(--card));
    --gf-track-border: var(--ink-soft);
    --g-bar-color: var(--gf-track);
    --g-bar-border: var(--gf-track-border);
    --g-progress-color: var(--human);
    --g-tick-color: var(--line);
    --g-tick-color-thick: var(--line);
    --g-border-color: var(--line);
    --g-text-dark: var(--ink);
    --g-text-light: #fff;
    --g-text-muted: var(--ink-soft);
    --g-arrow-color: var(--ink-soft);
    --g-today-highlight: var(--human);
    /* Make the per-row fill transparent so the alternating month bands (drawn behind everything in
       gantt_frappe.js) show through; the container background below is the base (odd months). */
    --g-row-color: transparent;
    /* Weekend shading: a subtle neutral ink overlay (darkens the month band beneath, both themes),
       matching the calendar's soft weekend tint rather than Frappe's flat grey default. */
    --g-weekend-highlight-color: color-mix(in srgb, var(--ink) 6%, transparent);
    /* Even calendar months (Feb, Apr, …) get this faint shade; odd months stay on --card. */
    --gff-month-shade: var(--paper);
}
/* Single internal scroller; names live in the left pane, so hide the on-bar labels. */
.gantt-frappe-host .gantt-container { height: 100% !important; border-radius: 0; background: var(--card); }
.gantt-frappe-host .gff-month-band { fill: var(--gff-month-shade); }
.gantt-frappe-host .bar-label { display: none; }

/* Date-header text size driven by the wrapper's CONFIG (--gff-font). The extra .gantt-container in
   the selector lifts specificity above Frappe's own `.gantt-container .upper-text` (0,2,0). */
.gantt-frappe-host .gantt-container .upper-text,
.gantt-frappe-host .gantt-container .lower-text { font-size: var(--gff-font, 12px); }

/* Header/grid horizontal drift on scroll: the last month's .upper-text (width:fit-content) is placed
   at its x and its tail overflows past the grid width, so the sticky .grid-header ends up wider than
   the SVG body. That width delta desyncs the day labels from the columns, growing as you scroll away
   from the start. Clip the header's horizontal overflow so its width matches the grid. `clip` (not
   `hidden`) creates no scroll container, so it spawns no scrollbar and leaves overflow-y:visible — the
   header's own top:0 sticky is untouched. The trailing month name that gets cut is redundant: the
   current month stays readable via .current-upper (sticky left:0).
   Fallback first: iOS Safari < 16 has no `clip`, so it falls back to `hidden` (safe here — the header
   is a fixed height with no vertical overflow, so the auto overflow-y it implies spawns no scrollbar). */
.gantt-frappe-host .gantt-container .grid-header {
    overflow-x: hidden;
    overflow-x: clip;
}

/* Per-type bar colours via the custom_class set in GanttFrappeView.
   NB: Frappe's own rule is `.gantt .bar-wrapper .bar` (specificity 0,3,0). Matching that with
   `.host .gf-x .bar` only ties — and Frappe's sheet loads last, so it would win and every bar
   stays green. Using `.bar-wrapper.gf-x` (compound) lifts us to 0,4,0 so our colours actually apply. */
/* No gf-project bar colour: a project is always top-level (KBW-0073), so it never appears as a bar
   inside a project-scoped Gantt. The lavender that used to mark projects now bands the phase rows. */
.gantt-frappe-host .bar-wrapper.gf-milestone .bar { fill: var(--amber); stroke: var(--amber); }

/* Milestone diamond: the bar is shrunk to a centred square in JS; rotate it 45° here. transform-box
   fill-box makes transform-origin relative to the bar's own box, so the pivot tracks the bar during
   a move-drag (a fixed-pivot SVG rotate would skew it diagonally). */
.gantt-frappe-host .bar-wrapper.gf-milestone .bar {
    transform-box: fill-box; transform-origin: center; transform: rotate(45deg);
}
/* A milestone is a single day, never a span — strip its resize handles so it can't be stretched. */
.gantt-frappe-host .bar-wrapper.gf-milestone .handle.left,
.gantt-frappe-host .bar-wrapper.gf-milestone .handle.right { display: none; }
/* Overdue open leaf: paint the track red; the green progress overlay (set by --g-progress-color)
   stays, so the bar reads "green = done, red = the part still owed and already late". */
.gantt-frappe-host .bar-wrapper.gf-late .bar { fill: var(--alert); stroke: var(--alert); }
/* Done leaf tasks switch to the focus palette (track + progress) to match the legend. */
.gantt-frappe-host .bar-wrapper.gf-done .bar          { fill: var(--focus-soft); stroke: var(--focus); }
.gantt-frappe-host .bar-wrapper.gf-done .bar-progress { fill: var(--focus); }
.gantt-frappe-host .bar-wrapper.gf-milestone .bar-progress { display: none; }

/* Phase = a horizontal square bracket (drawn in JS), not a filled bar. Frappe's base .bar also
   carries fill + outline + radius, so clear them all or a faint task-style rectangle shows through. */
.gantt-frappe-host .bar-wrapper.gf-phase .bar { fill: transparent; stroke: none; outline: none; }
/* Phase row band: a full-width lavender rect drawn behind the bars in gantt_frappe.js (paintPhaseBands),
   plus the matching left-pane row tint below — so a phase reads as one continuous section divider
   across both panes. Faint/translucent so month shade + weekend tint still show through. */
.gantt-frappe-host .gff-phase-band { fill: var(--gff-phase-band); }
/* Left name pane is a sibling of .gantt-frappe-host (not inside it), so this stays unscoped. */
.gff-side-row.phase { background: var(--gff-phase-band); }
/* Rollup completion inside the bracket. The rect is re-sized and re-positioned in gantt_frappe.js
   (it has to be: the y offset depends on the row, which only the JS knows) so it sits between the
   bracket's top stroke and the tips of its arms — visible without covering the bracket itself.
   The value is ProgressFor()'s average over the leaf descendants. */
.gantt-frappe-host .bar-wrapper.gf-phase .bar-progress { fill: var(--agent); opacity: .5; }
/* Solid ONLY at 100%: on a long phase a 98% fill is visually indistinguishable from a full one, so
   completion is carried by the opacity step rather than by the last 2% of width. */
.gantt-frappe-host .bar-wrapper.gf-phase.gf-full .bar-progress { opacity: 1; }
/* Expected-progress stays hidden: a second overlay in that thin strip is unreadable. */
.gantt-frappe-host .bar-wrapper.gf-phase .bar-expected-progress { display: none; }

/* Progress handle (drag the % on the light bar): leaf tasks only — hide it on containers/milestones. */
.gantt-frappe-host .bar-wrapper.gf-phase     .handle.progress,
.gantt-frappe-host .bar-wrapper.gf-project   .handle.progress,
.gantt-frappe-host .bar-wrapper.gf-milestone .handle.progress { display: none; }
.gantt-frappe-host .gf-phase-bracket { fill: none; stroke: var(--ink); stroke-width: 2.5; pointer-events: none; }

/* ── Calendar (02_TaskDomain §11) ─────────────────────────────────────────────
   Month grid. Each dated task is drawn as one pastel chip PER day it covers (rounded only on its
   real start/end, squared where it continues), lane-stacked so none overlap (CSS grid: column = day,
   row = lane). One soft colour per type — reads as a calendar, not a Gantt. */
.kcal {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
    border: 1px solid var(--line); border-radius: 12px; background: var(--paper); overflow: hidden;
}

.kcal-head {
    display: flex; align-items: center; gap: .75rem; flex: 0 0 auto;
    padding: .5rem .65rem; border-bottom: 1px solid var(--line); background: var(--paper);
}
.kcal-nav { display: flex; align-items: center; gap: .5rem; }
.kcal-title {
    min-width: 9.5rem; text-align: center; text-transform: capitalize;
    font-weight: 600; color: var(--ink);
}
.kcal-navbtn {
    width: 32px; height: 32px; flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--line); border-radius: 8px; background: var(--card);
    color: var(--ink-soft); cursor: pointer; transition: border-color .12s, color .12s;
}
.kcal-navbtn:hover { border-color: var(--ink); color: var(--ink); }

/* All header controls (Today / arrows / mode) share one height so they read as a single row of
   equal-sized buttons instead of three different sizes. On touch they all grow to the 44px
   minimum (§12), not just the arrows. */
.kcal-head .k-btn { height: 32px; box-sizing: border-box; display: inline-flex; align-items: center; padding: 0 12px; }
.kcal-modeseg { height: 32px; box-sizing: border-box; align-items: center; }
@media (pointer: coarse) {
    .kcal-navbtn, .kcal-modebtn { width: 44px; height: 44px; }
    .kcal-head .k-btn, .kcal-modeseg { height: 44px; }
}

.kcal-weekdays {
    display: grid; grid-template-columns: repeat(7, 1fr); flex: 0 0 auto;
    column-gap: 6px; padding: 0 6px; background: var(--paper);
}
.kcal-weekdays span {
    padding: .3rem .5rem; text-align: right;
    font-size: .68rem; font-weight: 600; text-transform: uppercase; letter-spacing: .03em;
    color: var(--ink-soft);
}

/* Day cells are separated white cards floating on the paper background (gaps + rounded corners),
   like the demo's month grid — not a continuous lined grid. */
.kcal-grid { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: grid; flex-direction: column; gap: 6px; padding: 6px; }

/* A week row sizes itself to its lanes: day-number band + lanes (≥2 lanes' worth so empty weeks
   still read as a calendar). */
.kcal-week { position: relative; min-height: calc(1.5rem + max(var(--lanes), 2) * 1.4rem + .4rem); }

.kcal-week-bg { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(7, 1fr); column-gap: 6px; }
.kcal-day {
    background: var(--card); border: 1px solid var(--ink-soft); border-radius: 10px;
    padding: .15rem .4rem; text-align: right;   /* right-aligns the day number */
    /* Grid items default to min-width:auto: a nowrap holiday name (.kcal-holname) would otherwise
       stretch the 1fr track and blow out the 7-column layout on narrow phones. Pin min-width:0 so
       the track keeps its share and clip the overflow (the name already ellipsizes). */
    min-width: 0; overflow: hidden;
}
.kcal-day.out { background: transparent; border-color: transparent; }
.kcal-day.out .kcal-daynum { color: var(--ink-soft); opacity: .55; }
.kcal-daynum { font-size: .72rem; font-weight: 600; color: var(--ink); }
.kcal-day.today { border-color: var(--human); }
.kcal-day.today .kcal-daynum {
    display: inline-block; min-width: 1.25rem; height: 1.25rem; line-height: 1.15rem;
    padding: 0 .05rem; border-radius: 50%; text-align: center;
    background: var(--human); color: #fff;
}

/* Holidays + weekends — tint the day cell. Holidays also carry a name label (see .kcal-holname);
   weekends are colour only. */
/* Weekends read as a subtle non-working shade; holidays get a soft alert tint + a named label
   (holiday rule declared after weekend so a holiday falling on a weekend wins). */
.kcal-day.weekend { background: var(--we); }
.kcal-day.holiday { background: var(--alert-soft); }
.kcal-day.weekend.out { background: var(--paper); }
.kcal-day.holiday.out { background: var(--alert-soft); opacity: .55; }
.kcal-holname {
    display: block; text-align: left; margin-top: .05rem;
    font-size: .62rem; font-weight: 600; line-height: 1.1; color: var(--alert-text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Time-grid (day/week) header — holiday name under the date number; weekends shade only. */
.ktime-dh.weekend { background: var(--we); }
.ktime-dh.holiday { background: var(--alert-soft); }
.ktime-dh-hol {
    display: block; font-size: .6rem; font-weight: 600; line-height: 1.05; color: var(--alert-text);
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Bars layer: same 7-column track, rows = lanes. Day-number band cleared with padding-top. */
/* Bars overlay: same 7-col track, gap and edges as the day cells so chips align inside them. */
.kcal-week-bars {
    position: relative; display: grid; grid-template-columns: repeat(7, 1fr);
    grid-auto-rows: 1.4rem; row-gap: .2rem; column-gap: 6px;
    padding: 1.5rem 0 .3rem; pointer-events: none;
}
/* Pastel chips (soft fill + matching text), one colour per type — see Kibo-Docs/2026_v2 demo.
   A solid saturated bar reads as a Gantt; the soft fill reads as a calendar entry. */
.kcal-bar {
    pointer-events: auto; overflow: hidden; min-width: 0; box-sizing: border-box;
    display: flex; align-items: center; gap: .25rem; padding: 0 .45rem;
    height: 1.4rem; border-radius: 7px; cursor: pointer; font-weight: 600;
    background: var(--human-soft); color: var(--human-text); border: 1px solid var(--human);
    transition: filter .12s, box-shadow .12s;
    /* Inset every day's rectangle so it reads as living inside its day cell (clearer day boundaries).
       Whether a task continues into the next/prev day is shown by the squared border (clip-*), not by
       the gap — so all bars keep the same inset. */
    margin: 0 5px;
}
.kcal-bar:hover { filter: brightness(.97); box-shadow: var(--shadow); }
/* Clipped edges (continues into the previous/next day or week) square off that side. */
.kcal-bar.clip-start { border-top-left-radius: 0; border-bottom-left-radius: 0; }
.kcal-bar.clip-end   { border-top-right-radius: 0; border-bottom-right-radius: 0; }
/* Tasks falling on the grid's leading/trailing days (adjacent months) are dimmed so they don't read
   as belonging to the month on screen. */
.kcal-bar.out { opacity: .3; }
.kcal-bar.project   { background: var(--agent-soft); color: var(--agent-text); border-color: var(--agent); }
.kcal-bar.phase     { background: var(--agent-soft); color: var(--agent-text); border-style: dashed; border-color: var(--agent); }
.kcal-bar.milestone { background: var(--amber-soft); color: var(--amber-text); border-color: var(--amber); }
.kcal-bar.done      { background: var(--focus-soft); color: var(--focus-text); border-color: var(--focus); }
.kcal-bar.approval  { background: var(--human-soft, rgba(124,77,255,.12)); color: var(--human-text, #5e35b1); border-color: var(--human, #7c4dff); border-style: dashed; }
.kcal-bar-txt { flex: 1 1 auto; font-size: .72rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Inline approve/reject buttons on a pending approval bar (item 2). */
.kcal-appr { display: inline-flex; gap: .15rem; margin-left: .2rem; }
.kcal-appr button { border: none; border-radius: 4px; width: 16px; height: 16px; line-height: 1; font-size: .68rem; cursor: pointer; padding: 0; }
.kcal-appr-yes { background: var(--ok, #2e7d32); color: #fff; }
.kcal-appr-no  { background: var(--alert, #c62828); color: #fff; }
.kcal-diamond {
    flex: 0 0 auto; width: 9px; height: 9px;
    background: var(--amber-text); transform: rotate(45deg); border-radius: 2px;
}

/* ── Calendar mode selector (Day / Work week / Week / Month) ─────────────────── */
.kcal-modeseg {
    margin-left: auto; display: inline-flex; gap: 2px; padding: 2px;
    background: var(--card); border: 1px solid var(--line); border-radius: 9px;
}
.kcal-modeseg button {
    border: none; background: none; cursor: pointer;
    padding: .3rem .6rem; border-radius: 7px; font-size: .8rem; color: var(--ink-soft);
    transition: background .12s, color .12s;
}
.kcal-modeseg button:hover { color: var(--ink); }
.kcal-modeseg button.on { background: var(--human-soft); color: var(--human-text); font-weight: 600; }

/* Compact calendar mode picker (phones only): a square icon button that opens a pulldown, shown in
   place of the segmented control which runs off-screen on a narrow header. */
.kcal-modemenu { position: relative; margin-left: auto; display: none; }
.kcal-modebtn {
    width: 32px; height: 32px; padding: 0; display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--line); border-radius: 9px; background: var(--card); color: var(--ink-soft); cursor: pointer;
}
.kcal-modebtn svg { width: 20px; height: 20px; }
.kcal-modemenu-backdrop { position: fixed; inset: 0; z-index: 40; }
.kcal-modemenu-pop {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 41;
    display: flex; flex-direction: column; gap: 2px; padding: 4px; min-width: 160px;
    background: var(--card); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow);
}
.kcal-modemenu-pop button {
    display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;
    border: none; background: none; cursor: pointer;
    padding: .45rem .5rem; border-radius: 7px; font-size: .85rem; color: var(--ink-soft);
}
.kcal-modemenu-pop button svg { width: 18px; height: 18px; flex: 0 0 auto; }
.kcal-modemenu-pop button:hover { background: var(--paper); color: var(--ink); }
.kcal-modemenu-pop button.on { color: var(--human-text); font-weight: 600; }
@media (max-width: 640px) {
    .kcal-modeseg { display: none; }
    .kcal-modemenu { display: block; }
    /* Reclaim header width so Today + arrows + the mode button all fit. */
    .kcal-head { gap: .4rem; padding: .45rem .5rem; }
    .kcal-title { min-width: 0; font-size: .9rem; }
}

/* ── Rich per-type popover (§14.4) ─────────────────────────────────────────────
   A single tap on a bar when the dock is closed peeks the task's key facts. Centred dialog on
   desktop; bottom-sheet on phones (where this is the primary path). The backdrop dismisses it. */
.kcal-pop-backdrop { position: fixed; inset: 0; z-index: 110; background: transparent; }
.kcal-pop {
    position: fixed; z-index: 111;
    left: 50%; top: 50%; transform: translate(-50%, -50%);
    width: min(340px, 92vw); max-height: 82dvh; overflow-y: auto;
    background: var(--card); border: 1px solid var(--line); border-radius: 14px;
    box-shadow: var(--shadow-md); padding: .85rem .95rem;
    display: flex; flex-direction: column; gap: .55rem;
}
.kcal-pop-head { display: flex; align-items: center; gap: .4rem; }
.kcal-pop-x {
    margin-left: auto; width: 26px; height: 26px; padding: 0; line-height: 1;
    border: none; background: none; color: var(--ink-soft); cursor: pointer; border-radius: 7px;
    font-size: .9rem;
}
.kcal-pop-x:hover { background: var(--paper); color: var(--ink); }
.kcal-pop-title { margin: 0; font-size: 1rem; font-weight: 600; color: var(--ink); line-height: 1.3; }
.kcal-pop-title.done { text-decoration: line-through; color: var(--ink-soft); opacity: .5; }

.kcal-pop-rows { margin: 0; display: flex; flex-direction: column; gap: .35rem; }
.kcal-pop-rows > div { display: grid; grid-template-columns: 84px 1fr; gap: .5rem; align-items: baseline; }
.kcal-pop-rows dt { font-size: .74rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .03em; }
.kcal-pop-rows dd { margin: 0; font-size: .84rem; color: var(--ink); }
.kcal-pop-who { display: flex; align-items: center; gap: .4rem; }
.kcal-pop-prio { display: flex; align-items: center; gap: .4rem; }
.kcal-pop-prog { display: flex; align-items: center; gap: .45rem; }
.kcal-pop-track { width: 120px; height: 6px; border-radius: 3px; background: var(--line); overflow: hidden; }
.kcal-pop-track > span { display: block; height: 100%; background: var(--focus); }
.kcal-pop-prog > em { font-style: normal; font-size: .78rem; color: var(--ink-soft); }
.kcal-pop-desc {
    margin: 0; font-size: .82rem; color: var(--ink-soft); line-height: 1.4;
    white-space: pre-wrap; max-height: 5.6em; overflow: hidden;
}
.kcal-pop-foot { display: flex; justify-content: flex-end; }

/* On phones it stays a centred modal (like the Memory Map settings modal), just with a dimmed
   scrim behind it instead of the desktop click-away transparent backdrop. */
@media (max-width: 640px) {
    .kcal-pop-backdrop { background: var(--scrim); }
}

/* ── Time grid (Day / Work week / Week) — hours × days ──────────────────────── */
.kcal-time .ktime {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    --hour: 44px; --gut: 54px;
}
/* Day headers + all-day band stay pinned while the hour grid scrolls under them. */
.ktime-top {
    position: sticky; top: 0; z-index: 3;
    background: var(--card); box-shadow: inset 0 -1px var(--line);
}
.ktime-head, .ktime-grid {
    display: grid; grid-template-columns: var(--gut) repeat(var(--cols), 1fr);
}
/* The all-day band is gutter + one content area; the bars/cells inside re-split into day columns. */
.ktime-allday { display: grid; grid-template-columns: var(--gut) 1fr; }
.ktime-head { border-bottom: 1px solid var(--line); }
.ktime-gutcell { border-right: 1px solid var(--line); }
.ktime-dh {
    display: flex; flex-direction: column; align-items: center; gap: 1px;
    padding: .35rem .25rem; text-align: center; border-right: 1px solid var(--line);
}
.ktime-dh:last-child { border-right: none; }
.ktime-dh-wd {
    font-size: .62rem; font-weight: 600; text-transform: uppercase;
    letter-spacing: .03em; color: var(--ink-soft);
}
.ktime-dh-num { font-size: 1.05rem; font-weight: 600; color: var(--ink); }
.ktime-dh.today .ktime-dh-num {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.6rem; height: 1.6rem; border-radius: 50%; background: var(--human); color: #fff;
}

.ktime-allday { border-bottom: 1px solid var(--line); }
/* Milestone band — its own labelled row above the all-day band (item 3). */
.ktime-ms { background: var(--amber-soft); }
.ktime-ms .ad-lbl { color: var(--amber-text); font-weight: 600; }
.ktime-gutcell.ad-lbl {
    display: flex; align-items: center; justify-content: flex-end; padding: .2rem .4rem;
    font-size: .6rem; text-transform: uppercase; letter-spacing: .03em; color: var(--ink-soft);
}
/* min-height keeps the all-day band a usable drop target even when it holds no items
   (dragging a timed task here converts it to all-day). */
.ktime-ad-cells { position: relative; min-height: 1.8rem; }
.ktime-ad-bg { position: absolute; inset: 0; display: grid; grid-template-columns: repeat(var(--cols), 1fr); }
.ktime-ad-cell { border-right: 1px solid var(--line); }
.ktime-ad-cell:last-child { border-right: none; }
.ktime-ad-bars {
    position: relative; display: grid; grid-template-columns: repeat(var(--cols), 1fr);
    grid-auto-rows: 1.4rem; row-gap: .15rem; column-gap: 3px; padding: .2rem .25rem;
}

.ktime-gutter { position: relative; border-right: 1px solid var(--line); }
.ktime-hr { height: var(--hour); position: relative; }
.ktime-hr span {
    position: absolute; top: -.55em; right: .4rem; padding: 0 .15rem;
    font-size: .62rem; color: var(--ink-soft); background: var(--card);
}
.ktime-hr:first-child span { top: .1rem; }   /* keep 00:00 from clipping at the very top */

.ktime-col { position: relative; border-right: 1px solid var(--line); }
.ktime-col:last-child { border-right: none; }
.ktime-col.today { background: color-mix(in srgb, var(--human) 6%, transparent); }
.ktime-line { height: var(--hour); border-bottom: 1px solid var(--line); }

/* Timed events: pastel fill + matching border + text (demo `.ev`), one colour per type. */
.ktime-ev {
    position: absolute; box-sizing: border-box; overflow: hidden;
    display: flex; flex-direction: column; line-height: 1.15;
    padding: 2px 6px; border-radius: 9px; min-height: 14px; cursor: pointer;
    background: var(--human-soft); color: var(--human-text); border: 1.5px solid var(--human);
    transition: filter .12s, box-shadow .12s;
}
.ktime-ev:hover { filter: brightness(.97); box-shadow: var(--shadow); z-index: 2; }
.ktime-ev.project { background: var(--agent-soft); color: var(--agent-text); border-color: var(--agent); }
.ktime-ev.phase   { background: var(--agent-soft); color: var(--agent-text); border-style: dashed; border-color: var(--agent); }
.ktime-ev.milestone { background: var(--amber-soft); color: var(--amber-text); border-color: var(--amber); }
.ktime-ev.done    { background: var(--focus-soft); color: var(--focus-text); border-color: var(--focus); }
.ktime-ev-time { font-size: .6rem; font-weight: 600; opacity: .85; }
.ktime-ev-txt  { font-size: .72rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* "Now" indicator line in today's column. */
.ktime-now { position: absolute; left: 0; right: 0; border-top: 2px solid var(--alert); z-index: 2; pointer-events: none; }
.ktime-now::before {
    content: ''; position: absolute; left: -1px; top: -4px;
    width: 7px; height: 7px; border-radius: 50%; background: var(--alert);
}

/* ── Tree nodes (02_TaskDomain §11) ────────────────────────────────────────── */
/* Depth indent is a CSS var set per row; children are flat siblings, indented visually.
   It is a SPACER, not the row's padding: the kebab sits in a fixed leading gutter (KBW-0117), and
   padding would have carried it right along with the depth, leaving the column jagged again. */
.tnode { padding-left: .65rem; }
.tnode-indent { flex: 0 0 calc(var(--depth, 0) * 1.4rem); }
.tnode-container .tasks-row-title { font-weight: 600; }

.tnode-toggle {
    flex: 0 0 auto; width: 20px; height: 20px;
    border: none; background: none; color: var(--ink-soft);
    display: inline-flex; align-items: center; justify-content: center;
    cursor: pointer; border-radius: 6px; line-height: 0;
    transition: transform .12s, background .12s, color .12s;
}
.tnode-toggle:hover { background: var(--paper); color: var(--ink); }
.tnode-toggle.open { transform: rotate(90deg); }

/* Milestone marker — a diamond, not a checkbox (it's a point in time, not a to-do). */
.tnode-diamond {
    flex: 0 0 auto; width: 11px; height: 11px; margin: 4px 4px 4px 5px;
    background: var(--amber-text); transform: rotate(45deg); border-radius: 2px;
}
/* Empty container (project/phase with no children): keep the check column aligned. */
.tnode-spacer { flex: 0 0 auto; width: 20px; height: 20px; }

.k-badge.phase { background: var(--agent-soft); color: var(--agent-text); }

/* ── Generic field list (EntityFieldList — 10_InteractionModel §4.2) ────────── */
/* Host-agnostic: used by the inspector and the in-chat "form" card. Tokens only. */
.flist { display: flex; flex-direction: column; gap: .55rem; }
.flist-card {
    border: 1px dashed var(--agent);          /* agent = violet/dashed = AI proposal */
    border-radius: 12px; padding: .85rem; background: var(--agent-soft);
}
.flist-row {
    display: grid; grid-template-columns: 7.5rem 1fr; align-items: center; gap: .65rem;
}
.flist-label { font-size: .8rem; color: var(--ink-soft); }
.flist-input { width: 100%; }
.flist-static { color: var(--ink); }
.flist-row textarea.flist-input { min-height: 4.5rem; resize: vertical; }
/* Split date + optional time: date takes the room, time is a compact trailing field. */
.flist-dt { display: flex; gap: .4rem; }
.flist-dt input[type="date"] { flex: 1 1 auto; min-width: 0; }
.flist-dt input[type="time"] { flex: 0 0 6.5rem; }
.flist-actions { display: flex; gap: .5rem; margin-top: .35rem; }

/* ── Slide-over sheet (host for the task inspector injected from chat) ───────── */
.sheet-backdrop { position: fixed; inset: 0; background: var(--scrim); z-index: 40; }
.sheet {
    position: fixed; top: 0; bottom: 0; width: min(440px, 94vw);
    background: var(--paper); z-index: 41; box-shadow: var(--shadow);
    display: flex; flex-direction: column;
}
.sheet-right { right: 0; border-left: 1px solid var(--line); }
.sheet-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: .85rem 1rem; border-bottom: 1px solid var(--line); flex: 0 0 auto;
}
.sheet-title { font-weight: 600; color: var(--ink); }
.sheet-close { border: none; background: none; cursor: pointer; color: var(--ink-soft); font-size: 1rem; line-height: 1; }
.sheet-close:hover { color: var(--ink); }
.sheet-body { padding: 1rem; overflow-y: auto; flex: 1 1 auto; }

/* ── Unified right dock (10_InteractionModel §11) ────────────────────────────
   A panel that slides over the page from the right edge (structure + colours after
   Kibo-Docs/2026_v2/idee/sidebar.html). An edge chevron opens/closes it; a drag handle resizes it.
   Split: top = inspector (scrolls), bottom = side-chat (always half the dock). On mobile it becomes
   a full-screen modal. Custom property --dock-w (set on .task-dock) is inherited by the fixed
   children. */

/* Header height — the dock starts below it; keep in sync with .app-header in chrome.css. */
.task-dock { --dock-top: 52px; }

/* Edge chevron — flush with the panel's inner edge, flips when open. Centred on the panel's span
   (which starts at --dock-top, below the header), not the viewport — so it lines up with the grip.
   Shift down by half the header height. */
/* Right dock tab — shares the .k-dock-tab / .k-dock-tab--right look (shell.css); only its own fixed
   anchor, right-slide animation and z-index live here (KBW-0028). The right offset is set inline
   (tracks the panel width). */
.task-dock-toggle {
    position: fixed;
    top: calc(var(--dock-top) + 12px);   /* just below the header, not mid-screen */
    z-index: 82;
    transition: background .15s, right .3s ease, color .15s;
}
.task-dock-toggle.no-transition { transition: background .15s, color .15s; }
/* Phone/tablet only: sit tight under the header, level with the left drawer handle, so the tab
   overlaps nothing but the head row's reserved gutter (KBW-0152). Desktop keeps its offset. */
@media (max-width: 1024px) {
    .task-dock-toggle { top: 56px; }
}
.task-dock-chevron { transition: transform .3s ease; }
.task-dock.open .task-dock-chevron { transform: rotate(180deg); }

/* The sliding panel itself. */
.task-dock-panel {
    position: fixed;
    top: var(--dock-top); right: 0; bottom: 0;   /* below the app header */
    width: var(--dock-w, 420px);
    z-index: 80;
    display: flex; flex-direction: row;
    background: var(--paper);
    border-left: 1.5px solid var(--line);
    box-shadow: -4px 0 24px rgba(42, 47, 66, .08);
    transform: translateX(100%);
    transition: transform .3s ease;
}
.task-dock.open .task-dock-panel { transform: translateX(0); }

/* Resizer visuals now live in base.css (.k-resizer / .k-resizer-grip / .k-resize-overlay).
   Only the dock-specific hide rules stay here. */

.task-dock-inner {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-direction: column;
    overflow: hidden;
}

/* Top bar: generic title + close. */
.task-dock-bar {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: space-between;
    padding: .6rem .85rem;
    border-bottom: 1px solid var(--line);
}
.task-dock-bar-title { font-weight: 700; color: var(--ink); font-size: .9rem; letter-spacing: -.01em; }
.task-dock-close {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px;
    border: none; background: none; cursor: pointer;
    color: var(--ink-soft); border-radius: 6px;
}
.task-dock-close:hover { color: var(--ink); background: var(--card); }
.task-dock-close svg { width: 18px; height: 18px; }

/* Bar action group: chat show/hide, maximize/restore, close. */
.task-dock-bar-actions { display: flex; align-items: center; gap: .15rem; }
.task-dock-act {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px;
    border: none; background: none; cursor: pointer;
    color: var(--ink-soft); border-radius: 6px;
}
.task-dock-act:hover { color: var(--ink); background: var(--card); }
.task-dock-act.on { color: var(--agent); }
.task-dock-act svg { width: 18px; height: 18px; }

/* Comfortable thumb targets on touch/mobile (KBW-0069). */
@media (max-width: 1024px) {
    .task-dock-close, .task-dock-act { width: 42px; height: 42px; }
    .task-dock-close svg, .task-dock-act svg { width: 22px; height: 22px; }
}

/* Maximized: the dock fills the work area (rail edge → window edge). The resize handle and the edge
   chevron are pointless at full width, so hide them; restore from the bar button. */
.task-dock.maximized .task-dock-panel { left: var(--rail); width: auto; }
.task-dock.maximized .task-dock-resizer,
.task-dock.maximized .task-dock-toggle { display: none; }

/* Side-chat hidden: the inspector takes the whole dock height. */
.task-dock.chat-collapsed .task-dock-chat { display: none; }
.task-dock.chat-collapsed .task-dock-inspector { flex: 1 1 100%; }

/* Chat-only (the task page, 10 §11.3): the inspector half isn't rendered at all, so the chat takes
   the panel. No top border either — there is nothing above it to be divided from. */
.task-dock.chat-only .task-dock-chat { flex: 1 1 100%; border-top: none; }

/* Maximize is meaningless on mobile — the dock is already a full-screen modal there. */
@media (max-width: 720px) { .task-dock-maximize { display: none; } }

/* Top: inspector — scrolls when its content overflows. */
.task-dock-inspector { flex: 1 1 50%; min-height: 0; overflow-y: auto; padding: 1rem; }

/* Bottom: side-chat — always half the dock. */
.task-dock-chat {
    flex: 1 1 50%; min-height: 0;
    display: flex; flex-direction: column;
    border-top: 1px solid var(--line);
    background: var(--card);
}
.task-dock-chat-head {
    flex: 0 0 auto; padding: .55rem .85rem;
    font-size: .72rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
    color: var(--ink-soft);
}
.task-dock-empty,
.task-dock-chat-placeholder { padding: 1rem; color: var(--ink-soft); font-size: .85rem; }

/* Mobile: full-screen modal (the edge chevron is hidden — close from the top bar). */
@media (max-width: 720px) {
    .task-dock-panel { width: 100vw; box-shadow: none; border-left: none; }
    .task-dock.open .task-dock-toggle { display: none; }
    .task-dock-resizer { display: none; }
}

/* The list row body is the click target for select/open — show it's interactive. */
.tasks-row-main { cursor: pointer; }

/* ── Task inspector header ──────────────────────────────────────────────────── */
.task-editor-head { display: flex; align-items: center; gap: .5rem; margin-bottom: .85rem; }
/* Copy-the-task-code button next to the id chip (KBW-0047) */
.task-id-copy {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: .18rem;
    color: var(--ink-soft);
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
    cursor: pointer;
    line-height: 0;
    transition: color .12s, background .12s, border-color .12s;
}
.task-id-copy:hover { color: var(--ink); background: var(--paper); border-color: color-mix(in srgb, var(--ink-soft) 35%, transparent); }
.task-id-copy:has(path[d^="M20 6"]) { color: var(--ok, #2e7d32); }   /* checkmark state = copied */

/* ── Completion record (read-only, server-stamped) ──────────────────────────── */
.task-completed-row { display: flex; align-items: center; gap: .5rem; margin-bottom: .85rem; padding: .4rem .55rem; background: var(--surface-2, rgba(0,0,0,.03)); border-radius: 6px; }
.task-completed-label { font-size: .72rem; color: var(--ink-soft); }
.task-completed-date { font-size: .8rem; color: var(--ink); font-weight: 600; }
.task-completed-delta { margin-left: auto; }

/* ── History / audit log (10_InteractionModel §12) ──────────────────────────── */
.task-history { margin-top: 1.1rem; padding-top: .85rem; border-top: 1px solid var(--line); }
.task-history-toggle { display: flex; align-items: center; gap: .4rem; width: 100%; background: none; border: none; padding: 0; cursor: pointer; font-size: .82rem; font-weight: 600; color: var(--ink); }
.task-history-caret { color: var(--ink-soft); font-size: .7rem; }
.task-history-empty { font-size: .78rem; margin: .5rem 0 0; }
.task-history-list { list-style: none; margin: .5rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .4rem; }
.task-history-row { display: flex; gap: .5rem; align-items: baseline; font-size: .78rem; line-height: 1.35; }
.task-history-when { flex: 0 0 auto; color: var(--ink-soft); font-variant-numeric: tabular-nums; font-size: .72rem; white-space: nowrap; }
.task-history-body { min-width: 0; }
.task-history-actor { font-weight: 600; color: var(--ink); margin-right: .35rem; }
.task-history-text { color: var(--ink-soft); }
/* Channel badge: the change came through the public API with a named key (KBW-0032) */
.task-via-badge { display: inline-block; margin: 0 .3rem; padding: 0 .35rem; border-radius: 4px; font-size: .66rem; font-weight: 600; line-height: 1.5; white-space: nowrap; color: var(--ink-soft); background: var(--surface-2, rgba(0,0,0,.05)); border: 1px solid var(--line); cursor: help; }

/* ── Comment thread + approval gate (item 2) ────────────────────────────────── */
.task-comments { margin-top: 1.1rem; padding-top: .85rem; border-top: 1px solid var(--line); }
.task-comments-title { font-size: .82rem; font-weight: 600; color: var(--ink); margin: 0 0 .5rem; }
.task-comments-empty { font-size: .78rem; margin: 0 0 .5rem; }
.task-comments-list { list-style: none; margin: 0 0 .6rem; padding: 0; display: flex; flex-direction: column; gap: .55rem; }
.task-comment { padding: .45rem .55rem; border-radius: 6px; background: var(--surface-2, rgba(0,0,0,.03)); }
.task-comment.decision.approved { border-left: 3px solid var(--ok, #2e7d32); }
.task-comment.decision.rejected { border-left: 3px solid var(--alert, #c62828); }
.task-comment.decision.changes_requested { border-left: 3px solid var(--warn, #ef6c00); }
.task-comment-head { display: flex; align-items: center; gap: .4rem; }
.task-comment-author { font-size: .8rem; font-weight: 600; color: var(--ink); }
.task-comment-verdict { font-size: .68rem; }
.task-comment-time { margin-left: auto; font-size: .7rem; color: var(--ink-soft); }
.task-comment-del { color: var(--ink-soft); }
.task-comment-body { margin-top: .3rem; font-size: .82rem; color: var(--ink); }
.task-comments-add { display: flex; flex-direction: column; gap: .4rem; align-items: stretch; }
.task-comments-send { align-self: flex-end; }

.appr-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-bottom: .5rem; }
.appr-state { margin-right: auto; }
.appr-note { width: 100%; resize: vertical; margin-bottom: .6rem; }

/* ── Project members panel (projects only) ──────────────────────────────────── */
.task-members { margin-top: 1.1rem; padding-top: .85rem; border-top: 1px solid var(--line); }
.task-members-title { font-size: .82rem; font-weight: 600; color: var(--ink); margin: 0 0 .5rem; }
.task-members-empty { font-size: .78rem; margin: 0 0 .5rem; }
.task-members-list { list-style: none; margin: 0 0 .6rem; padding: 0; display: flex; flex-direction: column; gap: .3rem; }
.task-members-row { display: flex; align-items: center; gap: .4rem; }
.task-members-name { flex: 1 1 auto; font-size: .82rem; color: var(--ink); }
.task-members-role { flex: 0 0 auto; width: auto; min-width: 84px; }
.task-members-remove { margin-left: auto; }
.task-members-add { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.task-members-cancreate { display: flex; align-items: center; gap: .3rem; font-size: .76rem; color: var(--ink-soft); }

/* ── "Belongs to" picker: project + phase (movable leaf types) ───────────────── */
.task-belongs { display: flex; flex-direction: column; gap: .45rem; margin: .6rem 0 .2rem; }
.task-belongs-row { display: flex; align-items: center; gap: .5rem; }
.task-belongs-label { flex: 0 0 auto; min-width: 64px; font-size: .78rem; color: var(--ink-soft); }
.task-belongs-row .k-field { flex: 1 1 auto; }

/* ── Per-project work settings (working calendar override) ───────────────────── */
.task-worksettings { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid var(--line, rgba(0,0,0,.08)); }
.task-ws-toggle { margin: .2rem 0 .5rem; }
.task-ws-inherit { font-size: .8rem; margin: .2rem 0; }
.task-ws-days { display: flex; flex-direction: column; gap: .3rem; margin-bottom: .6rem; }
.task-ws-day { display: flex; align-items: center; gap: .4rem; }
.task-ws-dayname { display: flex; align-items: center; gap: .35rem; flex: 0 0 auto; min-width: 108px; font-size: .8rem; }
.task-ws-day .k-field { width: 92px; }
.task-ws-sep { color: var(--ink-soft); }
.task-ws-breaks { display: flex; flex-direction: column; gap: .3rem; margin-bottom: .6rem; }
.task-ws-break { display: flex; align-items: center; gap: .4rem; }
.task-ws-break .k-field { width: 92px; }
.task-ws-breaklabel { flex: 1 1 auto !important; width: auto !important; }
.task-ws-sublabel { display: block; font-size: .78rem; color: var(--ink-soft); margin-bottom: .1rem; }
.task-ws-row { display: flex; align-items: center; gap: .5rem; margin-bottom: .5rem; }
.task-ws-row .task-ws-sublabel { margin-bottom: 0; flex: 0 0 auto; min-width: 108px; }
.task-ws-num { width: 92px; }
.task-ws-country { min-width: 160px; }
.task-ws-addbreak { align-self: flex-start; }
.task-ws-actions { margin-top: .4rem; }

@media (max-width: 640px) {
    .flist-row { grid-template-columns: 1fr; gap: .25rem; }
    .flist-label { font-size: .72rem; }
    .tasks-row-desc { display: none; }
    .tasks-status-sel { display: none; }   /* the round check covers done; full edit lands with the detail panel */

    /* ── Compact header (KBW-0152) ────────────────────────────────────────────
       Seven rows used to sit between the app header and the chart. Here the view switcher becomes a
       pulldown on the title's line, and the quick-add form, the Gantt range and the colour legend
       collapse into three buttons sharing one row — leaving title · filter chips · buttons. */

    /* Segmented switcher ran off the screen edge; the pulldown replaces it. */
    .tasks-viewseg { display: none; }
    .tasks-viewsel { display: inline-block; max-width: 8.5rem; }
    /* The pulldown already names the view — don't print it twice on the narrowest line we have. */
    .tasks-title-view { display: none; }
    .k-page-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    /* Quick-add moves into a modal: the on-screen keyboard covers the page anyway, so the inline
       form buys nothing here, while the row it occupied is chart height. */
    .tasks-add { display: none; }
    .tasks-add-btn { display: inline-flex; }
    .gff-act-period { display: inline-flex; }
    /* Same three selects, reached through the Period button — inline they overflowed the viewport. */
    .gff-range { display: none; }

    /* Three buttons, one row, each free to shrink to its label. */
    .tasks-actionbar { flex-wrap: wrap; }
    .tasks-actionbar > .k-btn { flex: 1 1 auto; justify-content: center; }

    /* .k-page-head goes to a column at this width, which would give the refresh button (and the
       list's status filter) a row of their own — the row we just saved. The title box is compact
       enough now to share one line with them, so this screen opts back into a row. */
    .tasks-head { flex-direction: row; align-items: center; }
    .tasks-titlebox { flex: 1 1 auto; }
    .k-page-head.tasks-head > .k-page-actions { flex: 0 0 auto; }

    /* Cap the tree indent so deep branches don't starve the useful column on a narrow screen (§10). */
    .tnode-indent { flex-basis: min(calc(var(--depth, 0) * 1.4rem), 5rem); }

    /* Phone: stack the id chip above the title; align-items:flex-start keeps the chip at its intrinsic
       width (in a column it would otherwise stretch to the full row). */
    .tasks-row-main { flex-direction: column; align-items: flex-start; }

    /* Phone: denser, continuous month grid — drop the desktop "floating white cards" (gaps + rounded
       corners) for a lined grid that reclaims the horizontal space the fixed edge grips eat into.
       The three stacked grids (weekday header / day-cell background / bars overlay) share their
       column-gap, and each bar is inset by its own margin, so ALL FOUR must go to zero together or the
       chips stop aligning with their day columns and multi-day bars break at day boundaries. */
    .kcal-grid { gap: 0; padding: 0; }
    .kcal-day { border-radius: 0; padding: .15rem .2rem; }
    /* Shrink the "today" pill so it doesn't touch the day's first event bar below it. */
    .kcal-day.today .kcal-daynum { min-width: 1rem; height: 1rem; line-height: 1rem; }
    .kcal-week-bg { column-gap: 0; }
    .kcal-week-bars { column-gap: 0; }
    .kcal-bar { margin: 0 .2rem; padding: 0 .2rem; border-radius: 5px; }
    /* Collapse the inset at a continuation edge so multi-day bars connect across day boundaries;
       keep the .2rem breathing room only at the bar's real start/end.
       clip-start continues from the left → drop the left margin; clip-end continues to the right →
       drop the right margin (a bar that is both keeps neither and runs edge to edge). */
    .kcal-bar.clip-start { margin-left: 0; }
    .kcal-bar.clip-end   { margin-right: 0; }
    .kcal-bar-txt { font-size: .5rem; }

    /* Weekday header: no gap, centred over its day cell (right-aligned like the desktop it reads as a
       misalignment on the narrow grid). Text shrunk so a label fits its column. */
    .kcal-weekdays { column-gap: 0; padding: 0; }
    .kcal-weekdays span { font-size: .4rem; padding: .3rem .1rem; text-align: center; }
}

/* ── Shared task affordances (every view: list / kanban / gantt / calendar) ─────
   Read-only lock (LockBadge) + drag cursors. Defined once so the four views match. */
.task-lock { display: inline-flex; align-items: center; flex: 0 0 auto; color: var(--ink-soft); }
.task-lock svg { width: 13px; height: 13px; }
.kcal-bar .task-lock { color: inherit; opacity: .8; margin-left: .15rem; }
.kcal-bar .task-lock svg, .ktime-ev .task-lock svg { width: 11px; height: 11px; }

/* Draggable calendar chips/events show a grab cursor; read-only/done ones don't (no .drag class). */
.kcal-bar.drag, .ktime-ev.drag { cursor: grab; }
.kcal-bar.drag:active, .ktime-ev.drag:active { cursor: grabbing; }

/* Gantt: read-only bars can't be dragged/resized (still openable from the name pane). */
.gantt-frappe-host .bar-wrapper.gf-readonly { pointer-events: none; }

/* ── Calendar export (left-panel dock) ─────────────────────────────────────── */
/* Mint a subscribable iCal link from the current project selection. The panel only
   creates links; it collapses back to the button whenever the selection changes. */
.tasks-panel-export { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--line); }
.tasks-export-gen { width: 100%; justify-content: center; }
.tasks-export-ready {
    border: 1px solid var(--line); border-radius: 10px; background: var(--paper);
    padding: 8px 10px;
}
.tasks-export-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.tasks-export-title { font-size: 12px; font-weight: 600; color: var(--ink); }
.tasks-export-close {
    border: none; background: none; cursor: pointer; font-size: 16px; line-height: 1;
    color: var(--ink-soft); padding: 0 2px;
}
.tasks-export-close:hover { color: var(--ink); }
.tasks-export-actions { display: flex; gap: 6px; flex-wrap: wrap; }
.tasks-export-hint { margin: 6px 0 0; font-size: 11px; color: var(--ink-soft); }

/* KBW-0034: the name pane resizer sits on the pane's right edge. The pane is position:relative and
   overflow:hidden, so the handle is pinned there rather than being a flex sibling like the dock's. */
.gff-side-resizer {
    position: absolute; top: 0; right: 0; bottom: 0;
    width: 6px; z-index: 3;
}

/* ── Task page (10 §11.3) ──────────────────────────────────────────────────────
   A task is a place with a URL, so it gets a page and not only a dock slot. Column
   width is capped: the inspector was designed for a narrow panel, and stretching its
   fields across a wide screen would make every label a long walk from its value. */
.taskpage { max-width: 980px; }

.taskpage-head {
    display: flex; align-items: center; gap: .75rem; flex-wrap: wrap;
    padding-bottom: .6rem; margin-bottom: .8rem;
    border-bottom: 1px solid var(--line);
}
.taskpage-crumbs {
    display: flex; align-items: center; gap: .35rem; flex: 1 1 auto; min-width: 0;
    font-size: .82rem; color: var(--ink-soft); flex-wrap: wrap;
}
.taskpage-crumbs a { color: inherit; text-decoration: none; }
.taskpage-crumbs a:hover { color: var(--ink); text-decoration: underline; }
.taskpage-crumb-sep { color: var(--line); }

.taskpage-actions { display: flex; align-items: center; gap: .5rem; flex: 0 0 auto; }
/* Always visible here, unlike on a row: it is one of the two controls on the page,
   not one of forty, so hiding it until hover would only make it hard to find. */
.taskpage-kebab { opacity: 1; }

.taskpage-body { padding-bottom: 2rem; }
.taskpage-missing { display: flex; align-items: center; gap: .75rem; }

/* The dock's "temporary" marker sits next to its title, not inside the chat: it describes the
   conversation as a whole, and inside the transcript it would scroll away with the first reply. */
.task-dock-temp { flex: 0 0 auto; font-size: .66rem; text-transform: lowercase; }
