/* ════════════════════════════════════════════════════════════════════════
   MSINDAHA Operations — UI PRIMITIVES
   Overlays (modal, drawer, popover, menus), feedback (toasts, confirm),
   form controls (date field, period filter, filter menus, segmented
   controls) and small display atoms (avatars, badges, due chips).
════════════════════════════════════════════════════════════════════════ */

const isTypingTarget = (el) =>
  !!el &&
  (el.tagName === "INPUT" ||
    el.tagName === "TEXTAREA" ||
    el.tagName === "SELECT" ||
    el.isContentEditable);

/* ─── Layer stack — Esc always closes only the topmost overlay (a date
   popover inside a modal inside a drawer closes one level at a time). ─ */
const __layers = [];
document.addEventListener(
  "keydown",
  (e) => {
    if (e.key !== "Escape" || !__layers.length) return;
    // First Esc while typing into a non-empty field only leaves the field,
    // so a half-written comment or form isn't thrown away by accident.
    const el = document.activeElement;
    if (isTypingTarget(el) && el.value) {
      el.blur();
      return;
    }
    e.preventDefault();
    e.stopPropagation();
    __layers[__layers.length - 1].close();
  },
  true,
);
const topLayerId = () => (__layers.length ? __layers[__layers.length - 1].id : null);
function useLayer(active, onClose) {
  const idRef = React.useRef(`layer-${uid4()}`);
  const cbRef = React.useRef(onClose);
  cbRef.current = onClose;
  React.useEffect(() => {
    if (!active) return;
    const entry = { id: idRef.current, close: () => cbRef.current?.() };
    __layers.push(entry);
    return () => {
      const i = __layers.indexOf(entry);
      if (i >= 0) __layers.splice(i, 1);
    };
  }, [active]);
  return idRef.current;
}

/* ─── Presence — keeps an overlay mounted for its exit animation. ───── */
function usePresence(open, ms = 200) {
  const [mounted, setMounted] = React.useState(open);
  React.useEffect(() => {
    if (open) {
      setMounted(true);
      return;
    }
    const t = setTimeout(() => setMounted(false), ms);
    return () => clearTimeout(t);
  }, [open, ms]);
  return { mounted: open || mounted, closing: !open && mounted };
}
// Keeps showing the last content while an overlay animates out, so a parent
// that clears its state on close doesn't blank the panel mid-animation.
function useStickyWhileOpen(open, value) {
  const ref = React.useRef(value);
  if (open) ref.current = value;
  return ref.current;
}

const Portal = ({ children }) => ReactDOM.createPortal(children, document.body);

/* ─── Focus helpers ─────────────────────────────────────────────────── */
const FOCUSABLE =
  'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
function useFocusReturn(active) {
  React.useEffect(() => {
    if (!active) return;
    const prev = document.activeElement;
    return () => {
      if (prev && typeof prev.focus === "function" && document.contains(prev)) {
        try {
          prev.focus({ preventScroll: true });
        } catch {}
      }
    };
  }, [active]);
}
function trapTab(e, container) {
  if (e.key !== "Tab" || !container) return;
  const items = [...container.querySelectorAll(FOCUSABLE)].filter(
    (el) => el.offsetParent !== null,
  );
  if (!items.length) return;
  const first = items[0];
  const last = items[items.length - 1];
  if (e.shiftKey && document.activeElement === first) {
    e.preventDefault();
    last.focus();
  } else if (!e.shiftKey && document.activeElement === last) {
    e.preventDefault();
    first.focus();
  }
}

/* ═══════════════════════════ MODAL ═══════════════════════════
   size: sm | md (default) | lg | xl. `dirty` protects unsaved forms: a
   click on the backdrop shakes the dialog instead of discarding input. */
