// Partitura — main timeline component.
// Supports: day/week/month/year periods, sort (alpha/load), modules as nested agent sub-rows,
// event cluster popover, resizable right panel, sticky panel.

const { useState, useRef, useEffect, useMemo, useLayoutEffect } = React;

// ============================================================
// CONSTANTS & SCALE HELPERS
// ============================================================
const TARGET_W = 1200;
const LABEL_W = 320;
const PRESETS = [
  { id: "day",  label: "День",  visibleMin: 1440 },
  { id: "8h",   label: "8 ч",   visibleMin: 8 * 60 },
  { id: "1h",   label: "Час",   visibleMin: 60 },
  { id: "30m",  label: "30 мин",visibleMin: 30 },
  { id: "15m",  label: "15 мин",visibleMin: 15 },
  { id: "5m",   label: "5 мин", visibleMin: 5 },
];
const SCALE_MIN = 0.5;
const SCALE_MAX = 280;

function presetToScale(preset, viewportW) {
  return viewportW / preset.visibleMin;
}
function scaleToSlider(scale) {
  return (Math.log(scale) - Math.log(SCALE_MIN)) / (Math.log(SCALE_MAX) - Math.log(SCALE_MIN));
}
function sliderToScale(v) {
  return Math.exp(Math.log(SCALE_MIN) + v * (Math.log(SCALE_MAX) - Math.log(SCALE_MIN)));
}

// ============================================================
// RULER
// ============================================================
function getTickStep(scale) {
  if (scale < 1.2)  return { minor: 60,  major: 180 };
  if (scale < 2.5)  return { minor: 30,  major: 60  };
  if (scale < 6)    return { minor: 15,  major: 60  };
  if (scale < 14)   return { minor: 5,   major: 30  };
  if (scale < 40)   return { minor: 5,   major: 15  };
  if (scale < 100)  return { minor: 1,   major: 5   };
  return { minor: 0.5, major: 1 };
}
function Ruler({ scale, width }) {
  const step = getTickStep(scale);
  const ticks = [];
  for (let m = 0; m <= 24 * 60; m += step.minor) {
    const isMajor = (Math.round(m * 10) % Math.round(step.major * 10)) === 0;
    ticks.push({ m, major: isMajor });
  }
  return (
    <div style={{
      position: "relative", width, height: 36,
      background: "var(--bg-elev)", borderBottom: "1px solid var(--border)",
    }}>
      {ticks.map((t, i) => {
        const x = t.m * scale;
        return (
          <div key={i} style={{
            position: "absolute", left: x, top: t.major ? 8 : 22, bottom: 0, width: 1,
            background: t.major ? "var(--text-muted)" : "var(--border)",
            pointerEvents: "none",
          }} />
        );
      })}
      {ticks.filter((t) => t.major).map((t, i) => {
        const x = t.m * scale;
        const label = scale >= 100 ? fmtHMS(t.m) : fmtHM(t.m);
        return (
          <div key={`l${i}`} style={{
            position: "absolute", left: x + 4, top: 4,
            fontSize: 10, fontFamily: "var(--font-mono)", color: "var(--text-dim)",
            letterSpacing: "0.02em", pointerEvents: "none", whiteSpace: "nowrap",
          }}>{label}</div>
        );
      })}
    </div>
  );
}

// ============================================================
// BAR (one task segment on day timeline)
// ============================================================
function Bar({ seg, scale, top, onClick, isSelected = false }) {
  const left = seg.start * scale;
  const width = Math.max(2, (seg.end - seg.start) * scale);
  const style = STATE_STYLES[seg.state];
  const isBlocked = seg.state === "blocked";
  const bg = isBlocked
    ? `repeating-linear-gradient(45deg, rgba(239,68,68,0.10) 0 5px, transparent 5px 10px)`
    : style.bg;
  const modCol = moduleColor(seg.module);

  return (
    <div
      onClick={(e) => { e.stopPropagation(); onClick(); }}
      title={`PB-${seg.seq} · ${seg.title} · ${fmtHM(seg.start)}–${fmtHM(seg.end)}`}
      style={{
        position: "absolute", left, top, height: 30, width,
        background: bg,
        border: `1px solid ${style.border}`,
        borderLeft: `3px solid ${style.color}`,
        borderRadius: 3,
        display: "flex", alignItems: "center", gap: 5, padding: "0 5px",
        cursor: "pointer", overflow: "hidden", whiteSpace: "nowrap",
        fontSize: 11, color: "var(--text)",
        boxShadow: isSelected
          ? `0 0 0 2px var(--accent)`
          : (isBlocked ? "inset 0 0 0 1px rgba(239,68,68,0.3)" : "none"),
        zIndex: isSelected ? 2 : 1,
        transition: "background 0.1s, box-shadow 0.1s",
      }}
    >
      <span style={{ flexShrink: 0, width: 4, height: 18, background: modCol, borderRadius: 1, marginRight: 2 }} />
      <span style={{ fontFamily: "var(--font-mono)", fontWeight: 600, color: style.color, flexShrink: 0 }}>PB-{seg.seq}</span>
      {width > 80 && <span style={{ color: "var(--text-dim)", overflow: "hidden", textOverflow: "ellipsis" }}>{seg.title}</span>}
      {isBlocked && width > 40 && (
        <span style={{
          marginLeft: "auto", fontFamily: "var(--font-mono)", fontSize: 9,
          textTransform: "uppercase", letterSpacing: "0.08em",
          color: "var(--crit)", fontWeight: 600, flexShrink: 0,
        }}>BLOCKED</span>
      )}
    </div>
  );
}

function OfflineZone({ start, end, scale, top, height }) {
  const left = start * scale;
  const width = (end - start) * scale;
  return (
    <div style={{
      position: "absolute", left, top, height, width,
      background: "repeating-linear-gradient(135deg, rgba(127,127,127,0.10) 0 6px, rgba(127,127,127,0.04) 6px 12px)",
      borderTop: "1px dashed rgba(161,161,170,0.4)",
      borderBottom: "1px dashed rgba(161,161,170,0.4)",
      pointerEvents: "none",
    }}>
      <div style={{
        position: "absolute", top: 4, left: 6,
        fontSize: 10, color: "var(--text-muted)", fontFamily: "var(--font-mono)",
        textTransform: "uppercase", letterSpacing: "0.08em",
      }}>watcher offline</div>
    </div>
  );
}