function Modal({
  open,
  onClose,
  title,
  subtitle,
  icon: Icon,
  size = "md",
  children,
  footer,
  dirty = false,
  className,
}) {
  const { mounted, closing } = usePresence(open, 180);
  const content = useStickyWhileOpen(open, { title, subtitle, children, footer, Icon });
  const boxRef = React.useRef(null);
  const [shake, setShake] = React.useState(false);
  const attemptClose = () => {
    if (dirty) {
      setShake(true);
      setTimeout(() => setShake(false), 400);
      return;
    }
    onClose?.();
  };
  useLayer(open, attemptClose);
  useFocusReturn(open);
  React.useEffect(() => {
    if (!open) return;
    const t = setTimeout(() => {
      const box = boxRef.current;
      if (!box || box.contains(document.activeElement)) return;
      const auto = box.querySelector("[data-autofocus]") || box.querySelector(FOCUSABLE);
      auto?.focus?.();
    }, 30);
    return () => clearTimeout(t);
  }, [open]);
  if (!mounted) return null;
  const C = content;
  return (
    <Portal>
      <div
        className={cx("modal-overlay", closing && "closing")}
        onMouseDown={(e) => {
          if (e.target === e.currentTarget) attemptClose();
        }}
      >
        <div
          ref={boxRef}
          className={cx("modal", `modal-${size}`, shake && "shake", closing && "closing", className)}
          role="dialog"
          aria-modal="true"
          aria-label={typeof C.title === "string" ? C.title : undefined}
          onKeyDown={(e) => trapTab(e, boxRef.current)}
        >
          {C.title != null && (
            <div className="modal-hdr">
              <div className="modal-hdr-text">
                {C.Icon && (
                  <span className="modal-hdr-icon">
                    <C.Icon />
                  </span>
                )}
                <div>
                  <h3>{C.title}</h3>
                  {C.subtitle && <p className="modal-sub">{C.subtitle}</p>}
                </div>
              </div>
              <button className="icon-btn" onClick={() => onClose?.()} aria-label="Close">
                <IC.X />
              </button>
            </div>
          )}
          <div className="modal-body">{C.children}</div>
          {C.footer && <div className="modal-footer">{C.footer}</div>}
        </div>
      </div>
    </Portal>
  );
}

/* ═══════════════════════════ DRAWER ═══════════════════════════
   Right-hand slide-over used for ticket and task details. */
function Drawer({ open, onClose, children, width = 920, label }) {
  const { mounted, closing } = usePresence(open, 240);
  const content = useStickyWhileOpen(open, children);
  const panelRef = React.useRef(null);
  const layerId = useLayer(open, onClose);
  useFocusReturn(open);
  React.useEffect(() => {
    if (open) setTimeout(() => panelRef.current?.focus({ preventScroll: true }), 40);
  }, [open]);
  if (!mounted) return null;
  return (
    <Portal>
      <div
        className={cx("drawer-overlay", closing && "closing")}
        onMouseDown={(e) => {
          if (e.target === e.currentTarget) onClose?.();
        }}
      >
        <aside
          ref={panelRef}
          tabIndex={-1}
          className={cx("drawer", closing && "closing")}
          style={{ width: `min(${width}px, 100vw)` }}
          role="dialog"
          aria-modal="true"
          aria-label={label}
          data-layer={layerId}
        >
          {content}
        </aside>
      </div>
    </Portal>
  );
}

/* ═══════════════════════════ POPOVER ═══════════════════════════
   Anchored to a trigger element, portalled to <body> (immune to clipping
   and to transformed ancestors), flips above the trigger near the bottom
   edge, and stays glued to it while anything scrolls. */
function Popover({
  open,
  anchorRef,
  onClose,
  children,
  placement = "bottom-start",
  width,
  minWidth,
  className,
  offset = 6,
  matchWidth = false,
}) {
  const { mounted, closing } = usePresence(open, 140);
  const popRef = React.useRef(null);
  const [pos, setPos] = React.useState({ top: -9999, left: -9999, up: false, ready: false });
  useLayer(open, onClose);

  React.useLayoutEffect(() => {
    if (!mounted) return;
    const update = () => {
      const anchor = anchorRef.current;
      const pop = popRef.current;
      if (!anchor || !pop) return;
      const a = anchor.getBoundingClientRect();
      const pw = pop.offsetWidth;
      const ph = pop.offsetHeight;
      const vw = window.innerWidth;
      const vh = window.innerHeight;
      let left = placement.endsWith("end") ? a.right - pw : a.left;
      left = Math.max(8, Math.min(left, vw - pw - 8));
      let top = a.bottom + offset;
      let up = false;
      if (top + ph > vh - 8 && a.top - offset - ph >= 8) {
        top = a.top - offset - ph;
        up = true;
      }
      top = Math.max(8, Math.min(top, vh - ph - 8));
      setPos({ top, left, up, ready: true, aw: a.width });
    };
    update();
    const ro = new ResizeObserver(update);
    if (popRef.current) ro.observe(popRef.current);
    window.addEventListener("resize", update);
    window.addEventListener("scroll", update, true);
    return () => {
      ro.disconnect();
      window.removeEventListener("resize", update);
      window.removeEventListener("scroll", update, true);
    };
  }, [mounted, placement, offset]);

  React.useEffect(() => {
    if (!open) return;
    const onDown = (e) => {
      if (popRef.current?.contains(e.target)) return;
      if (anchorRef.current?.contains(e.target)) return;
      onClose?.();
    };
    document.addEventListener("mousedown", onDown, true);
    return () => document.removeEventListener("mousedown", onDown, true);
  }, [open]);

  if (!mounted) return null;
  return (
    <Portal>
      <div
        ref={popRef}
        className={cx("popover", pos.up && "up", closing && "closing", className)}
        style={{
          top: pos.top,
          left: pos.left,
          width: matchWidth ? pos.aw : width,
          minWidth,
          visibility: pos.ready ? "visible" : "hidden",
        }}
        role="dialog"
      >
        {children}
      </div>
    </Portal>
  );
}

/* Dropdown = trigger + popover with its own open state. */
function Dropdown({ trigger, children, width = 240, placement = "bottom-start", className, matchWidth }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  const close = React.useCallback(() => setOpen(false), []);
  return (
    <>
      {trigger({ ref, open, toggle: () => setOpen((o) => !o), close })}
      <Popover
        open={open}
        anchorRef={ref}
        onClose={close}
        width={width}
        placement={placement}
        className={className}
        matchWidth={matchWidth}
      >
        {typeof children === "function" ? children({ close }) : children}
      </Popover>
    </>
  );
}

/* Menu list with arrow-key navigation between items. */
function MenuList({ children, className }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const first = ref.current?.querySelector('[role="menuitem"].active, [role="menuitem"]:not([disabled])');
    setTimeout(() => first?.focus({ preventScroll: true }), 20);
  }, []);
  const onKeyDown = (e) => {
    if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key)) return;
    const items = [...ref.current.querySelectorAll('[role="menuitem"]:not([disabled])')];
    if (!items.length) return;
    e.preventDefault();
    const i = items.indexOf(document.activeElement);
    let n = 0;
    if (e.key === "ArrowDown") n = i < 0 ? 0 : (i + 1) % items.length;
    if (e.key === "ArrowUp") n = i <= 0 ? items.length - 1 : i - 1;
    if (e.key === "End") n = items.length - 1;
    items[n].focus();
  };
  return (
    <div ref={ref} className={cx("menu", className)} role="menu" onKeyDown={onKeyDown}>
      {children}
    </div>
  );
}
function MenuItem({ icon: Icon, children, hint, active, danger, disabled, onSelect, right, checkable }) {
  return (
    <button
      type="button"
      role="menuitem"
      className={cx("menu-item", active && "active", danger && "danger")}
      disabled={disabled}
      onClick={onSelect}
    >
      {checkable && (
        <span className="menu-check">{active && <IC.Check />}</span>
      )}
      {Icon && (
        <span className="menu-icon">
          <Icon />
        </span>
      )}
      <span className="menu-label">{children}</span>
      {hint != null && <span className="menu-hint">{hint}</span>}
      {right}
    </button>
  );
}
const MenuSep = () => <div className="menu-sep" />;
const MenuLabel = ({ children }) => <div className="menu-label-head">{children}</div>;

/* ═══════════════════════ TOASTS ═══════════════════════ */
const ToastCtx = React.createContext(() => {});
const useToast = () => React.useContext(ToastCtx);
const TOAST_ICONS = {
  info: IC.Info,
  success: IC.CheckCircle,
  warning: IC.Alert,
  error: IC.Alert,
  assign: IC.Target,
  comment: IC.Comment,
  bell: IC.Bell,
};
function ToastProvider({ children }) {
  const [toasts, setToasts] = React.useState([]);
  const timers = React.useRef({});
  const dismiss = React.useCallback((id) => {
    clearTimeout(timers.current[id]);
    setToasts((ts) => ts.map((t) => (t.id === id ? { ...t, leaving: true } : t)));
    setTimeout(() => setToasts((ts) => ts.filter((t) => t.id !== id)), 220);
  }, []);
  const schedule = React.useCallback(
    (id, ms) => {
      clearTimeout(timers.current[id]);
      if (ms > 0) timers.current[id] = setTimeout(() => dismiss(id), ms);
    },
    [dismiss],
  );
  const push = React.useCallback(
    (t) => {
      const id = `t-${Date.now()}-${uid4()}`;
      const toast = { type: "info", duration: 5000, ...t, id };
      setToasts((ts) => [...ts.filter((x) => !x.leaving).slice(-3), toast]);
      schedule(id, toast.duration);
      return id;
    },
    [schedule],
  );
  return (
    <ToastCtx.Provider value={push}>
      {children}
      <Portal>
        <div className="toast-stack" aria-live="polite">
          {toasts.map((t) => {
            const Icon = TOAST_ICONS[t.type] || IC.Info;
            return (
              <div
                key={t.id}
                className={cx("toast", t.type, t.leaving && "leaving")}
                onMouseEnter={() => clearTimeout(timers.current[t.id])}
                onMouseLeave={() => schedule(t.id, 2500)}
              >
                <div className="toast-icon">
                  <Icon />
                </div>
                <div className="toast-body">
                  <div className="toast-title">{t.title}</div>
                  {t.msg && <div className="toast-msg">{t.msg}</div>}
                  {t.action && (
                    <button
                      className="toast-action"
                      onClick={() => {
                        t.action.onClick();
                        dismiss(t.id);
                      }}
                    >
                      {t.action.label}
                    </button>
                  )}
                </div>
                <button className="toast-close" onClick={() => dismiss(t.id)} aria-label="Dismiss">
                  <IC.X />
                </button>
              </div>
            );
          })}
        </div>
      </Portal>
    </ToastCtx.Provider>
  );
}