function EventMarker({ ev, scale, onClick, top = 44, highlighted = false }) {
  const left = ev.t * scale;
  return (
    <div
      onClick={(e) => { e.stopPropagation(); onClick(e); }}
      title={`${fmtHM(ev.t)} · PB-${ev.seq} · ${ev.text}`}
      style={{
        position: "absolute", left: left - 9, top,
        width: 18, height: 18,
        display: "flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer", zIndex: highlighted ? 5 : 2,
        borderRadius: 4,
        background: highlighted ? "var(--accent-soft)" : "transparent",
        boxShadow: highlighted ? "0 0 0 2px var(--accent)" : "none",
      }}
    >
      <EventIcon kind={ev.kind} sev={ev.sev} size={16} />
    </div>
  );
}

function EventCluster({ events, scale, onClick, top = 42 }) {
  const center = events[0].t * scale;
  const sev = events.find((e) => e.kind === "alert" && e.sev === "critical") ? "critical"
            : events.find((e) => e.kind === "alert" && e.sev === "warning")  ? "warning"
            : null;
  const ringColor = sev === "critical" ? "var(--crit)" : sev === "warning" ? "var(--warn)" : "var(--text-dim)";
  return (
    <div
      onClick={(e) => { e.stopPropagation(); onClick(e); }}
      title={`${events.length} событий — кликни, чтобы выбрать конкретное`}
      style={{
        position: "absolute", left: center - 12, top,
        width: 24, height: 22, borderRadius: 11,
        background: "var(--bg-elev)", border: `1.5px solid ${ringColor}`,
        display: "flex", alignItems: "center", justifyContent: "center",
        fontSize: 10, fontFamily: "var(--font-mono)",
        color: "var(--text)", fontWeight: 600,
        cursor: "pointer", zIndex: 3,
      }}
    >{events.length}</div>
  );
}

function clusterEvents(events, scale) {
  if (events.length === 0) return [];
  const minDistPx = 14;
  const sorted = events.slice().sort((a, b) => a.t - b.t);
  const groups = [];
  let current = [sorted[0]];
  for (let i = 1; i < sorted.length; i++) {
    const prev = current[current.length - 1];
    const dxPx = (sorted[i].t - prev.t) * scale;
    if (dxPx < minDistPx) current.push(sorted[i]);
    else { groups.push(current); current = [sorted[i]]; }
  }
  groups.push(current);
  return groups;
}

// ============================================================
// LABELS
// ============================================================
function AgentTrackLabel({ agent, alerts }) {
  return (
    <div className="pt-tl pt-tl-agent">
      <div className="pt-tl-row1">
        <div className={"pt-tl-avatar" + (agent.isLead ? " is-lead" : "")} style={{ backgroundImage: `url(${agent.avatar})` }} />
        <div className="pt-tl-id">
          <div className="pt-tl-name">{agent.name}</div>
          <div className="pt-tl-role">{agent.roleLabel}</div>
        </div>
        {alerts && alerts.length > 0 && (
          <span className="pt-tl-alert" title={alerts.map(a => a.ruleTitle).join("\n")}>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L22 21H2L12 2z"/></svg>
            {alerts.length}
          </span>
        )}
      </div>
      <div className="pt-tl-watcher">
        {agent.watcherOnline ? (
          <>
            <span className="dot ok" />
            <span>watcher</span>
            <span className="pt-tl-mono">{Math.floor(agent.watcherUptimeMin/60)}ч {agent.watcherUptimeMin%60}м</span>
          </>
        ) : (
          <>
            <span className="dot crit" />
            <span className="pt-tl-crit">offline</span>
            <span className="pt-tl-mono">{Math.round(agent.lastHeartbeatMin)} мин</span>
          </>
        )}
      </div>
      <div className="pt-tl-task">
        {agent.currentTask ? (
          <>
            <span className="pt-tl-seq">PB-{agent.currentTask.seq}</span>
            <span className="pt-tl-title">{agent.currentTask.title}</span>
          </>
        ) : <span className="pt-tl-free">— свободен —</span>}
      </div>
      <div className="pt-tl-foot">
        <span className="pt-tl-foot-pair">
          <span className="pt-tl-foot-lbl">ctx</span>
          <span className="pt-tl-mono pt-tl-pct" style={{
            color: agent.contextPct >= 70 ? "var(--ok)" : agent.contextPct >= 30 ? "var(--warn)" : "var(--crit)",
          }}>{agent.contextPct}%</span>
        </span>
        <span className="pt-tl-foot-pair">
          <span className="pt-tl-foot-lbl">tools</span>
          <span className="pt-tl-mono">{agent.avgToolCalls}</span>
        </span>
        <span className="pt-tl-load"><MiniBars data={agent.todayLoad} /></span>
      </div>
    </div>
  );
}

// Subagent label inside a module — compact agent row.
function SubAgentLabel({ agent, segments, range, isLead }) {
  const total = (segments || []).reduce((acc, s) => {
    const ov = Math.max(0, Math.min(s.end, range[1]) - Math.max(s.start, range[0]));
    return acc + ov;
  }, 0);
  return (
    <div className="pt-sl">
      <div className="pt-sl-prong" />
      <div className={"pt-sl-avatar" + (isLead ? " is-lead" : "")} style={{ backgroundImage: `url(${agent.avatar})` }} />
      <div className="pt-sl-id">
        <div className="pt-sl-name">{agent.name}</div>
        <div className="pt-sl-role">{agent.roleLabel}</div>
      </div>
      <div className="pt-sl-stat">
        <div className="pt-sl-stat-num">{Math.round(total)}<span className="pt-sl-stat-unit">м</span></div>
        <div className="pt-sl-stat-lbl">{segments.length} плашек</div>
      </div>
    </div>
  );
}