/* ═══════════════════════ CONFIRM DIALOG ═══════════════════════
   const ok = await confirm({ title, message, confirmLabel, danger }) */
const ConfirmCtx = React.createContext(async () => false);
const useConfirm = () => React.useContext(ConfirmCtx);
function ConfirmProvider({ children }) {
  const [state, setState] = React.useState({ open: false });
  const resolveRef = React.useRef(null);
  const confirm = React.useCallback(
    (opts) =>
      new Promise((resolve) => {
        resolveRef.current = resolve;
        setState({ ...opts, open: true });
      }),
    [],
  );
  const finish = (v) => {
    resolveRef.current?.(v);
    resolveRef.current = null;
    setState((s) => ({ ...s, open: false }));
  };
  return (
    <ConfirmCtx.Provider value={confirm}>
      {children}
      <Modal
        open={state.open}
        onClose={() => finish(false)}
        title={state.title || "Are you sure?"}
        icon={state.danger ? IC.Alert : IC.Info}
        size="sm"
        className={state.danger ? "modal-danger" : ""}
        footer={
          <>
            <button className="btn btn-ghost" onClick={() => finish(false)}>
              {state.cancelLabel || "Cancel"}
            </button>
            <button
              className={cx("btn", state.danger ? "btn-danger-solid" : "btn-primary")}
              onClick={() => finish(true)}
              data-autofocus
            >
              {state.confirmLabel || "Confirm"}
            </button>
          </>
        }
      >
        <div className="confirm-msg">{state.message}</div>
      </Modal>
    </ConfirmCtx.Provider>
  );
}

/* ═══════════════════════ DISPLAY ATOMS ═══════════════════════ */
function Avatar({ name, email, size = 26, title, className }) {
  return (
    <span
      className={cx("avatar", className)}
      style={{
        width: size,
        height: size,
        fontSize: Math.max(9, Math.round(size * 0.4)),
        background: colorFor(email || name || "?"),
      }}
      title={title ?? (name || email || "")}
    >
      {initialsOf(name, email)}
    </span>
  );
}
function AvatarStack({ people, max = 3, size = 22 }) {
  const shown = people.slice(0, max);
  const extra = people.length - shown.length;
  return (
    <span className="avatar-stack" title={people.map((p) => p.name).join(", ")}>
      {shown.map((p) => (
        <Avatar key={p.uid || p.email || p.name} name={p.name} email={p.email} size={size} title="" />
      ))}
      {extra > 0 && (
        <span className="avatar avatar-more" style={{ width: size, height: size, fontSize: Math.round(size * 0.38) }}>
          +{extra}
        </span>
      )}
    </span>
  );
}
function StatusBadge({ status, kind = "ticket", className }) {
  const tone = (kind === "task" ? TASK_TONE : STATUS_TONE)[status] || "gray";
  const label = kind === "task" ? taskStatusLabel(status) : statusLabel(status);
  return (
    <span className={cx("badge", "status-badge", `tone-${tone}`, className)}>
      <span className="status-dot" />
      {label}
    </span>
  );
}
function PriorityIcon({ p = "medium" }) {
  if (p === "critical")
    return (
      <span className="prio-ic prio-critical" title="Critical priority">
        <svg viewBox="0 0 16 16" aria-hidden="true">
          <rect x="1.5" y="1.5" width="13" height="13" rx="3.5" fill="currentColor" />
          <path d="M8 4.6v4.1" stroke="#fff" strokeWidth="1.8" strokeLinecap="round" />
          <circle cx="8" cy="11.2" r="1.05" fill="#fff" />
        </svg>
      </span>
    );
  const n = { low: 1, medium: 2, high: 3 }[p] || 2;
  return (
    <span className={cx("prio-ic", `prio-${p}`)} title={`${priorityLabel(p)} priority`}>
      <svg viewBox="0 0 16 16" aria-hidden="true">
        {[0, 1, 2].map((i) => {
          const h = 4 + i * 3.5;
          return (
            <rect
              key={i}
              x={2 + i * 4.5}
              y={14 - h}
              width="3"
              height={h}
              rx="1"
              fill="currentColor"
              opacity={i < n ? 1 : 0.22}
            />
          );
        })}
      </svg>
    </span>
  );
}
function PriorityTag({ p }) {
  return (
    <span className={cx("prio-tag", `prio-${p}`)}>
      <PriorityIcon p={p} />
      {priorityLabel(p)}
    </span>
  );
}
function DueChip({ due, done, empty = null, compact }) {
  const info = dueInfo(due, done);
  if (!info) return empty;
  return (
    <span className={cx("due-chip", `due-${info.state}`, compact && "compact")} title={info.title}>
      {info.state === "overdue" ? <IC.Alert /> : <IC.Calendar />}
      {info.label}
    </span>
  );
}
function DeptPill({ assignment }) {
  if (!assignment) return null;
  return (
    <span className="dept-pill" title="Shared assignment — the first person to finish completes it for everyone">
      <IC.Layers />
      {assignment.mode === "company"
        ? "Entire company"
        : assignment.departments.map(deptLabelOf).join(", ")}
    </span>
  );
}
// Compact "who is this assigned to" cell for lists and cards.
function AssigneeCell({ item, users, size = 22, showName = true }) {
  const a = assignmentOf(item);
  if (isSharedAssignment(a)) return <DeptPill assignment={a} />;
  let people = resolveAssignees(a, users);
  if (!people.length && item.assignee?.uid)
    people = [{ uid: item.assignee.uid, name: item.assignee.name, email: item.assignee.email }];
  if (!people.length)
    return (
      <span className="unassigned">
        <span className="avatar-ph" style={{ width: size, height: size }} />
        {showName && "Unassigned"}
      </span>
    );
  if (people.length === 1)
    return (
      <span className="assignee-one">
        <Avatar name={people[0].name} email={people[0].email} size={size} />
        {showName && <span className="assignee-name">{people[0].name}</span>}
      </span>
    );
  return (
    <span className="assignee-many">
      <AvatarStack people={people} size={size} />
      {showName && <span className="assignee-name">{people.length} people</span>}
    </span>
  );
}
const Spinner = ({ size = 18 }) => (
  <span className="spinner" style={{ width: size, height: size }} aria-label="Loading" />
);
const Kbd = ({ children }) => <kbd className="kbd">{children}</kbd>;
function EmptyState({ icon: Icon = IC.Inbox, title, children, action, compact }) {
  return (
    <div className={cx("empty-state", compact && "compact")}>
      <span className="empty-icon">
        <Icon />
      </span>
      {title && <div className="empty-title">{title}</div>}
      {children && <div className="empty-text">{children}</div>}
      {action}
    </div>
  );
}
const Skeleton = ({ w = "100%", h = 12, r = 6, style }) => (
  <span className="skeleton" style={{ width: w, height: h, borderRadius: r, ...style }} />
);

/* ═══════════════════════ FORM CONTROLS ═══════════════════════ */
function Field({ label, hint, error, required, children, className }) {
  return (
    <div className={cx("form-group", error && "has-error", className)}>
      {label && (
        <label className="form-label">
          {label}
          {required && <span className="req">*</span>}
        </label>
      )}
      {children}
      {error ? <div className="form-error">{error}</div> : hint ? <div className="form-hint">{hint}</div> : null}
    </div>
  );
}

const AutosizeTextarea = React.forwardRef(function AutosizeTextarea(props, ref) {
  const { value, minRows = 3, maxRows = 14, className } = props;
  const rest = omit(props, ["value", "minRows", "maxRows", "className"]);
  const inner = React.useRef(null);
  React.useImperativeHandle(ref, () => inner.current);
  React.useLayoutEffect(() => {
    const el = inner.current;
    if (!el) return;
    el.style.height = "auto";
    const cs = getComputedStyle(el);
    const lh = parseFloat(cs.lineHeight) || 20;
    const pad = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom) + 2;
    const max = lh * maxRows + pad;
    const min = lh * minRows + pad;
    el.style.height = `${Math.max(min, Math.min(el.scrollHeight + 2, max))}px`;
    el.style.overflowY = el.scrollHeight + 2 > max ? "auto" : "hidden";
  }, [value, minRows, maxRows]);
  return <textarea ref={inner} value={value} className={cx("form-textarea", className)} {...rest} />;
});

function Switch({ checked, onChange, disabled, label }) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={!!checked}
      aria-label={label}
      disabled={disabled}
      className={cx("switch", checked && "on")}
      onClick={() => onChange(!checked)}
    >
      <span className="switch-thumb" />
    </button>
  );
}