function ModuleTrackLabel({ moduleName, segments }) {
  const uniqueAgents = Array.from(new Set(segments.map(s => s._agent).filter(Boolean)));
  const totalMin = segments.reduce((acc, s) => acc + (s.end - s.start), 0);
  const blockedMin = segments.filter(s => s.state === "blocked").reduce((acc, s) => acc + (s.end - s.start), 0);
  const taskCount = new Set(segments.map(s => s.seq)).size;
  return (
    <div className="pt-tl pt-tl-module">
      <div className="pt-tl-row1">
        <div className="pt-tl-mod-icon" style={{
          background: `${moduleColor(moduleName)}22`,
          borderColor: moduleColor(moduleName),
          color: moduleColor(moduleName),
        }}>{moduleName[0].toUpperCase()}</div>
        <div className="pt-tl-id">
          <div className="pt-tl-name pt-tl-mono">{moduleName}</div>
          <div className="pt-tl-role">{taskCount} задач · {uniqueAgents.length} агент{uniqueAgents.length === 1 ? "" : "ов"}</div>
        </div>
      </div>
      <div className="pt-tl-mod-agents">
        {uniqueAgents.slice(0, 6).map((aid) => {
          const a = teamById(aid);
          if (!a) return null;
          return <div key={aid} className="pt-tl-mini-avatar" title={a.name} style={{ backgroundImage: `url(${a.avatar})` }} />;
        })}
      </div>
      <div className="pt-tl-foot">
        <span className="pt-tl-foot-pair">
          <span className="pt-tl-foot-lbl">сегодня</span>
          <span className="pt-tl-mono">{Math.round(totalMin)}м</span>
        </span>
        {blockedMin > 0 && (
          <span className="pt-tl-foot-pair">
            <span className="pt-tl-foot-lbl">block</span>
            <span className="pt-tl-mono pt-tl-crit">{Math.round(blockedMin)}м</span>
          </span>
        )}
      </div>
    </div>
  );
}

function MiniBars({ data, width = 76, height = 14 }) {
  const max = Math.max(...data, 1);
  const w = width / data.length - 1.5;
  return (
    <svg width={width} height={height} style={{ display: "block" }}>
      {data.map((v, i) => {
        const h = (v / max) * (height - 1);
        return <rect key={i} x={i * (w + 1.5)} y={height - h} width={w} height={h}
          fill="var(--accent)" opacity={0.4 + 0.6 * (v / max)} />;
      })}
    </svg>
  );
}

// ============================================================
// HELPERS — workload, sort
// ============================================================
function workload(segments, range) {
  if (!segments) return 0;
  return segments.reduce((acc, s) => {
    const ov = Math.max(0, Math.min(s.end, range[1]) - Math.max(s.start, range[0]));
    return acc + ov;
  }, 0);
}

function applySort(items, sortMode, range) {
  if (sortMode === "default") return items;
  const sorted = items.slice();
  if (sortMode === "alphaAsc" || sortMode === "alphaDesc") {
    sorted.sort((a, b) => (a.sortName || "").localeCompare(b.sortName || "", "ru"));
    if (sortMode === "alphaDesc") sorted.reverse();
  } else if (sortMode === "loadAsc" || sortMode === "loadDesc") {
    sorted.sort((a, b) => workload(a.segments || [], range) - workload(b.segments || [], range));
    if (sortMode === "loadDesc") sorted.reverse();
  }
  return sorted;
}

function packSegments(segs) {
  const sorted = segs.slice().sort((a, b) => a.start - b.start);
  const rowEnds = [];
  return sorted.map((s) => {
    let row = rowEnds.findIndex((e) => e <= s.start);
    if (row === -1) { row = rowEnds.length; rowEnds.push(s.end); }
    else rowEnds[row] = s.end;
    return { ...s, _row: row };
  });
}

// ============================================================
// CONTROLS
// ============================================================
function Controls({
  mode, setMode,
  period, setPeriod,
  sortMode, setSortMode,
  scale, setScale, viewportW,
  filters, setFilters,
  scrollToNow,
  totalRows,
  isDay,
}) {
  const sliderVal = scaleToSlider(scale);
  const visibleMin = viewportW / scale;

  return (
    <div className="pt-controls">
      {/* Row 1: Period + Mode + Sort + Now */}
      <div className="pt-ctl-row">
        {/* Period */}
        <div className="pt-segctl">
          {PERIODS.map((p) => (
            <button
              key={p.id}
              onClick={() => setPeriod(p.id)}
              className={"pt-segctl-btn" + (period === p.id ? " is-on" : "")}
            >{p.label}</button>
          ))}
        </div>

        <div className="pt-ctl-sep" />

        {/* Mode */}
        <div className="pt-segctl">
          {[{ id: "agents", label: "По агентам" }, { id: "modules", label: "По модулям" }].map((opt) => (
            <button
              key={opt.id}
              onClick={() => setMode(opt.id)}
              className={"pt-segctl-btn" + (mode === opt.id ? " is-on" : "")}
            >{opt.label}</button>
          ))}
        </div>

        <div className="pt-ctl-sep" />

        {/* Sort */}
        <label className="pt-ctl-field">
          <span className="pt-ctl-lbl">сортировка</span>
          <select className="pt-select" value={sortMode} onChange={(e) => setSortMode(e.target.value)}>
            <option value="default">по умолчанию</option>
            <option value="alphaAsc">А → Я</option>
            <option value="alphaDesc">Я → А</option>
            <option value="loadDesc">загрузка ↓</option>
            <option value="loadAsc">загрузка ↑</option>
          </select>
        </label>

        <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 10 }}>
          <span style={{ fontSize: 11, color: "var(--text-muted)", fontFamily: "var(--font-mono)" }}>
            строк: <strong style={{ color: "var(--text)" }}>{totalRows}</strong>
          </span>
          {isDay && (
            <button onClick={scrollToNow} className="ctl-btn ctl-primary">⟶ к сейчас</button>
          )}
        </div>
      </div>

      {/* Row 2: Zoom (only for day) */}
      {isDay && (
        <div className="pt-ctl-row">
          <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
            <span className="pt-ctl-lbl" style={{ marginRight: 6 }}>зум:</span>
            {PRESETS.map((p) => {
              const targetScale = presetToScale(p, viewportW);
              const isActive = Math.abs(targetScale - scale) / scale < 0.06;
              return (
                <button
                  key={p.id}
                  onClick={() => setScale(targetScale)}
                  className={"pt-preset" + (isActive ? " is-on" : "")}
                >{p.label}</button>
              );
            })}
          </div>
          <div className="pt-ctl-sep" />
          <input
            type="range" className="pt-slider"
            min="0" max="1" step="0.001"
            value={sliderVal}
            onChange={(e) => setScale(sliderToScale(parseFloat(e.target.value)))}
            style={{ flex: 1, maxWidth: 320, accentColor: "var(--accent)" }}
          />
          <div style={{ display: "flex", alignItems: "center", gap: 4 }}>
            <span className="pt-ctl-lbl">видно:</span>
            <input
              type="number" min="1" max="1440" step="1"
              value={Math.round(visibleMin)}
              onChange={(e) => { const v = parseFloat(e.target.value); if (v > 0) setScale(viewportW / v); }}
              className="pt-numinput"
            />
            <span className="pt-ctl-lbl">мин</span>
            <span className="pt-ctl-lbl">·</span>
            <span className="pt-ctl-lbl"><span style={{ color: "var(--text)" }}>{scale.toFixed(2)}</span> px/мин</span>
          </div>
        </div>
      )}

      {/* Row 3: Filter chips */}
      <div className="pt-ctl-row" style={{ flexWrap: "wrap" }}>
        <span className="pt-ctl-lbl">{isDay ? "события:" : "учитывать:"}</span>
        {EVENT_KINDS_ORDER.map((k) => (
          <FilterChip key={k} active={filters[k]}
            onClick={() => setFilters({ ...filters, [k]: !filters[k] })}>
            <EventIcon kind={k} sev={k === "alert" ? "warning" : null} size={12} />
            <span>{EVENT_LABELS[k]}</span>
          </FilterChip>
        ))}
        {isDay && (
          <>
            <div className="pt-ctl-sep" />
            <FilterChip active={filters._blocked}
              onClick={() => setFilters({ ...filters, _blocked: !filters._blocked })}>
              <span style={{
                display: "inline-block", width: 14, height: 8,
                background: "repeating-linear-gradient(45deg, rgba(239,68,68,0.6) 0 3px, transparent 3px 6px)",
                border: "1px solid var(--crit)",
              }} />
              <span>Blocked</span>
            </FilterChip>
          </>
        )}
      </div>
    </div>
  );
}