function Segmented({ value, onChange, options, size, className }) {
  return (
    <div className={cx("segmented", size && `seg-${size}`, className)} role="tablist">
      {options.map((o) => (
        <button
          key={o.value}
          type="button"
          role="tab"
          aria-selected={o.value === value}
          className={cx("seg-btn", o.value === value && "active")}
          onClick={() => onChange(o.value)}
          title={o.title}
        >
          {o.icon && <o.icon />}
          {o.label != null && <span>{o.label}</span>}
          {o.count != null && <span className="seg-count">{o.count}</span>}
        </button>
      ))}
    </div>
  );
}

function ViewTabs({ value, onChange, tabs }) {
  return (
    <div className="view-tabs" role="tablist">
      {tabs.map((t) => (
        <button
          key={t.value}
          type="button"
          role="tab"
          aria-selected={t.value === value}
          className={cx("view-tab", t.value === value && "active")}
          onClick={() => onChange(t.value)}
        >
          {t.label}
          {t.count != null && <span className="view-tab-count">{t.count}</span>}
        </button>
      ))}
    </div>
  );
}

function SearchInput({ value, onChange, placeholder = "Search…", inputRef, shortcut = true, className }) {
  const localRef = React.useRef(null);
  const ref = inputRef || localRef;
  React.useEffect(() => {
    if (!shortcut) return;
    const onKey = (e) => {
      if (e.key !== "/" || e.ctrlKey || e.metaKey || e.altKey) return;
      if (isTypingTarget(document.activeElement) || __layers.length) return;
      e.preventDefault();
      ref.current?.focus();
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [shortcut]);
  return (
    <div className={cx("search-field", value && "has-value", className)}>
      <IC.Search />
      <input
        ref={ref}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        placeholder={placeholder}
        onKeyDown={(e) => {
          if (e.key === "Escape" && value) {
            e.stopPropagation();
            onChange("");
          }
        }}
      />
      {value ? (
        <button className="search-clear" onClick={() => onChange("")} aria-label="Clear search">
          <IC.X />
        </button>
      ) : (
        shortcut && <Kbd>/</Kbd>
      )}
    </div>
  );
}

/* FilterMenu — a chip that opens a single-select option list.
   options: [{ value, label, count?, node? }] */
function FilterMenu({ label, icon: Icon, value, options, onChange, allValue = "all", width = 230 }) {
  const active = value !== allValue;
  const current = options.find((o) => o.value === value);
  return (
    <Dropdown
      width={width}
      trigger={({ ref, toggle, open }) => (
        <span className={cx("filter-chip", active && "active", open && "open")}>
          <button ref={ref} type="button" className="filter-chip-main" onClick={toggle}>
            {Icon && <Icon />}
            <span>
              {label}
              {active && current ? <b>: {current.label}</b> : null}
            </span>
            <IC.ChevronDown />
          </button>
          {active && (
            <button
              type="button"
              className="filter-chip-clear"
              aria-label={`Clear ${label} filter`}
              onClick={() => onChange(allValue)}
            >
              <IC.X />
            </button>
          )}
        </span>
      )}
    >
      {({ close }) => (
        <MenuList>
          {options.map((o) =>
            o.sep ? (
              <MenuSep key={o.value} />
            ) : (
              <MenuItem
                key={o.value}
                checkable
                active={o.value === value}
                hint={o.count}
                onSelect={() => {
                  onChange(o.value);
                  close();
                }}
              >
                {o.node || o.label}
              </MenuItem>
            ),
          )}
        </MenuList>
      )}
    </Dropdown>
  );
}

/* ═══════════════════════ CALENDAR ═══════════════════════
   A fixed 6-week grid (stable height). Supports a single selected day, a
   committed range, and a live hover preview while a range is being picked.
   Everything is compared as local "YYYY-MM-DD" strings. */
function Calendar({ value, rangeFrom, rangeTo, pendingFrom, onPick }) {
  const focusISO = value || rangeFrom || null;
  const [view, setView] = React.useState(() => {
    const base = parseISODate(focusISO) || new Date();
    return new Date(base.getFullYear(), base.getMonth(), 1);
  });
  React.useEffect(() => {
    const base = parseISODate(focusISO);
    if (base) setView(new Date(base.getFullYear(), base.getMonth(), 1));
  }, [focusISO]);
  const [hover, setHover] = React.useState(null);
  const today = todayISO();
  const year = view.getFullYear();
  const month = view.getMonth();
  const lead = (new Date(year, month, 1).getDay() - CFG.weekStart + 7) % 7;
  const start = addDays(new Date(year, month, 1), -lead);
  const cells = Array.from({ length: 42 }, (_, i) => addDays(start, i));
  const dows = Array.from({ length: 7 }, (_, i) => DOW_SHORT[(i + CFG.weekStart) % 7]);

  let lo = rangeFrom;
  let hi = rangeTo || rangeFrom;
  if (pendingFrom && hover) {
    lo = hover < pendingFrom ? hover : pendingFrom;
    hi = hover < pendingFrom ? pendingFrom : hover;
  }
  const go = (delta) => setView(new Date(year, month + delta, 1));

  return (
    <div className="cal" onMouseLeave={() => setHover(null)}>
      <div className="cal-head">
        <button type="button" className="icon-btn sm" onClick={() => go(-1)} aria-label="Previous month">
          <IC.ChevronLeft />
        </button>
        <div className="cal-title">
          {MONTH_NAMES[month]} {year}
        </div>
        <button type="button" className="icon-btn sm" onClick={() => go(1)} aria-label="Next month">
          <IC.ChevronRight />
        </button>
      </div>
      <div className="cal-grid">
        {dows.map((d) => (
          <div key={d} className="cal-dow">
            {d}
          </div>
        ))}
        {cells.map((date) => {
          const iso = toISODate(date);
          const outside = date.getMonth() !== month;
          const inRange = lo && hi && iso >= lo && iso <= hi;
          const isStart = lo && iso === lo;
          const isEnd = hi && iso === hi;
          return (
            <button
              key={iso}
              type="button"
              className={cx(
                "cal-day",
                outside && "outside",
                iso === today && "today",
                iso === value && "selected",
                inRange && "in-range",
                isStart && "range-start",
                isEnd && "range-end",
                pendingFrom && "picking",
              )}
              onMouseEnter={() => setHover(iso)}
              onClick={() => onPick(iso)}
              title={fmtDate(iso)}
            >
              {date.getDate()}
            </button>
          );
        })}
      </div>
      <div className="cal-foot">
        <button
          type="button"
          className="link-btn"
          onClick={() => setView(new Date(new Date().getFullYear(), new Date().getMonth(), 1))}
        >
          Jump to today
        </button>
      </div>
    </div>
  );
}

/* DateField — single due-date picker: quick picks + calendar + clear.
   Value is a local "YYYY-MM-DD" string or "" / null. */
function DateField({ value, onChange, placeholder = "No due date", disabled, clearable = true, className, variant }) {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  const t = new Date();
  const quick = [
    ["Today", toISODate(t)],
    ["Tomorrow", toISODate(addDays(t, 1))],
    ["In 3 days", toISODate(addDays(t, 3))],
    ["Next week", toISODate(addDays(t, 7))],
    ["End of month", toISODate(endOfMonth(t))],
  ];
  const info = value ? dueInfo(value, false) : null;
  const pick = (iso) => {
    onChange(iso);
    setOpen(false);
  };
  return (
    <>
      <span className={cx("date-field", variant && `date-field-${variant}`, value && "has-value", disabled && "disabled", className)}>
        <button
          ref={ref}
          type="button"
          className="date-field-btn"
          disabled={disabled}
          onClick={() => setOpen((o) => !o)}
          title={info?.title}
        >
          <IC.Calendar />
          <span className={cx(!value && "placeholder", info && `due-text-${info.state}`)}>
            {value ? fmtDate(value) : placeholder}
          </span>
        </button>
        {value && clearable && !disabled && (
          <button type="button" className="date-field-clear" onClick={() => onChange("")} aria-label="Clear date">
            <IC.X />
          </button>
        )}
      </span>
      <Popover open={open} anchorRef={ref} onClose={() => setOpen(false)} width={292}>
        <div className="date-pop">
          <div className="date-quick">
            {quick.map(([l, iso]) => (
              <button key={l} type="button" className={cx("chip-btn", value === iso && "active")} onClick={() => pick(iso)}>
                {l}
              </button>
            ))}
          </div>
          <Calendar value={value || null} onPick={pick} />
          {value && clearable && (
            <div className="date-pop-foot">
              <span className="muted">{fmtDate(value)}</span>
              <button type="button" className="btn btn-ghost btn-sm" onClick={() => pick("")}>
                Clear date
              </button>
            </div>
          )}
        </div>
      </Popover>
    </>
  );
}

/* PeriodFilter ("الفترة") — filter chip for a date range on due date or
   creation date: quick states (overdue / no due date), relative presets
   (stay correct over time), or a custom range picked with two clicks
   (a single click already filters to that day). */
function PeriodFilter({ value, onChange, counts = {}, fields = ["due", "created"] }) {
  const v = value || EMPTY_PERIOD;
  const [open, setOpen] = React.useState(false);
  const [pending, setPending] = React.useState(null);
  const ref = React.useRef(null);
  React.useEffect(() => {
    if (!open) setPending(null);
  }, [open]);
  const label = periodLabel(v);
  const active = v.kind !== "all";
  const set = (patch) => onChange({ ...EMPTY_PERIOD, field: v.field, ...patch });
  const range = periodRange(v);
  const presetIds = v.field === "created" ? CREATED_PRESET_IDS : DUE_PRESET_IDS;

  const pick = (iso) => {
    if (!pending) {
      setPending(iso);
      set({ kind: "custom", from: iso, to: iso });
    } else {
      const [a, b] = iso < pending ? [iso, pending] : [pending, iso];
      set({ kind: "custom", from: a, to: b });
      setPending(null);
    }
  };

  return (
    <>
      <span className={cx("filter-chip", active && "active", open && "open")}>
        <button ref={ref} type="button" className="filter-chip-main" onClick={() => setOpen((o) => !o)}>
          <IC.Calendar />
          <span>{active ? <b>{label}</b> : "Period"}</span>
          <IC.ChevronDown />
        </button>
        {active && (
          <button type="button" className="filter-chip-clear" aria-label="Clear period" onClick={() => onChange({ ...EMPTY_PERIOD, field: v.field })}>
            <IC.X />
          </button>
        )}
      </span>
      <Popover open={open} anchorRef={ref} onClose={() => setOpen(false)} width={316}>
        <div className="period-pop">
          {fields.length > 1 && (
            <Segmented
              size="sm"
              className="period-fields"
              value={v.field}
              onChange={(field) => {
                // A custom range carries over to the other field; quick
                // states and presets are field-specific, so they reset.
                setPending(null);
                onChange({
                  ...EMPTY_PERIOD,
                  field,
                  ...(v.kind === "custom" ? { kind: "custom", from: v.from, to: v.to } : {}),
                });
              }}
              options={fields.map((f) => ({ value: f, label: PERIOD_FIELDS[f] }))}
            />
          )}
          {v.field === "due" && (
            <div className="period-quick">
              <button
                type="button"
                className={cx("chip-btn", "chip-danger", v.kind === "overdue" && "active")}
                onClick={() => set({ kind: "overdue" })}
              >
                <IC.Alert /> Overdue{counts.overdue ? <span className="chip-count">{counts.overdue}</span> : null}
              </button>
              <button type="button" className={cx("chip-btn", v.kind === "none" && "active")} onClick={() => set({ kind: "none" })}>
                No due date{counts.none ? <span className="chip-count">{counts.none}</span> : null}
              </button>
            </div>
          )}
          <div className="period-presets">
            {presetIds.map((id) => (
              <button
                key={id}
                type="button"
                className={cx("preset-btn", v.kind === "preset" && v.preset === id && "active")}
                onClick={() => {
                  setPending(null);
                  set({ kind: "preset", preset: id });
                }}
              >
                {PERIOD_PRESETS[id].label}
              </button>
            ))}
          </div>
          <div className="period-cal-label">
            <span>Custom range</span>
            <span className="muted">
              {pending
                ? "Now pick the end date"
                : range
                  ? range.from === range.to
                    ? fmtDate(range.from)
                    : `${fmtDateShort(range.from)} – ${fmtDateShort(range.to)}`
                  : "Click a start date"}
            </span>
          </div>
          <Calendar rangeFrom={range?.from || null} rangeTo={range?.to || null} pendingFrom={pending} onPick={pick} />
          <div className="period-foot">
            <button
              type="button"
              className="btn btn-ghost btn-sm"
              onClick={() => {
                setPending(null);
                onChange({ ...EMPTY_PERIOD, field: v.field });
              }}
            >
              Clear
            </button>
            <button type="button" className="btn btn-primary btn-sm" onClick={() => setOpen(false)}>
              Done
            </button>
          </div>
        </div>
      </Popover>
    </>
  );
}

/* IntersectionObserver sentinel for infinite lists. */
function LoadMoreSentinel({ onVisible, shown, total, label = "items" }) {
  const ref = React.useRef(null);
  const cb = React.useRef(onVisible);
  cb.current = onVisible;
  React.useEffect(() => {
    if (!ref.current || shown >= total) return;
    const io = new IntersectionObserver(
      (entries) => entries.some((e) => e.isIntersecting) && cb.current(),
      { rootMargin: "320px" },
    );
    io.observe(ref.current);
    return () => io.disconnect();
  }, [shown, total]);
  if (total === 0) return null;
  return (
    <div ref={ref} className="load-more">
      {shown < total ? (
        <button className="btn btn-ghost btn-sm" onClick={onVisible}>
          Load more · {total - shown} remaining
        </button>
      ) : (
        <span className="load-more-count">
          Showing all {total} {label}
        </span>
      )}
    </div>
  );
}