function FilterChip({ active, onClick, children }) {
  return (
    <button
      onClick={onClick}
      className={"pt-filter-chip" + (active ? " is-on" : "")}
    >{children}</button>
  );
}

// ============================================================
// CLUSTER POPOVER
// ============================================================
function ClusterPopover({ events, rowKey, x, y, onPickEvent, onClose }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    const onClick = () => onClose();
    setTimeout(() => window.addEventListener("click", onClick), 0);
    window.addEventListener("keydown", onKey);
    return () => {
      window.removeEventListener("click", onClick);
      window.removeEventListener("keydown", onKey);
    };
  }, [onClose]);

  return (
    <div
      onClick={(e) => e.stopPropagation()}
      style={{
        position: "fixed",
        left: Math.min(x, window.innerWidth - 320),
        top: Math.min(y, window.innerHeight - 280),
        width: 300,
        background: "var(--surface)",
        border: "1px solid var(--border)",
        borderRadius: 8,
        boxShadow: "0 10px 32px rgba(0,0,0,0.35)",
        zIndex: 200,
        maxHeight: 320,
        display: "flex",
        flexDirection: "column",
      }}
    >
      <div style={{
        padding: "8px 12px",
        borderBottom: "1px solid var(--border-soft)",
        display: "flex", justifyContent: "space-between", alignItems: "center",
      }}>
        <div style={{ fontSize: 11, color: "var(--text-muted)", fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.06em" }}>
          событий в этом моменте: <strong style={{ color: "var(--text)" }}>{events.length}</strong>
        </div>
        <button onClick={onClose} style={{
          background: "transparent", border: "none", color: "var(--text-muted)",
          cursor: "pointer", fontSize: 14, padding: 2, lineHeight: 1,
        }}>✕</button>
      </div>
      <div style={{ overflowY: "auto", flex: 1 }}>
        {events.map((ev, i) => (
          <div
            key={i}
            onClick={() => onPickEvent(ev)}
            style={{
              display: "grid", gridTemplateColumns: "22px 1fr",
              gap: 8, padding: "8px 12px",
              borderBottom: i < events.length - 1 ? "1px solid var(--border-soft)" : "none",
              cursor: "pointer",
              transition: "background 0.08s",
            }}
            onMouseEnter={(e) => e.currentTarget.style.background = "var(--surface-hi)"}
            onMouseLeave={(e) => e.currentTarget.style.background = "transparent"}
          >
            <div style={{ display: "flex", alignItems: "center", justifyContent: "center" }}>
              <EventIcon kind={ev.kind} sev={ev.sev} size={14} />
            </div>
            <div style={{ minWidth: 0 }}>
              <div style={{ display: "flex", gap: 6, alignItems: "center" }}>
                <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-muted)" }}>{fmtHM(ev.t)}</span>
                <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--accent)", fontWeight: 600 }}>PB-{ev.seq}</span>
              </div>
              <div style={{ fontSize: 12, color: "var(--text)", marginTop: 1 }}>{ev.text}</div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ============================================================
// ROW — handles agent / module-head / module-sub kinds
// ============================================================
function Row({
  row, scale, totalWidth,
  isSelected, selectedSegKey, highlightEvKey,
  onClickRow, onClickBar, onClickEventGroup,
}) {
  const isHead = row.kind === "module-head";
  const isSub  = row.kind === "module-sub";

  // Layout per kind
  const isHeader = isHead;
  const segmentRows = (row.segments || []).map((s) => ({ ...s, _row: 0 }));
  const trackHeight = isHead ? 52 : (isSub ? 76 : 124);
  const barTop = isHead ? 16 : Math.round((trackHeight - 30) / 2 - (isSub ? 4 : 8));
  const eventTop = isHead ? 16 : (barTop + 36);

  const groups = clusterEvents(row.events || [], scale);

  return (
    <div className={"pt-track" + (isSelected ? " is-selected" : "") + (isHead ? " pt-head" : "") + (isSub ? " pt-sub" : "")}
      style={{ display: "flex", borderBottom: "1px solid var(--border-soft)", minHeight: trackHeight, position: "relative" }}
    >
      <div
        onClick={onClickRow}
        className="pt-track-label"
        style={{
          width: LABEL_W,
          flexShrink: 0,
          padding: 0,
          background: isSelected ? "var(--accent-soft)" : (isHead ? "var(--surface)" : "var(--bg-elev)"),
          borderRight: "1px solid var(--border)",
          borderLeft: isSelected ? "3px solid var(--accent)" : "3px solid transparent",
          position: "sticky", left: 0, zIndex: 4,
          cursor: "pointer",
          transition: "background 0.12s, border-color 0.12s",
        }}
      >
        {row.label}
      </div>
      <div style={{ position: "relative", width: totalWidth, flex: "0 0 auto" }}>
        {/* Offline zones (agent + module-sub) */}
        {!isHead && (row.offline || []).map((o, i) => (
          <OfflineZone key={i} start={o.start} end={o.end} scale={scale} top={barTop - 6} height={trackHeight - 12} />
        ))}
        {/* Bars (no bars on module-head) */}
        {!isHead && segmentRows.map((seg, i) => {
          const isSegSel = isSelected && selectedSegKey === `${seg.seq}_${seg.start}`;
          return (
            <Bar key={i} seg={seg} scale={scale} top={barTop}
              isSelected={isSegSel} onClick={() => onClickBar(seg)} />
          );
        })}
        {/* Events */}
        {groups.map((g, i) => {
          if (g.length === 1) {
            const ev = g[0];
            const evKey = `${ev.t}_${ev.agent}_${ev.kind}_${ev.seq}`;
            return <EventMarker key={i} ev={ev} scale={scale} top={eventTop}
              highlighted={highlightEvKey === evKey}
              onClick={(e) => onClickEventGroup(g, e)} />;
          }
          return <EventCluster key={i} events={g} scale={scale} top={eventTop - 2}
            onClick={(e) => onClickEventGroup(g, e)} />;
        })}
        {/* Module head accent strip */}
        {isHead && (
          <div style={{
            position: "absolute", top: 0, left: 0, right: 0, height: 3,
            background: row.moduleColor || "var(--accent)",
            opacity: 0.7,
          }} />
        )}
      </div>
    </div>
  );
}

// ============================================================
// STATS ROW (week / month / year)
// ============================================================
function StatsRow({
  row, isSelected, onClickRow, period,
}) {
  const isHead = row.kind === "stats-module-head";
  const isSub  = row.kind === "stats-module-sub";
  const isAgent = row.kind === "stats-agent";

  return (
    <div
      onClick={onClickRow}
      className={"pt-track" + (isSelected ? " is-selected" : "") + (isHead ? " pt-head" : "")}
      style={{
        display: "flex", borderBottom: "1px solid var(--border-soft)",
        minHeight: isSub ? 84 : (isHead ? 60 : 116),
        position: "relative", cursor: "pointer",
        background: isSelected ? "linear-gradient(90deg, var(--accent-soft) 0 320px, transparent 320px)" : "transparent",
      }}
    >
      <div
        className="pt-track-label"
        style={{
          width: LABEL_W, flexShrink: 0, padding: 0,
          background: isSelected ? "var(--accent-soft)" : (isHead ? "var(--surface)" : "var(--bg-elev)"),
          borderRight: "1px solid var(--border)",
          borderLeft: isSelected ? "3px solid var(--accent)" : "3px solid transparent",
        }}
      >
        {row.label}
      </div>
      <div style={{ flex: 1, padding: "12px 18px", display: "flex", alignItems: "center", gap: 24, overflow: "hidden" }}>
        {/* Big numeric stats */}
        <div className="pt-stat-numbers">
          <PtStat lbl="часов" val={row.stats.hours} accent="var(--accent)" />
          <PtStat lbl="плашек" val={row.stats.segments} />
          <PtStat lbl="событий" val={row.stats.events} />
          <PtStat lbl="алертов" val={row.stats.alerts} accent={row.stats.alerts > 0 ? "var(--crit)" : "var(--text-dim)"} />
        </div>
        {/* Daily/weekly strip */}
        <div style={{ flex: 1, minWidth: 200 }}>
          <DailyStrip history={row.history} buckets={period === "year" ? row.weekBuckets : row.history} period={period} eventCounts={row.eventCounts} />
        </div>
        {/* Trend */}
        <div style={{ flexShrink: 0, textAlign: "right" }}>
          <div style={{ fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 600,
            color: row.stats.trend > 0 ? "var(--ok)" : row.stats.trend < 0 ? "var(--crit)" : "var(--text-dim)",
          }}>
            {row.stats.trend > 0 ? "↑" : row.stats.trend < 0 ? "↓" : "·"} {Math.abs(Math.round(row.stats.trend))}%
          </div>
          <div style={{ fontSize: 9, color: "var(--text-muted)", fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.06em" }}>
            vs пред.
          </div>
        </div>
      </div>
    </div>
  );
}

function PtStat({ lbl, val, accent }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", minWidth: 56 }}>
      <div style={{ fontFamily: "var(--font-mono)", fontSize: 18, fontWeight: 600, color: accent || "var(--text)", lineHeight: 1 }}>{val}</div>
      <div style={{ fontSize: 9, color: "var(--text-muted)", fontFamily: "var(--font-mono)", textTransform: "uppercase", letterSpacing: "0.06em", marginTop: 4 }}>{lbl}</div>
    </div>
  );
}

// Daily strip — stacked horizontal bars showing per-state breakdown
function DailyStrip({ history, buckets, period, eventCounts }) {
  const data = period === "year" ? buckets : history;
  // Each bucket: { planning, in-progress, review, testing, research, blocked, total }
  const maxTotal = Math.max(...data.map(d => d.total || 0), 1);
  const states = ["in-progress", "review", "testing", "planning", "research", "blocked"];
  return (
    <svg width="100%" height="48" viewBox={`0 0 ${data.length * 24} 48`} preserveAspectRatio="none">
      {data.slice().reverse().map((d, idx) => {
        const x = idx * 24 + 2;
        const totalH = ((d.total || 0) / maxTotal) * 36;
        let yCursor = 44;
        return (
          <g key={idx}>
            {states.map((s) => {
              const v = d[s] || 0;
              if (v === 0) return null;
              const h = (v / maxTotal) * 36;
              yCursor -= h;
              const st = STATE_STYLES[s];
              return <rect key={s} x={x} y={yCursor} width={20} height={h}
                fill={st.color} opacity={0.85} />;
            })}
            {/* Event count dot */}
            {eventCounts && eventCounts[data.length - 1 - idx] && eventCounts[data.length - 1 - idx].alerts > 0 && (
              <circle cx={x + 10} cy={4} r={2.5} fill="var(--crit)" />
            )}
          </g>
        );
      })}
    </svg>
  );
}

// ============================================================
// ROW BUILDERS
// ============================================================
function buildRowsDay({ mode, sortMode, filters, visibleEvents, alertsForLabels, visRange }) {
  if (mode === "agents") {
    const items = TEAM.map((a) => {
      const segments = (PT_AGENT_SEGMENTS[a.id] || []).filter(s => filters._blocked || s.state !== "blocked");
      const events = visibleEvents.filter(e => e.agent === a.id);
      const myAlerts = (alertsForLabels || []).filter(x => x.agent === a.id && x.status === "active");
      return {
        key: `agent:${a.id}`,
        kind: "agent",
        sortName: a.name,
        agent: a,
        label: <AgentTrackLabel agent={a} alerts={myAlerts} />,
        segments,
        events,
        offline: PT_OFFLINE[a.id] || [],
      };
    });
    return applySort(items, sortMode, visRange);
  }
  // modules
  const allModules = Array.from(new Set(
    Object.values(PT_AGENT_SEGMENTS).flatMap(segs => segs.map(s => s.module))
  ));
  const moduleData = allModules.map((m) => {
    const subAgents = [];
    TEAM.forEach((a) => {
      const segs = (PT_AGENT_SEGMENTS[a.id] || []).filter(s =>
        s.module === m && (filters._blocked || s.state !== "blocked")
      );
      if (segs.length === 0) return;
      const segsWithAgent = segs.map(s => ({ ...s, _agent: a.id }));
      const evs = visibleEvents.filter(e => {
        if (e.agent !== a.id) return false;
        const ownerSeg = (PT_AGENT_SEGMENTS[a.id] || []).find(s => s.seq === e.seq);
        return ownerSeg && ownerSeg.module === m;
      });
      subAgents.push({
        key: `module:${m}/agent:${a.id}`,
        kind: "module-sub",
        sortName: a.name,
        agent: a,
        moduleName: m,
        segments: segsWithAgent,
        events: evs,
        offline: (PT_OFFLINE[a.id] || []).filter(o => true),  // could narrow further
        label: <SubAgentLabel agent={a} segments={segsWithAgent} range={visRange} isLead={a.isLead} />,
      });
    });
    const allSegs = subAgents.flatMap(x => x.segments);
    const allEvs = subAgents.flatMap(x => x.events);
    const totalLoad = subAgents.reduce((acc, x) => acc + workload(x.segments, visRange), 0);
    return {
      moduleName: m,
      head: {
        key: `module:${m}`,
        kind: "module-head",
        sortName: m,
        moduleName: m,
        moduleColor: moduleColor(m),
        segments: allSegs,
        events: allEvs,
        label: <ModuleTrackLabel moduleName={m} segments={allSegs} />,
      },
      subAgents,
      totalLoad,
    };
  });

  // Sort modules
  moduleData.sort((a, b) => {
    if (sortMode === "alphaDesc") return b.moduleName.localeCompare(a.moduleName, "ru");
    if (sortMode === "loadDesc") return b.totalLoad - a.totalLoad;
    if (sortMode === "loadAsc") return a.totalLoad - b.totalLoad;
    return a.moduleName.localeCompare(b.moduleName, "ru");
  });
  // Flatten + sort sub-agents within each module
  const out = [];
  moduleData.forEach((md) => {
    out.push(md.head);
    applySort(md.subAgents, sortMode, visRange).forEach((sa) => out.push(sa));
  });
  return out;
}

function buildRowsStats({ mode, sortMode, period }) {
  const pdef = periodDef(period);
  const days = pdef.days;

  if (mode === "agents") {
    const items = TEAM.map((a) => {
      const history = genHistory(a.id, days);
      const eventCounts = genEventCounts(a.id, days);
      const agg = aggregateHistory(history);
      const prevAgg = aggregateHistory(genHistory(a.id, days * 2).slice(days));  // prev period
      const trend = prevAgg.total > 0 ? ((agg.total - prevAgg.total) / prevAgg.total) * 100 : 0;
      const totalEvents = eventCounts.reduce((s, x) => s + x.events, 0);
      const totalAlerts = eventCounts.reduce((s, x) => s + x.alerts, 0);
      const allSegs = Object.values(history).flatMap(h => h);
      const segmentCount = history.reduce((s, h) => s + (h.total > 0 ? 1 : 0), 0); // simplified
      return {
        key: `agent:${a.id}`,
        kind: "stats-agent",
        sortName: a.name,
        agent: a,
        history,
        eventCounts,
        weekBuckets: period === "year" ? bucketHistory(history, pdef.bucket) : null,
        stats: {
          hours: Math.round(agg.total / 60),
          segments: Math.round(history.length * 1.5),
          events: totalEvents,
          alerts: totalAlerts,
          trend: Math.round(trend),
        },
        segments: history,  // for sort purpose
        label: <AgentTrackLabel agent={a} alerts={[]} />,
      };
    });
    if (sortMode === "alphaAsc" || sortMode === "alphaDesc") {
      items.sort((a, b) => a.sortName.localeCompare(b.sortName, "ru"));
      if (sortMode === "alphaDesc") items.reverse();
    } else if (sortMode === "loadAsc" || sortMode === "loadDesc") {
      items.sort((a, b) => a.stats.hours - b.stats.hours);
      if (sortMode === "loadDesc") items.reverse();
    }
    return items;
  }
  // Modules stats — aggregate per module across agents
  const allModules = Array.from(new Set(
    Object.values(PT_AGENT_SEGMENTS).flatMap(segs => segs.map(s => s.module))
  ));
  const data = allModules.map((m) => {
    const agentsInModule = TEAM.filter(a =>
      (PT_AGENT_SEGMENTS[a.id] || []).some(s => s.module === m)
    );
    // synthesize module history: sum agent histories scaled by their fraction in this module
    const moduleHistory = Array.from({ length: days }, () => ({
      planning: 0, "in-progress": 0, review: 0, testing: 0, research: 0, blocked: 0, total: 0,
    }));
    agentsInModule.forEach((a) => {
      const h = genHistory(a.id, days);
      h.forEach((day, i) => {
        const factor = 0.4 + 0.5 * Math.random();  // each agent puts 40-90% of work to this module on a given day
        Object.keys(moduleHistory[i]).forEach((k) => {
          moduleHistory[i][k] += Math.round((day[k] || 0) * factor);
        });
      });
    });
    const agg = aggregateHistory(moduleHistory);
    const eventCounts = Array.from({ length: days }, (_, i) => ({
      events: agentsInModule.reduce((s, a) => s + (genEventCounts(a.id, days)[i].events || 0), 0),
      alerts: agentsInModule.reduce((s, a) => s + (genEventCounts(a.id, days)[i].alerts || 0), 0),
    }));
    const totalEvents = eventCounts.reduce((s, x) => s + x.events, 0);
    const totalAlerts = eventCounts.reduce((s, x) => s + x.alerts, 0);
    const trend = Math.round((Math.random() - 0.5) * 30);
    return {
      moduleName: m,
      head: {
        key: `module:${m}`,
        kind: "stats-module-head",
        sortName: m,
        moduleName: m,
        moduleColor: moduleColor(m),
        history: moduleHistory,
        eventCounts,
        weekBuckets: period === "year" ? bucketHistory(moduleHistory, pdef.bucket) : null,
        stats: {
          hours: Math.round(agg.total / 60),
          segments: agentsInModule.length,
          events: totalEvents,
          alerts: totalAlerts,
          trend,
        },
        segments: moduleHistory,
        label: <ModuleTrackLabel moduleName={m} segments={[]} />,
      },
      subAgents: agentsInModule.map((a) => {
        const h = genHistory(a.id, days);
        const aagg = aggregateHistory(h);
        const ec = genEventCounts(a.id, days);
        return {
          key: `module:${m}/agent:${a.id}`,
          kind: "stats-module-sub",
          sortName: a.name,
          agent: a,
          history: h,
          eventCounts: ec,
          weekBuckets: period === "year" ? bucketHistory(h, pdef.bucket) : null,
          stats: {
            hours: Math.round(aagg.total / 60 * 0.6),
            segments: Math.max(1, Math.round(h.filter(x => x.total > 0).length * 0.4)),
            events: Math.round(ec.reduce((s, x) => s + x.events, 0) * 0.5),
            alerts: ec.reduce((s, x) => s + x.alerts, 0),
            trend: Math.round((Math.random() - 0.5) * 30),
          },
          segments: h,
          label: <SubAgentLabel agent={a} segments={[]} range={[0, 1440]} isLead={a.isLead} />,
        };
      }),
    };
  });

  // Sort modules
  data.sort((a, b) => {
    if (sortMode === "alphaDesc") return b.moduleName.localeCompare(a.moduleName, "ru");
    if (sortMode === "loadAsc") return a.head.stats.hours - b.head.stats.hours;
    if (sortMode === "loadDesc") return b.head.stats.hours - a.head.stats.hours;
    return a.moduleName.localeCompare(b.moduleName, "ru");
  });
  const out = [];
  data.forEach((md) => {
    out.push(md.head);
    let subs = md.subAgents.slice();
    if (sortMode === "alphaAsc" || sortMode === "alphaDesc") {
      subs.sort((a, b) => a.sortName.localeCompare(b.sortName, "ru"));
      if (sortMode === "alphaDesc") subs.reverse();
    } else if (sortMode === "loadAsc" || sortMode === "loadDesc") {
      subs.sort((a, b) => a.stats.hours - b.stats.hours);
      if (sortMode === "loadDesc") subs.reverse();
    }
    subs.forEach((s) => out.push(s));
  });
  return out;
}

// ============================================================
// MAIN
// ============================================================
function Partitura({ embedded = false, maxHeight, alertsForLabels = [], stickyTop = 96 }) {
  const [mode, setMode] = useState("agents");
  const [period, setPeriod] = useState("day");
  const [sortMode, setSortMode] = useState("default");
  const [scale, setScale] = useState(0.9);
  const [filters, setFilters] = useState({
    new_task: true, state_change: true, assigned: true,
    label: true, comment: true, commit: true, alert: true,
    _blocked: true,
  });
  const [selectedKey, setSelectedKey] = useState(null);
  const [selectedSeg, setSelectedSeg] = useState(null);    // { seg, rowKey }
  const [highlightEvKey, setHighlightEvKey] = useState(null);
  const [clusterPopover, setClusterPopover] = useState(null);
  const [panelW, setPanelW] = useState(340);
  const [visRange, setVisRange] = useState([0, 1440]);
  const [viewportW, setViewportW] = useState(900);

  const containerRef = useRef(null);
  const scrollRef = useRef(null);
  const rafRef = useRef(0);

  const isDay = period === "day";

  // Reset selection on mode/period switch
  useEffect(() => {
    setSelectedKey(null); setSelectedSeg(null);
    setHighlightEvKey(null); setClusterPopover(null);
  }, [mode, period]);

  // Measure viewport
  useLayoutEffect(() => {
    const measure = () => {
      if (scrollRef.current) {
        const w = scrollRef.current.clientWidth - LABEL_W;
        if (w > 100) setViewportW(w);
      }
    };
    measure();
    window.addEventListener("resize", measure);
    return () => window.removeEventListener("resize", measure);
  }, []);

  // Resize panel
  const onDragStart = (e) => {
    e.preventDefault();
    const startX = e.clientX;
    const startW = panelW;
    const onMove = (ev) => {
      const next = Math.max(240, Math.min(640, startW + (startX - ev.clientX)));
      setPanelW(next);
    };
    const onUp = () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
      document.body.style.cursor = "";
      document.body.style.userSelect = "";
    };
    document.body.style.cursor = "col-resize";
    document.body.style.userSelect = "none";
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
  };

  const totalWidth = 24 * 60 * scale;

  // Recompute visible range
  const recomputeVis = () => {
    const el = scrollRef.current;
    if (!el) return;
    const start = el.scrollLeft / scale;
    const end = (el.scrollLeft + (el.clientWidth - LABEL_W)) / scale;
    setVisRange([Math.max(0, start), Math.min(1440, end)]);
  };
  const onTimelineScroll = () => {
    if (rafRef.current) cancelAnimationFrame(rafRef.current);
    rafRef.current = requestAnimationFrame(recomputeVis);
  };
  useEffect(() => { recomputeVis(); }, [scale]);

  // Visible events (kind filters)
  const visibleEvents = useMemo(() => PT_EVENTS.filter(ev => filters[ev.kind]), [filters]);

  // Build rows
  const rows = useMemo(() => {
    if (isDay) {
      return buildRowsDay({ mode, sortMode, filters, visibleEvents, alertsForLabels, visRange });
    }
    return buildRowsStats({ mode, sortMode, period });
  }, [isDay, mode, sortMode, period, filters, visibleEvents, alertsForLabels, visRange]);

  // All visible events for the panel
  const allVisibleEvents = useMemo(() => {
    const seen = new Set();
    const out = [];
    rows.forEach((r) => {
      if (!r.events) return;
      r.events.forEach((ev) => {
        const k = `${ev.t}_${ev.agent}_${ev.kind}_${ev.seq}`;
        if (!seen.has(k)) { seen.add(k); out.push(ev); }
      });
    });
    return out;
  }, [rows]);

  // Scroll-to-now
  const scrollToNow = () => {
    if (!scrollRef.current) return;
    const target = PT_NOW_MIN * scale - viewportW / 2 + 100;
    scrollRef.current.scrollTo({ left: Math.max(0, target), behavior: "smooth" });
  };

  // Keep center stable on zoom
  const prevScaleRef = useRef(scale);
  useEffect(() => {
    const prev = prevScaleRef.current;
    if (!scrollRef.current || prev === scale) return;
    const ratio = scale / prev;
    const left = scrollRef.current.scrollLeft;
    const centerPx = left + scrollRef.current.clientWidth / 2;
    scrollRef.current.scrollLeft = centerPx * ratio - scrollRef.current.clientWidth / 2;
    prevScaleRef.current = scale;
  }, [scale]);

  // Initial scroll
  useEffect(() => {
    if (scrollRef.current && isDay) {
      scrollRef.current.scrollLeft = 7.5 * 60 * scale;
      requestAnimationFrame(recomputeVis);
    }
    // eslint-disable-next-line
  }, [isDay]);

  // Selection helpers
  const onBarClick = (seg, row) => {
    setSelectedKey(row.key);
    setSelectedSeg({ seg, rowKey: row.key });
    setHighlightEvKey(null);
    setClusterPopover(null);
  };
  const onEventGroup = (events, row, e) => {
    if (events.length === 1) {
      const ev = events[0];
      setSelectedKey(row.key);
      setSelectedSeg(null);
      setHighlightEvKey(`${ev.t}_${ev.agent}_${ev.kind}_${ev.seq}`);
      setClusterPopover(null);
    } else {
      const rect = e.target.getBoundingClientRect ? e.target.getBoundingClientRect() : null;
      const x = rect ? rect.right + 6 : e.clientX || 200;
      const y = rect ? rect.top : e.clientY || 200;
      setClusterPopover({ events, rowKey: row.key, x, y });
    }
  };
  const onRowClick = (row) => {
    if (selectedKey === row.key) {
      setSelectedKey(null); setSelectedSeg(null); setHighlightEvKey(null);
    } else {
      setSelectedKey(row.key); setSelectedSeg(null); setHighlightEvKey(null);
    }
    setClusterPopover(null);
  };
  const pickFromPopover = (ev) => {
    setSelectedKey(clusterPopover.rowKey);
    setSelectedSeg(null);
    setHighlightEvKey(`${ev.t}_${ev.agent}_${ev.kind}_${ev.seq}`);
    setClusterPopover(null);
  };

  const computedStyle = maxHeight ? { height: maxHeight } : undefined;

  return (
    <div ref={containerRef} className={"pt-root" + (embedded ? " pt-embedded" : "")}>
      <Controls
        mode={mode} setMode={setMode}
        period={period} setPeriod={setPeriod}
        sortMode={sortMode} setSortMode={setSortMode}
        scale={scale} setScale={(s) => setScale(Math.max(SCALE_MIN, Math.min(SCALE_MAX, s)))}
        viewportW={viewportW}
        filters={filters} setFilters={setFilters}
        scrollToNow={scrollToNow}
        totalRows={rows.length}
        isDay={isDay}
      />

      <div className="pt-main" style={computedStyle}>
        {isDay ? (
          <div ref={scrollRef} onScroll={onTimelineScroll} className="pt-timeline-scroll">
            <div className="pt-ruler-row">
              <div className="pt-label-corner">{mode === "agents" ? "Агент / роль" : "Модуль / агенты"}</div>
              <div style={{ width: totalWidth, position: "relative", flex: "0 0 auto" }}>
                <Ruler scale={scale} width={totalWidth} />
              </div>
            </div>
            <div style={{ position: "relative" }}>
              {rows.map((r) => (
                <Row
                  key={r.key}
                  row={r}
                  scale={scale}
                  totalWidth={totalWidth}
                  isSelected={selectedKey === r.key}
                  selectedSegKey={selectedSeg && selectedSeg.rowKey === r.key ? `${selectedSeg.seg.seq}_${selectedSeg.seg.start}` : null}
                  highlightEvKey={highlightEvKey}
                  onClickRow={() => onRowClick(r)}
                  onClickBar={(seg) => onBarClick(seg, r)}
                  onClickEventGroup={(g, e) => onEventGroup(g, r, e)}
                />
              ))}
              <div className="pt-now-line" style={{ left: LABEL_W + PT_NOW_MIN * scale }}>
                <div className="pt-now-line-lbl">СЕЙЧАС · {fmtHM(PT_NOW_MIN)}</div>
              </div>
            </div>
          </div>
        ) : (
          // Stats mode — no horizontal scroll, no ruler
          <div className="pt-timeline-scroll">
            <div className="pt-ruler-row">
              <div className="pt-label-corner">{mode === "agents" ? "Агент / роль" : "Модуль / агенты"}</div>
              <div style={{ padding: "8px 16px", flex: 1, fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-muted)", textTransform: "uppercase", letterSpacing: "0.05em", borderBottom: "1px solid var(--border)", background: "var(--bg-elev)" }}>
                период: {periodDef(period).label} ({periodDef(period).days} дн.) · акцент на статистику и динамику
              </div>
            </div>
            <div style={{ position: "relative" }}>
              {rows.map((r) => (
                <StatsRow
                  key={r.key}
                  row={r}
                  period={period}
                  isSelected={selectedKey === r.key}
                  onClickRow={() => onRowClick(r)}
                />
              ))}
            </div>
          </div>
        )}

        <div className="pt-resize-handle" onMouseDown={onDragStart} title="Потяни">
          <div className="pt-resize-handle-grip" />
        </div>

        <EventsPanel
          width={panelW}
          events={allVisibleEvents}
          rows={rows}
          selectedKey={selectedKey}
          setSelectedKey={(k) => { setSelectedKey(k); setSelectedSeg(null); setHighlightEvKey(null); }}
          visRange={visRange}
          mode={mode}
          period={period}
          selectedSeg={selectedSeg}
          onClearSeg={() => setSelectedSeg(null)}
          highlightEventKey={highlightEvKey}
          agents={TEAM}
          stickyTop={stickyTop}
        />
      </div>

      {clusterPopover && (
        <ClusterPopover
          events={clusterPopover.events}
          rowKey={clusterPopover.rowKey}
          x={clusterPopover.x}
          y={clusterPopover.y}
          onPickEvent={pickFromPopover}
          onClose={() => setClusterPopover(null)}
        />
      )}
    </div>
  );
}

window.Partitura = Partitura;
