(() => {
  "use strict";
  const { useCallback, useEffect, useMemo, useRef, useState } = React;
  const TZ = "Asia/Tehran", SCHEMA = 1, MAX_ITEMS = 5000;
  const KINDS = { task: "کار", deadline: "مهلت حقوقی", hearing: "رسیدگی", meeting: "جلسه", reminder: "یادآور" };
  const KIND_RGB = { task: "73,210,220", deadline: "255,127,132", hearing: "217,195,123", meeting: "217,195,123", reminder: "105,229,176" };
  const PRIORITIES = { low: "کم", normal: "عادی", high: "زیاد", critical: "بحرانی" };
  const STATUSES = { todo: "در انتظار", in_progress: "در حال انجام", done: "انجام‌شده", cancelled: "لغوشده" };
  const REPEATS = { none: "بدون تکرار", daily: "روزانه", weekly: "هفتگی", monthly: "ماهانه شمسی", yearly: "سالانه شمسی" };
  const FILTERS = [["all", "همه"], ["inbox", "صندوق ورودی"], ["today", "امروز"], ["reminders", "یادآورها"], ["upcoming", "آینده"], ["overdue", "عقب‌افتاده"], ["completed", "انجام‌شده"], ["archived", "بایگانی"], ["trash", "سطل حذف"]];
  const REMINDERS = [[0, "همان زمان"], [15, "۱۵ دقیقه قبل"], [30, "۳۰ دقیقه قبل"], [60, "۱ ساعت قبل"], [120, "۲ ساعت قبل"], [1440, "۱ روز قبل"], [2880, "۲ روز قبل"], [10080, "۱ هفته قبل"]];
  const MONTHS = ["فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"];
  const WEEKDAYS = ["ش", "ی", "د", "س", "چ", "پ", "ج"];
  const pFmt = new Intl.DateTimeFormat("en-US-u-ca-persian", { timeZone: TZ, year: "numeric", month: "numeric", day: "numeric", weekday: "long" });
  const gFmt = new Intl.DateTimeFormat("en-US-u-ca-gregory", { timeZone: TZ, year: "numeric", month: "numeric", day: "numeric", hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
  const p2gCache = new Map();
  const uid = (prefix = "pc") => window.crypto?.randomUUID?.() || `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
  const clone = (value) => window.structuredClone ? window.structuredClone(value) : JSON.parse(JSON.stringify(value));
  const pad = (value) => String(value).padStart(2, "0");
  const fa = (value) => String(value).replace(/\d/g, (digit) => "۰۱۲۳۴۵۶۷۸۹"[Number(digit)]);
  function planCellStyle(items = []) {
    const colors = [...new Set(items.map((item) => KIND_RGB[item.kind]).filter(Boolean))].slice(0, 3);
    if (!colors.length) return undefined;
    const fill = colors.length === 1
      ? `linear-gradient(145deg, rgba(${colors[0]},.42), rgba(${colors[0]},.14))`
      : `linear-gradient(135deg, ${colors.map((color, index) => `rgba(${color},${index === 0 ? ".42" : ".3"}) ${Math.round((index * 100) / (colors.length - 1))}%`).join(",")})`;
    return { "--pc-plan-rgb": colors[0], "--pc-plan-fill": fill };
  }

  function numberParts(formatter, date) {
    return Object.fromEntries(formatter.formatToParts(date)
      .filter((part) => ["year", "month", "day", "weekday", "hour", "minute"].includes(part.type))
      .map((part) => [part.type, part.value]));
  }
  function persianParts(date = new Date()) {
    const parts = numberParts(pFmt, date);
    return { year: +parts.year, month: +parts.month, day: +parts.day, weekday: parts.weekday };
  }
  function tehranParts(date = new Date()) {
    const parts = numberParts(gFmt, date);
    return { year: +parts.year, month: +parts.month, day: +parts.day, hour: +parts.hour, minute: +parts.minute };
  }
  function todayIso(date = new Date()) {
    const parts = tehranParts(date);
    return `${parts.year}-${pad(parts.month)}-${pad(parts.day)}`;
  }
  function validDate(value) {
    if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return false;
    const [year, month, day] = value.split("-").map(Number);
    const date = new Date(Date.UTC(year, month - 1, day, 12));
    return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day;
  }
  const validTime = (value) => value === null || value === "" || /^([01]\d|2[0-3]):[0-5]\d$/.test(value);
  function gregorianToPersian(isoDate) {
    if (!validDate(isoDate)) throw new Error("تاریخ معتبر نیست.");
    return persianParts(new Date(`${isoDate}T12:00:00Z`));
  }
  function persianToGregorian(year, month, day) {
    const key = `${year}-${month}-${day}`;
    if (p2gCache.has(key)) return p2gCache.get(key);
    if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day) || year < 1200 || year > 1600 || month < 1 || month > 12 || day < 1 || day > 31) return null;
    const cursor = new Date(Date.UTC(year + 621, 1, 15, 12));
    for (let index = 0; index < 430; index += 1) {
      const parts = persianParts(cursor);
      if (parts.year === year && parts.month === month && parts.day === day) {
        const result = cursor.toISOString().slice(0, 10);
        p2gCache.set(key, result);
        return result;
      }
      cursor.setUTCDate(cursor.getUTCDate() + 1);
    }
    p2gCache.set(key, null);
    return null;
  }
  const persianMonthLength = (year, month) => month <= 6 ? 31 : month <= 11 ? 30 : (persianToGregorian(year, 12, 30) ? 30 : 29);
  function shiftPersianMonth(year, month, delta) {
    const zero = year * 12 + month - 1 + delta;
    return { year: Math.floor(zero / 12), month: ((zero % 12) + 12) % 12 + 1 };
  }
  function buildMonth(year, month) {
    const firstIso = persianToGregorian(year, month, 1);
    if (!firstIso) return [];
    const first = new Date(`${firstIso}T12:00:00Z`);
    const offset = (first.getUTCDay() + 1) % 7;
    const count = persianMonthLength(year, month);
    const cells = [];
    for (let index = 0; index < 42; index += 1) {
      const day = index - offset + 1;
      const inMonth = day >= 1 && day <= count;
      cells.push({
        key: `${year}-${month}-${index}`,
        day: inMonth ? day : null,
        isoDate: inMonth ? new Date(first.getTime() + (day - 1) * 86400000).toISOString().slice(0, 10) : null,
      });
    }
    while (cells.length > 35 && cells.slice(-7).every((cell) => !cell.day)) cells.splice(-7);
    return cells;
  }
  function formatPersianDate(isoDate, short = false) {
    if (!validDate(isoDate)) return "بدون تاریخ";
    return new Intl.DateTimeFormat("fa-IR-u-ca-persian", {
      timeZone: TZ, weekday: short ? undefined : "long", day: "numeric", month: "long", year: short ? undefined : "numeric",
    }).format(new Date(`${isoDate}T12:00:00Z`));
  }
  function addDays(isoDate, days) {
    const date = new Date(`${isoDate}T12:00:00Z`);
    date.setUTCDate(date.getUTCDate() + days);
    return date.toISOString().slice(0, 10);
  }
  function nextRecurringDate(isoDate, recurrence) {
    if (!validDate(isoDate) || recurrence === "none") return null;
    if (recurrence === "daily") return addDays(isoDate, 1);
    if (recurrence === "weekly") return addDays(isoDate, 7);
    const current = gregorianToPersian(isoDate);
    if (recurrence === "monthly") {
      const next = shiftPersianMonth(current.year, current.month, 1);
      return persianToGregorian(next.year, next.month, Math.min(current.day, persianMonthLength(next.year, next.month)));
    }
    return persianToGregorian(current.year + 1, current.month, Math.min(current.day, persianMonthLength(current.year + 1, current.month)));
  }
  function tehranEpoch(isoDate, time = "09:00") {
    if (!validDate(isoDate) || !validTime(time)) return NaN;
    const [year, month, day] = isoDate.split("-").map(Number);
    const [hour, minute] = (time || "09:00").split(":").map(Number);
    const target = Date.UTC(year, month - 1, day, hour, minute);
    let guess = target;
    for (let pass = 0; pass < 3; pass += 1) {
      const parts = tehranParts(new Date(guess));
      guess += target - Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute);
    }
    return guess;
  }
  const text = (value, max = 5000) => (typeof value === "string" ? value.trim() : "").slice(0, max);
  const normalizeSearch = (value) => String(value || "").toLocaleLowerCase("fa").replace(/ي/g, "ی").replace(/ك/g, "ک").replace(/\s+/g, " ").trim();
  const fingerprint = (input) => normalizeSearch([input.title, input.kind, input.dueDate || "", input.startTime || "", input.caseReference || ""].join("|"));

  function validateInput(raw) {
    if (!raw || typeof raw !== "object") throw new Error("اطلاعات آیتم معتبر نیست.");
    const title = text(raw.title, 160);
    if (!title) throw new Error("عنوان الزامی است.");
    const kind = KINDS[raw.kind] ? raw.kind : "task";
    const priority = PRIORITIES[raw.priority] ? raw.priority : "normal";
    const status = STATUSES[raw.status] ? raw.status : "todo";
    const recurrence = REPEATS[raw.recurrence] ? raw.recurrence : "none";
    const dueDate = raw.dueDate ? String(raw.dueDate) : null;
    const allDay = raw.allDay === true;
    const startTime = allDay || !raw.startTime ? null : String(raw.startTime);
    const endTime = allDay || !raw.endTime ? null : String(raw.endTime);
    if (dueDate && !validDate(dueDate)) throw new Error("تاریخ معتبر نیست.");
    if (!validTime(startTime) || !validTime(endTime)) throw new Error("ساعت معتبر نیست.");
    if (!dueDate && (startTime || endTime)) throw new Error("برای ساعت، تاریخ را مشخص کنید.");
    if (startTime && endTime && endTime <= startTime) throw new Error("پایان باید بعد از شروع باشد.");
    const tags = [...new Set((Array.isArray(raw.tags) ? raw.tags : []).map((tag) => text(tag, 36)).filter(Boolean))].slice(0, 12);
    const checklist = (Array.isArray(raw.checklist) ? raw.checklist : []).slice(0, 40)
      .map((item) => ({ id: text(item?.id, 80) || uid("check"), label: text(item?.label, 180), done: item?.done === true }))
      .filter((item) => item.label);
    const reminderMinutes = [...new Set(Array.isArray(raw.reminderMinutes) ? raw.reminderMinutes : [])]
      .filter((value) => Number.isInteger(value) && value >= 0 && value <= 525600).sort((a, b) => a - b).slice(0, 8);
    const source = text(raw.source, 240);
    if (!dueDate && reminderMinutes.length) throw new Error("برای یادآور، تاریخ لازم است.");
    if (!dueDate && recurrence !== "none") throw new Error("برای تکرار، تاریخ لازم است.");
    if (kind === "deadline" && !dueDate) throw new Error("تاریخ مهلت حقوقی الزامی است.");
    if (kind === "deadline" && !source) throw new Error("منشأ مهلت حقوقی الزامی است.");
    if (kind === "deadline" && !reminderMinutes.length) throw new Error("مهلت حقوقی حداقل یک یادآور می‌خواهد.");
    return {
      title, description: text(raw.description), kind, priority, status, dueDate, startTime, endTime, allDay,
      caseReference: text(raw.caseReference, 120), source, location: text(raw.location, 240),
      tags, checklist, reminderMinutes, recurrence,
    };
  }
  function makeItem(input, metadata = {}) {
    const value = validateInput(input);
    const now = new Date().toISOString();
    const id = metadata.id || uid("item");
    return {
      id, ...value, seriesId: metadata.seriesId || (value.recurrence !== "none" ? id : null),
      createdAt: metadata.createdAt || now, updatedAt: metadata.updatedAt || now,
      completedAt: metadata.completedAt || (value.status === "done" ? now : null),
      archivedAt: metadata.archivedAt || null, deletedAt: metadata.deletedAt || null,
      snoozedUntil: metadata.snoozedUntil || null,
      version: Number.isInteger(metadata.version) && metadata.version > 0 ? metadata.version : 1,
      fingerprint: fingerprint(value),
    };
  }
  const emptyEnvelope = () => ({
    schemaVersion: SCHEMA, revision: 0, items: [],
    settings: { reminderPermission: "ask", lastReminderScan: null, notified: {} },
  });
  function normalizeEnvelope(raw) {
    const value = Array.isArray(raw) ? { schemaVersion: 1, revision: 0, items: raw, settings: {} } : raw;
    if (!value || typeof value !== "object" || !Array.isArray(value.items) || value.items.length > MAX_ITEMS) {
      throw new Error("ساختار پشتیبان معتبر نیست.");
    }
    const ids = new Set();
    const items = value.items.map((candidate) => {
      const item = makeItem(candidate, candidate);
      if (ids.has(item.id)) throw new Error("شناسه تکراری در پشتیبان وجود دارد.");
      ids.add(item.id);
      return item;
    });
    const settings = value.settings && typeof value.settings === "object" ? value.settings : {};
    const notified = settings.notified && typeof settings.notified === "object" ? settings.notified : {};
    return {
      schemaVersion: SCHEMA,
      revision: Number.isInteger(value.revision) && value.revision >= 0 ? value.revision : 0,
      items,
      settings: {
        reminderPermission: ["ask", "granted", "denied"].includes(settings.reminderPermission) ? settings.reminderPermission : "ask",
        lastReminderScan: typeof settings.lastReminderScan === "string" && !Number.isNaN(Date.parse(settings.lastReminderScan)) ? settings.lastReminderScan : null,
        notified: Object.fromEntries(Object.entries(notified).filter(([, stamp]) => Number.isFinite(stamp)).sort((a, b) => b[1] - a[1]).slice(0, 600)),
      },
    };
  }
  class PlannerRecoveryError extends Error {
    constructor(message, recovery) {
      super(message);
      this.name = "PlannerRecoveryError";
      this.plannerRecovery = recovery;
    }
  }

  function mountRecoveryDialog(recovery) {
    const layer = document.createElement("div");
    layer.dir = "rtl";
    layer.setAttribute("role", "alertdialog");
    layer.setAttribute("aria-modal", "true");
    layer.setAttribute("aria-labelledby", "pc-recovery-title");
    Object.assign(layer.style, {
      position: "fixed", inset: "0", zIndex: "100000", display: "grid", placeItems: "center",
      padding: "20px", background: "rgba(1,10,16,.88)", backdropFilter: "blur(8px)"
    });
    const card = document.createElement("section");
    Object.assign(card.style, {
      width: "min(560px,100%)", padding: "24px", border: "1px solid rgba(255,127,132,.45)",
      borderRadius: "16px", background: "#071b28", color: "#edfaff", boxShadow: "0 24px 80px rgba(0,0,0,.55)"
    });
    const title = document.createElement("h2");
    title.id = "pc-recovery-title";
    title.textContent = "نیاز به بازیابی داده‌های تقویم";
    title.style.margin = "0 0 10px";
    const message = document.createElement("p");
    message.textContent = recovery.message || "فایل ذخیره تقویم آسیب دیده است. برای جلوگیری از حذف ناخواسته، ذخیره‌سازی متوقف شد.";
    Object.assign(message.style, { margin: "0 0 18px", color: "#b8d1dc", lineHeight: "1.9" });
    const actions = document.createElement("div");
    Object.assign(actions.style, { display: "flex", flexWrap: "wrap", gap: "9px" });
    const exportButton = document.createElement("button");
    exportButton.type = "button";
    exportButton.textContent = "دانلود داده خام";
    Object.assign(exportButton.style, { minHeight: "42px", padding: "0 15px", border: "1px solid #49d2dc", borderRadius: "10px", background: "rgba(73,210,220,.1)", color: "#75edf4", cursor: "pointer" });
    exportButton.addEventListener("click", () => {
      download(`shahdad-calendar-recovery-${Date.now()}.json`, JSON.stringify({ exportedAt: new Date().toISOString(), recovery }, null, 2), "application/json;charset=utf-8");
    });
    const resetButton = document.createElement("button");
    resetButton.type = "button";
    resetButton.textContent = "بازنشانی تأییدشده";
    Object.assign(resetButton.style, { minHeight: "42px", padding: "0 15px", border: "1px solid rgba(255,127,132,.55)", borderRadius: "10px", background: "rgba(255,127,132,.1)", color: "#ff9da1", cursor: "pointer" });
    resetButton.addEventListener("click", () => {
      if (!window.confirm("داده‌های فعلی و نسخه‌های قرنطینه‌شده تقویم حذف و فضای ذخیره‌سازی از نو ساخته شود؟")) return;
      try {
        const keys = [];
        for (let index = 0; index < localStorage.length; index += 1) keys.push(localStorage.key(index));
        keys.filter((key) => key && (key === recovery.primaryKey || key === recovery.tempKey || key.startsWith(`${recovery.storagePrefix}.quarantine.`))).forEach((key) => localStorage.removeItem(key));
        location.reload();
      } catch (error) {
        window.alert("بازنشانی انجام نشد. دسترسی ذخیره‌سازی مرورگر را بررسی کنید.");
      }
    });
    actions.append(exportButton, resetButton);
    card.append(title, message, actions);
    layer.append(card);
    document.body.append(layer);
    requestAnimationFrame(() => exportButton.focus());
    return () => layer.remove();
  }
  class LocalPlannerStore {
    constructor(ownerKey) {
      this.key = `shahdad.v31.planner.${ownerKey}.v1`;
      this.tempKey = `${this.key}.tmp`;
      this.memory = null;
    }
    load(seedFactory) {
      let primaryRaw = null;
      let tempRaw = null;
      try {
        primaryRaw = localStorage.getItem(this.key);
        tempRaw = localStorage.getItem(this.tempKey);
      } catch (error) {
        if (this.memory) return normalizeEnvelope(this.memory);
        this.recoveryBlocked = true;
        throw new PlannerRecoveryError("دسترسی به فضای ذخیره‌سازی تقویم ممکن نیست.", {
          message: "مرورگر اجازه خواندن داده‌های تقویم را نداد. هیچ داده‌ای بازنویسی نشده است.",
          storagePrefix: this.key, primaryKey: this.key, tempKey: this.tempKey, records: [],
        });
      }
      const inspect = (raw, source) => {
        if (raw === null) return { source, exists: false, valid: false, raw: null, value: null, error: null };
        try {
          return { source, exists: true, valid: true, raw, value: normalizeEnvelope(JSON.parse(raw)), error: null };
        } catch (error) {
          return { source, exists: true, valid: false, raw, value: null, error: error instanceof Error ? error.message : "داده نامعتبر" };
        }
      };
      const primary = inspect(primaryRaw, "primary");
      const temporary = inspect(tempRaw, "temp");
      const quarantine = (entry) => {
        if (!entry.exists || entry.valid) return null;
        const key = `${this.key}.quarantine.${Date.now()}.${entry.source}`;
        try { localStorage.setItem(key, entry.raw); } catch (error) { /* raw remains attached for export */ }
        return { source: entry.source, key, raw: entry.raw, error: entry.error };
      };
      if (primary.valid) {
        if (temporary.exists && !temporary.valid) quarantine(temporary);
        try { localStorage.removeItem(this.tempKey); } catch (error) { /* primary remains authoritative */ }
        this.memory = clone(primary.value);
        return primary.value;
      }
      if (temporary.valid) {
        const records = [];
        const quarantinedPrimary = quarantine(primary);
        if (quarantinedPrimary) records.push(quarantinedPrimary);
        try {
          localStorage.setItem(this.key, JSON.stringify(temporary.value));
          localStorage.removeItem(this.tempKey);
          this.memory = clone(temporary.value);
          return temporary.value;
        } catch (error) {
          this.recoveryBlocked = true;
          records.push({ source: "temp", key: this.tempKey, raw: temporary.raw, error: "promote_failed" });
          throw new PlannerRecoveryError("بازیابی خودکار تقویم کامل نشد.", {
            message: "نسخه موقت سالم پیدا شد اما انتقال آن به ذخیره اصلی ممکن نبود. هیچ داده‌ای حذف نشده است.",
            storagePrefix: this.key, primaryKey: this.key, tempKey: this.tempKey, records,
          });
        }
      }
      if (!primary.exists && !temporary.exists) {
        return this.save(seedFactory ? seedFactory() : emptyEnvelope());
      }
      const records = [quarantine(primary), quarantine(temporary)].filter(Boolean);
      this.recoveryBlocked = true;
      throw new PlannerRecoveryError("داده‌های تقویم نیاز به بازیابی دارند.", {
        message: "نسخه اصلی یا موقت تقویم آسیب دیده است. برای جلوگیری از حذف ناخواسته، ذخیره‌سازی متوقف و داده خام قرنطینه شد.",
        storagePrefix: this.key, primaryKey: this.key, tempKey: this.tempKey, records,
      });
    }
    save(envelope) {
      if (this.recoveryBlocked) throw new Error("تا تعیین تکلیف داده‌های آسیب‌دیده، ذخیره‌سازی متوقف است.");
      const safe = normalizeEnvelope(envelope);
      const payload = JSON.stringify(safe);
      this.memory = clone(safe);
      try {
        localStorage.setItem(this.tempKey, payload);
        localStorage.setItem(this.key, payload);
        localStorage.removeItem(this.tempKey);
      } catch (error) {
        throw new Error("ذخیره محلی انجام نشد.");
      }
      return safe;
    }
  }
  function ownerKey(session) {
    const raw = session?.user?.email || session?.email || session?.user?.id || session?.userId || session?.displayName || (session?.localPreview ? "preview" : "guest");
    let hash = 2166136261;
    for (const character of String(raw).toLowerCase()) {
      hash ^= character.charCodeAt(0);
      hash = Math.imul(hash, 16777619);
    }
    return `u-${(hash >>> 0).toString(36)}`;
  }
  const isPreview = (session) => ["127.0.0.1", "localhost", "::1"].includes(location.hostname)
    && new URLSearchParams(location.search).get("previewPlan") === "earth" && session?.localPreview === true;
  function seedEnvelope() {
    const today = todayIso();
    const envelope = emptyEnvelope();
    envelope.items = [
      makeItem({
        title: "بررسی مهلت اعتراض پرونده", description: "کنترل ابلاغ و آماده‌سازی پیش‌نویس اعتراض",
        kind: "deadline", priority: "critical", status: "todo", dueDate: addDays(today, 2),
        startTime: "10:00", endTime: "10:30", allDay: false, caseReference: "پرونده قرارداد مشارکت",
        source: "ابلاغیه شعبه حقوقی", location: "", tags: ["اعتراض", "مهلت"],
        checklist: [{ id: uid("check"), label: "کنترل تاریخ ابلاغ", done: true }, { id: uid("check"), label: "آماده‌سازی پیش‌نویس", done: false }],
        reminderMinutes: [30, 120, 1440], recurrence: "none",
      }),
      makeItem({
        title: "جلسه بررسی اسناد قرارداد", description: "نسخه نهایی قرارداد و پیوست‌ها مرور شود.",
        kind: "meeting", priority: "high", status: "in_progress", dueDate: today,
        startTime: "16:00", endTime: "17:00", allDay: false, caseReference: "قرارداد مشارکت",
        source: "", location: "جلسه آنلاین", tags: ["قرارداد"], checklist: [],
        reminderMinutes: [15, 60], recurrence: "weekly",
      }),
      makeItem({
        title: "پیگیری نظریه کارشناس", description: "", kind: "task", priority: "normal",
        status: "todo", dueDate: addDays(today, 5), startTime: null, endTime: null, allDay: true,
        caseReference: "مطالبه وجه", source: "", location: "", tags: ["کارشناس"],
        checklist: [], reminderMinutes: [1440], recurrence: "none",
      }),
    ];
    return envelope;
  }
  function Icon({ name, size = 18 }) {
    const paths = {
      plus: <path d="M12 5v14M5 12h14" />,
      search: <><circle cx="11" cy="11" r="7" /><path d="m20 20-4-4" /></>,
      calendar: <><rect x="3" y="5" width="18" height="16" rx="3" /><path d="M8 3v4M16 3v4M3 10h18" /></>,
      clock: <><circle cx="12" cy="12" r="9" /><path d="M12 7v5l3 2" /></>,
      bell: <><path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4" /></>,
      tools: <><circle cx="12" cy="12" r="3" /><path d="M12 2v3M12 19v3M2 12h3M19 12h3M4.9 4.9 7 7M17 17l2.1 2.1M19.1 4.9 17 7M7 17l-2.1 2.1" /></>,
      edit: <><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L8 18l-4 1 1-4Z" /></>,
      check: <path d="m5 12 4 4L19 6" />,
      archive: <><path d="M4 7h16v13H4zM3 3h18v4H3zM9 11h6" /></>,
      trash: <><path d="M4 7h16M9 7V4h6v3M8 10v7M12 10v7M16 10v7M6 7l1 14h10l1-14" /></>,
      restore: <><path d="M3 12a9 9 0 1 0 3-6.7L3 8" /><path d="M3 3v5h5" /></>,
      download: <><path d="M12 3v12m0 0 4-4m-4 4-4-4M4 20h16" /></>,
      upload: <><path d="M12 16V4m0 0 4 4m-4-4-4 4M4 20h16" /></>,
      close: <path d="M6 6l12 12M18 6 6 18" />,
      right: <path d="m9 18 6-6-6-6" />, left: <path d="m15 18-6-6 6-6" />,
      folder: <path d="M3 6h7l2 2h9v11H3z" />,
    };
    return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">{paths[name] || <circle cx="12" cy="12" r="8" />}</svg>;
  }
  const blankDraft = (date) => ({
    title: "", description: "", kind: "task", priority: "normal", status: "todo",
    dueDate: date || "", startTime: "", endTime: "", allDay: true,
    caseReference: "", source: "", location: "", tagsText: "", checklist: [],
    reminderMinutes: [], recurrence: "none",
  });
  const itemToDraft = (item) => ({
    ...item, dueDate: item.dueDate || "", startTime: item.startTime || "", endTime: item.endTime || "",
    tagsText: item.tags.join("، "), checklist: clone(item.checklist), reminderMinutes: [...item.reminderMinutes],
  });
  function download(name, content, type) {
    const blob = new Blob([content], { type });
    const url = URL.createObjectURL(blob);
    const anchor = document.createElement("a");
    anchor.href = url; anchor.download = name; document.body.appendChild(anchor); anchor.click(); anchor.remove();
    URL.revokeObjectURL(url);
  }
  const escapeIcs = (value) => String(value || "").replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/,/g, "\\,").replace(/;/g, "\\;");

  function AdvancedCalendarView(props) {
    const session = props?.session || props?.sessionState || {};
    const accountKey = useMemo(() => ownerKey(session), [session?.user?.email, session?.email, session?.user?.id, session?.userId, session?.displayName, session?.localPreview]);
    const store = useMemo(() => new LocalPlannerStore(accountKey), [accountKey]);
    const preview = isPreview(session);
    const [initialLoad] = useState(() => {
      try {
        return { planner: store.load(preview ? seedEnvelope : null), recovery: null };
      } catch (error) {
        return {
          planner: emptyEnvelope(),
          recovery: error?.plannerRecovery || {
            message: error instanceof Error ? error.message : "بازیابی تقویم لازم است.",
            storagePrefix: store.key, primaryKey: store.key, tempKey: store.tempKey, records: [],
          },
        };
      }
    });
    const [planner, setPlanner] = useState(initialLoad.planner);
    const recovery = initialLoad.recovery;
    const plannerRef = useRef(planner);
    useEffect(() => recovery ? mountRecoveryDialog(recovery) : undefined, [recovery]);
    const today = todayIso();
    const initialPersian = gregorianToPersian(today);
    const [month, setMonth] = useState({ year: initialPersian.year, month: initialPersian.month });
    const [selectedDate, setSelectedDate] = useState(today);
    const [activeFilter, setActiveFilter] = useState("today");
    const [query, setQuery] = useState("");
    const [clock, setClock] = useState(new Date());
    const [editor, setEditor] = useState(null);
    const [draft, setDraft] = useState(blankDraft(today));
    const [formError, setFormError] = useState("");
    const [toolsOpen, setToolsOpen] = useState(false);
    const [restoreMode, setRestoreMode] = useState("merge");
    const [toast, setToast] = useState(null);
    const [undoState, setUndoState] = useState(null);
    const [alerts, setAlerts] = useState([]);
    const fileInputRef = useRef(null);

    useEffect(() => {
      const loaded = store.load(preview ? seedEnvelope : null);
      plannerRef.current = loaded;
      setPlanner(loaded);
    }, [store, preview]);
    useEffect(() => { plannerRef.current = planner; }, [planner]);
    useEffect(() => {
      const timer = setInterval(() => setClock(new Date()), 1000);
      return () => clearInterval(timer);
    }, []);
    useEffect(() => {
      if (!toast) return undefined;
      const timer = setTimeout(() => setToast(null), 5000);
      return () => clearTimeout(timer);
    }, [toast]);
    useEffect(() => {
      if (!undoState) return undefined;
      const timer = setTimeout(() => setUndoState(null), 10000);
      return () => clearTimeout(timer);
    }, [undoState]);

    const commit = useCallback((message, mutate, options = {}) => {
      const before = plannerRef.current;
      try {
        const next = clone(before);
        mutate(next);
        next.schemaVersion = SCHEMA;
        next.revision = before.revision + 1;
        const saved = store.save(next);
        plannerRef.current = saved;
        setPlanner(saved);
        if (options.undo !== false) setUndoState({ snapshot: clone(before), message });
        if (message) setToast({ tone: "ok", text: message });
        return saved;
      } catch (error) {
        setToast({ tone: "error", text: error.message || "عملیات انجام نشد." });
        return null;
      }
    }, [store]);

    function undo() {
      if (!undoState) return;
      try {
        const restored = clone(undoState.snapshot);
        restored.revision = plannerRef.current.revision + 1;
        const saved = store.save(restored);
        plannerRef.current = saved;
        setPlanner(saved);
        setUndoState(null);
        setToast({ tone: "ok", text: "آخرین تغییر بازگردانده شد." });
      } catch (error) {
        setToast({ tone: "error", text: error.message });
      }
    }

    const scanReminders = useCallback(() => {
      const current = plannerRef.current;
      const now = Date.now();
      const lastScan = current.settings.lastReminderScan ? Math.max(Date.parse(current.settings.lastReminderScan), now - 7 * 86400000) : now - 86400000;
      const notified = { ...current.settings.notified };
      const found = [];
      current.items.forEach((item) => {
        if (item.deletedAt || item.archivedAt || ["done", "cancelled"].includes(item.status) || !item.dueDate) return;
        if (item.snoozedUntil) {
          const snoozeAt = Date.parse(item.snoozedUntil);
          const signature = `snooze:${item.id}:${item.snoozedUntil}`;
          if (snoozeAt <= now && snoozeAt > lastScan && !notified[signature]) {
            notified[signature] = now;
            found.push({ item, signature });
          }
          if (snoozeAt > now) return;
        }
        const eventAt = tehranEpoch(item.dueDate, item.startTime || "09:00");
        item.reminderMinutes.forEach((minutes) => {
          const reminderAt = eventAt - minutes * 60000;
          const signature = `${item.id}:${item.dueDate}:${item.startTime || "09:00"}:${minutes}`;
          if (reminderAt <= now && reminderAt > lastScan && !notified[signature]) {
            notified[signature] = now;
            found.push({ item, signature });
          }
        });
      });
      const next = clone(current);
      next.settings.lastReminderScan = new Date(now).toISOString();
      next.settings.notified = Object.fromEntries(Object.entries(notified).sort((a, b) => b[1] - a[1]).slice(0, 600));
      next.revision = current.revision + 1;
      try {
        const saved = store.save(next);
        plannerRef.current = saved;
        setPlanner(saved);
      } catch (error) {
        return;
      }
      if (found.length) {
        setAlerts((old) => {
          const known = new Set(old.map((alert) => alert.signature));
          return [...old, ...found.filter((alert) => !known.has(alert.signature))].slice(-4);
        });
        if (window.Notification?.permission === "granted") {
          found.forEach(({ item }) => {
            try {
              new Notification("یادآور حقوقی شهداد", {
                body: `${item.title}${item.caseReference ? ` · ${item.caseReference}` : ""}`,
                tag: `planner-${item.id}`,
              });
            } catch (error) {}
          });
        }
      }
    }, [store]);
    useEffect(() => {
      const first = setTimeout(scanReminders, 1200);
      const interval = setInterval(scanReminders, 60000);
      const focus = () => scanReminders();
      const visibility = () => { if (document.visibilityState === "visible") scanReminders(); };
      addEventListener("focus", focus);
      document.addEventListener("visibilitychange", visibility);
      return () => {
        clearTimeout(first); clearInterval(interval);
        removeEventListener("focus", focus);
        document.removeEventListener("visibilitychange", visibility);
      };
    }, [scanReminders]);

    const now = tehranParts(clock);
    const activeItems = useMemo(() => planner.items.filter((item) => !item.deletedAt && !item.archivedAt), [planner.items]);
    function isOverdue(item) {
      if (!item.dueDate || ["done", "cancelled"].includes(item.status)) return false;
      if (item.dueDate < today) return true;
      if (item.dueDate > today || item.allDay || !item.startTime) return false;
      return item.startTime < `${pad(now.hour)}:${pad(now.minute)}`;
    }
    function matchesFilter(item, filter) {
    if (arguments[1] === "reminders") {
      const candidate = arguments[0];
      return candidate?.kind === "reminder" || (Array.isArray(candidate?.reminderMinutes) && candidate.reminderMinutes.length > 0);
    }
      if (filter === "trash") return Boolean(item.deletedAt);
      if (filter === "archived") return !item.deletedAt && Boolean(item.archivedAt);
      if (item.deletedAt || item.archivedAt) return false;
      if (filter === "all") return true;
      if (filter === "day") return item.dueDate === selectedDate;
      if (filter === "inbox") return !item.dueDate && item.status !== "done";
      if (filter === "today") return item.dueDate === today && item.status !== "done";
      if (filter === "upcoming") return item.dueDate > today && item.status !== "done";
      if (filter === "overdue") return isOverdue(item);
      if (filter === "completed") return item.status === "done";
      return true;
    }
    const filterCounts = useMemo(() => Object.fromEntries(FILTERS.map(([key]) => [key, planner.items.filter((item) => matchesFilter(item, key)).length])), [planner.items, today, clock, selectedDate]);
    const displayedItems = useMemo(() => {
      const needle = normalizeSearch(query);
      return planner.items.filter((item) => matchesFilter(item, activeFilter))
        .filter((item) => !needle || normalizeSearch([item.title, item.description, item.caseReference, item.source, item.location, item.tags.join(" ")].join(" ")).includes(needle))
        .sort((a, b) => !a.dueDate ? 1 : !b.dueDate ? -1 : `${a.dueDate}${a.startTime || ""}`.localeCompare(`${b.dueDate}${b.startTime || ""}`));
    }, [planner.items, activeFilter, query, selectedDate, today, clock]);
    const cells = useMemo(() => buildMonth(month.year, month.month), [month]);
    const itemsByDate = useMemo(() => {
      const result = {};
      activeItems.forEach((item) => {
        if (item.dueDate) (result[item.dueDate] || (result[item.dueDate] = [])).push(item);
      });
      return result;
    }, [activeItems]);

    function openNew(date = selectedDate) {
      setDraft(blankDraft(date));
      setFormError("");
      setEditor({ id: null, baseVersion: null });
    }
    function openEdit(item) {
      setDraft(itemToDraft(item));
      setFormError("");
      setEditor({ id: item.id, baseVersion: item.version });
    }
    function ensureNextOccurrence(items, item, wasDone) {
      if (wasDone || item.status !== "done" || item.recurrence === "none" || !item.dueDate) return;
      const dueDate = nextRecurringDate(item.dueDate, item.recurrence);
      if (!dueDate) return;
      const seriesId = item.seriesId || item.id;
      item.seriesId = seriesId;
      if (items.some((candidate) => candidate.seriesId === seriesId && candidate.dueDate === dueDate)) return;
      items.push(makeItem({ ...item, status: "todo", dueDate, checklist: item.checklist.map((entry) => ({ ...entry, done: false })) }, { seriesId }));
    }
    function saveForm(event) {
      event.preventDefault();
      setFormError("");
      try {
        const input = validateInput({
          ...draft,
          dueDate: draft.dueDate || null,
          startTime: draft.startTime || null,
          endTime: draft.endTime || null,
          tags: draft.tagsText.split(/[،,]/).map((tag) => tag.trim()).filter(Boolean),
        });
        const current = plannerRef.current;
        const nextFingerprint = fingerprint(input);
        if (editor.id) {
          const existing = current.items.find((item) => item.id === editor.id);
          if (!existing) throw new Error("آیتم پیدا نشد.");
          if (existing.version !== editor.baseVersion) throw new Error("آیتم هم‌زمان تغییر کرده؛ دوباره بازش کنید.");
          if (current.items.some((item) => item.id !== existing.id && !item.deletedAt && item.fingerprint === nextFingerprint)) throw new Error("آیتم مشابه وجود دارد.");
          if (!commit("تغییرات ذخیره شد.", (state) => {
            const index = state.items.findIndex((item) => item.id === existing.id);
            const wasDone = state.items[index].status === "done";
            const updated = {
              ...state.items[index], ...input, updatedAt: new Date().toISOString(),
              completedAt: input.status === "done" ? (state.items[index].completedAt || new Date().toISOString()) : null,
              version: state.items[index].version + 1, fingerprint: nextFingerprint,
            };
            state.items[index] = updated;
            ensureNextOccurrence(state.items, updated, wasDone);
          })) return;
        } else {
          if (current.items.some((item) => !item.deletedAt && item.fingerprint === nextFingerprint)) throw new Error("آیتم مشابه وجود دارد.");
          if (!commit("آیتم ایجاد شد.", (state) => state.items.push(makeItem(input)))) return;
        }
        setEditor(null);
      } catch (error) {
        setFormError(error.message || "فرم معتبر نیست.");
      }
    }
    function completeItem(id) {
      commit("آیتم انجام شد.", (state) => {
        const index = state.items.findIndex((item) => item.id === id);
        if (index < 0) throw new Error("آیتم پیدا نشد.");
        const wasDone = state.items[index].status === "done";
        state.items[index] = {
          ...state.items[index], status: "done",
          completedAt: state.items[index].completedAt || new Date().toISOString(),
          updatedAt: new Date().toISOString(), version: state.items[index].version + 1,
        };
        ensureNextOccurrence(state.items, state.items[index], wasDone);
      });
    }
    function mutateItem(id, message, mutation, options) {
      commit(message, (state) => {
        const item = state.items.find((candidate) => candidate.id === id);
        if (!item) throw new Error("آیتم پیدا نشد.");
        mutation(item);
        item.updatedAt = new Date().toISOString();
        item.version += 1;
      }, options);
    }
    const archiveItem = (id) => mutateItem(id, "آیتم بایگانی شد.", (item) => { item.archivedAt = new Date().toISOString(); });
    const softDeleteItem = (id) => mutateItem(id, "آیتم به سطل حذف رفت.", (item) => { item.deletedAt = new Date().toISOString(); item.archivedAt = null; });
    const restoreItem = (id) => mutateItem(id, "آیتم بازیابی شد.", (item) => { item.deletedAt = null; item.archivedAt = null; });
    function permanentDelete(id) {
      if (confirm("این آیتم برای همیشه حذف شود؟")) commit("آیتم برای همیشه حذف شد.", (state) => { state.items = state.items.filter((item) => item.id !== id); }, { undo: false });
    }
    function toggleChecklist(itemId, checklistId) {
      mutateItem(itemId, "چک‌لیست به‌روزرسانی شد.", (item) => {
        const entry = item.checklist.find((candidate) => candidate.id === checklistId);
        if (!entry) throw new Error("گزینه پیدا نشد.");
        entry.done = !entry.done;
      });
    }
    function snoozeItem(id) {
      mutateItem(id, "یادآور یک ساعت عقب افتاد.", (item) => { item.snoozedUntil = new Date(Date.now() + 3600000).toISOString(); });
      setAlerts((current) => current.filter((alert) => alert.item.id !== id));
    }
    function exportJson() {
      download(`shahdad-planner-${today}.json`, JSON.stringify({ ...plannerRef.current, exportedAt: new Date().toISOString() }, null, 2), "application/json;charset=utf-8");
      setToolsOpen(false);
    }
    function exportIcs() {
      const lines = ["BEGIN:VCALENDAR", "VERSION:2.0", "PRODID:-//Shahdad AI//Legal Planner//FA", "CALSCALE:GREGORIAN"];
      plannerRef.current.items.filter((item) => item.dueDate && !item.deletedAt).forEach((item) => {
        const date = item.dueDate.replace(/-/g, "");
        lines.push("BEGIN:VEVENT", `UID:${item.id}@planner.shahdad.ai`, `DTSTAMP:${new Date().toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}`);
        if (item.allDay || !item.startTime) {
          lines.push(`DTSTART;VALUE=DATE:${date}`, `DTEND;VALUE=DATE:${addDays(item.dueDate, 1).replace(/-/g, "")}`);
        } else {
          lines.push(`DTSTART;TZID=Asia/Tehran:${date}T${item.startTime.replace(":", "")}00`);
          if (item.endTime) lines.push(`DTEND;TZID=Asia/Tehran:${date}T${item.endTime.replace(":", "")}00`);
        }
        lines.push(`SUMMARY:${escapeIcs(item.title)}`, `DESCRIPTION:${escapeIcs([item.description, item.caseReference && `پرونده: ${item.caseReference}`, item.source && `منشأ: ${item.source}`].filter(Boolean).join("\n"))}`);
        if (item.location) lines.push(`LOCATION:${escapeIcs(item.location)}`);
        item.reminderMinutes.forEach((minutes) => lines.push("BEGIN:VALARM", `TRIGGER:-PT${minutes}M`, "ACTION:DISPLAY", `DESCRIPTION:${escapeIcs(item.title)}`, "END:VALARM"));
        lines.push("END:VEVENT");
      });
      lines.push("END:VCALENDAR");
      download(`shahdad-planner-${today}.ics`, lines.join("\r\n"), "text/calendar;charset=utf-8");
      setToolsOpen(false);
    }
    async function importJson(event) {
      const file = event.target.files?.[0];
      event.target.value = "";
      if (!file) return;
      try {
        const incoming = normalizeEnvelope(JSON.parse(await file.text()));
        if (restoreMode === "replace" && !confirm("داده‌های فعلی جایگزین شوند؟")) return;
        commit(restoreMode === "replace" ? "داده‌ها جایگزین شدند." : "داده‌ها ادغام شدند.", (state) => {
          if (restoreMode === "replace") {
            state.items = clone(incoming.items);
            state.settings = clone(incoming.settings);
            return;
          }
          const ids = new Map(state.items.map((item) => [item.id, item]));
          incoming.items.forEach((item) => {
            const existing = ids.get(item.id);
            if (existing) {
              if (item.version > existing.version || item.updatedAt > existing.updatedAt) state.items[state.items.findIndex((candidate) => candidate.id === item.id)] = clone(item);
              return;
            }
            const duplicate = state.items.some((candidate) => (item.seriesId && candidate.seriesId === item.seriesId && candidate.dueDate === item.dueDate) || (!candidate.deletedAt && candidate.fingerprint === item.fingerprint));
            if (!duplicate) state.items.push(clone(item));
          });
        });
        setToolsOpen(false);
      } catch (error) {
        setToast({ tone: "error", text: error.message || "فایل معتبر نیست." });
      }
    }
    async function enableNotifications() {
      if (!window.Notification) {
        setToast({ tone: "error", text: "اعلان مرورگر پشتیبانی نمی‌شود." });
        return;
      }
      try {
        const permission = await Notification.requestPermission();
        commit(permission === "granted" ? "اعلان‌ها فعال شد." : "مجوز اعلان صادر نشد.", (state) => { state.settings.reminderPermission = permission; }, { undo: false });
      } catch (error) {
        setToast({ tone: "error", text: "درخواست مجوز انجام نشد." });
      }
    }

    const nowPersian = persianParts(clock);
    const agendaTitle = activeFilter === "day" ? formatPersianDate(selectedDate) : (FILTERS.find(([key]) => key === activeFilter)?.[1] || "برنامه‌ها");
    return <div className="pc-shell" dir="rtl">
      <header className="pc-header">
        <div className="pc-heading"><span><Icon name="calendar" size={22} /></span><div><small>برنامه‌ریز حقوقی</small><h2>تقویم و ساعت</h2></div></div>
        <div className="pc-live"><strong>{fa(`${pad(now.hour)}:${pad(now.minute)}`)}</strong><span>{`${nowPersian.weekday}، ${fa(nowPersian.day)} ${MONTHS[nowPersian.month - 1]} ${fa(nowPersian.year)}`}</span><i /></div>
        <div className="pc-header-actions">
          <button className="pc-primary" onClick={() => openNew()}><Icon name="plus" /><span>آیتم جدید</span></button>
          <button className="pc-icon" aria-label="فعال‌سازی اعلان‌ها" title="اعلان‌ها" onClick={enableNotifications}><Icon name="bell" /></button>
          <button className="pc-icon" aria-label="ابزارهای تقویم" title="ابزارها" aria-expanded={toolsOpen} onClick={() => setToolsOpen(!toolsOpen)}><Icon name="tools" /></button>
        </div>
      </header>
      {toolsOpen && <section className="pc-tools">
        <header><strong>ابزارهای تقویم</strong><button className="pc-icon small" aria-label="بستن" onClick={() => setToolsOpen(false)}><Icon name="close" /></button></header>
        <button onClick={exportJson}><Icon name="download" />پشتیبان JSON</button>
        <button onClick={exportIcs}><Icon name="download" />خروجی ICS</button>
        <label>شیوه بازیابی<select value={restoreMode} onChange={(event) => setRestoreMode(event.target.value)}><option value="merge">ادغام با داده‌های فعلی</option><option value="replace">جایگزینی کامل</option></select></label>
        <button onClick={() => fileInputRef.current?.click()}><Icon name="upload" />بازیابی JSON</button>
        <input ref={fileInputRef} className="pc-sr" type="file" accept=".json,application/json" onChange={importJson} />
        <small>نسخه داده {fa(SCHEMA)} · بازبینی {fa(planner.revision)}</small>
      </section>}
      {alerts.length > 0 && <section className="pc-alert" aria-live="polite">
        <Icon name="bell" /><div><strong>{alerts[0].item.title}</strong><span>زمان یادآوری رسیده است.</span></div>
        <button onClick={() => snoozeItem(alerts[0].item.id)}>یک ساعت بعد</button>
        <button className="pc-icon small" aria-label="بستن یادآور" onClick={() => setAlerts((current) => current.slice(1))}><Icon name="close" /></button>
      </section>}
      <div className="pc-workspace">
        <aside className="pc-sidebar">
          <label className="pc-search"><Icon name="search" /><input type="search" value={query} onChange={(event) => setQuery(event.target.value)} placeholder="جست‌وجو در برنامه‌ها" aria-label="جست‌وجو" /></label>
          <nav>{FILTERS.map(([key, label]) => <button key={key} data-active={activeFilter === key} onClick={() => setActiveFilter(key)}><span>{label}</span><b>{fa(filterCounts[key] || 0)}</b></button>)}</nav>
          <div className="pc-storage"><Icon name="folder" /><strong>ذخیره حساب‌محور</strong><p>داده‌ها در این مرورگر اعتبارسنجی و نسخه‌بندی می‌شوند.</p><span><i />ذخیره محلی فعال</span></div>
        </aside>
        <main className="pc-calendar">
          <header><div><small>تقویم شمسی</small><h3>{MONTHS[month.month - 1]} {fa(month.year)}</h3></div><div>
            <button className="pc-icon" aria-label="ماه بعد" onClick={() => setMonth(shiftPersianMonth(month.year, month.month, 1))}><Icon name="right" /></button>
            <button className="pc-today" onClick={() => { setMonth({ year: initialPersian.year, month: initialPersian.month }); setSelectedDate(today); setActiveFilter("today"); }}>امروز</button>
            <button className="pc-icon" aria-label="ماه قبل" onClick={() => setMonth(shiftPersianMonth(month.year, month.month, -1))}><Icon name="left" /></button>
          </div></header>
          <div className="pc-weekdays">{WEEKDAYS.map((day) => <span key={day}>{day}</span>)}</div>
          <div className="pc-grid">{cells.map((cell) => {
            if (!cell.day) return <span key={cell.key} className="pc-day empty" />;
            const dayPlans = itemsByDate[cell.isoDate] || [];
            const planKinds = new Set(dayPlans.map((item) => item.kind));
            return <button key={cell.key} className="pc-day" data-today={cell.isoDate === today} data-selected={cell.isoDate === selectedDate}
                data-has-plans={dayPlans.length > 0} data-plan-kind={dayPlans[0]?.kind || undefined} data-mixed={planKinds.size > 1}
                style={planCellStyle(dayPlans)}
                onClick={() => { setSelectedDate(cell.isoDate); setActiveFilter("day"); }} onDoubleClick={() => openNew(cell.isoDate)}
                aria-label={`${formatPersianDate(cell.isoDate)}، ${dayPlans.length} برنامه`}>
                <strong>{fa(cell.day)}</strong><span>{dayPlans.slice(0, 2).map((item) => <i key={item.id} data-kind={item.kind} title={item.title}>{item.title}</i>)}{dayPlans.length > 2 && <small>+{fa(dayPlans.length - 2)}</small>}</span>
              </button>;
          })}
          </div>
          <footer>{Object.entries(KINDS).map(([key, label]) => <span key={key}><i data-kind={key} />{label}</span>)}</footer>
        </main>
        <aside className="pc-agenda">
          <header><div><small>برنامه‌ها</small><h3>{agendaTitle}</h3></div><b>{fa(displayedItems.length)}</b></header>
          <div className="pc-list">{displayedItems.length === 0
            ? <div className="pc-empty"><Icon name="calendar" size={28} /><strong>برنامه‌ای در این بخش نیست</strong><p>یک آیتم بسازید یا فیلتر دیگری انتخاب کنید.</p><button onClick={() => openNew()}>ساخت آیتم</button></div>
            : displayedItems.map((item) => <article className="pc-card" data-priority={item.priority} key={item.id}>
                <header><span data-kind={item.kind}>{KINDS[item.kind]}</span><div>{item.priority !== "normal" && <b>{PRIORITIES[item.priority]}</b>}<button className="pc-icon small" aria-label={`ویرایش ${item.title}`} onClick={() => openEdit(item)}><Icon name="edit" /></button></div></header>
                <h4>{item.title}</h4>
                <div className="pc-meta">{item.dueDate && <span><Icon name="calendar" size={14} />{formatPersianDate(item.dueDate, true)}</span>}{item.startTime && <span><Icon name="clock" size={14} />{fa(item.startTime)}</span>}{item.caseReference && <span><Icon name="folder" size={14} />{item.caseReference}</span>}</div>
                {item.description && <p>{item.description}</p>}
                {item.tags.length > 0 && <div className="pc-tags">{item.tags.map((tag) => <span key={tag}>#{tag}</span>)}</div>}
                {item.checklist.length > 0 && <div className="pc-inline-checks">{item.checklist.map((entry) => <label key={entry.id}><input type="checkbox" checked={entry.done} onChange={() => toggleChecklist(item.id, entry.id)} /><span>{entry.label}</span></label>)}</div>}
                <footer>{item.deletedAt
                  ? <><button onClick={() => restoreItem(item.id)}><Icon name="restore" />بازیابی</button><button className="danger" onClick={() => permanentDelete(item.id)}><Icon name="trash" />حذف دائمی</button></>
                  : item.archivedAt
                    ? <><button onClick={() => restoreItem(item.id)}><Icon name="restore" />بازگردانی</button><button className="danger" onClick={() => softDeleteItem(item.id)}><Icon name="trash" />حذف</button></>
                    : <>{item.status !== "done" && <button className="complete" onClick={() => completeItem(item.id)}><Icon name="check" />انجام شد</button>}{item.reminderMinutes.length > 0 && <button onClick={() => snoozeItem(item.id)}><Icon name="bell" />یک ساعت بعد</button>}<button className="pc-icon small" aria-label="بایگانی" onClick={() => archiveItem(item.id)}><Icon name="archive" /></button><button className="pc-icon small danger" aria-label="حذف" onClick={() => softDeleteItem(item.id)}><Icon name="trash" /></button></>}
                </footer>
              </article>)}
          </div>
        </aside>
      </div>
      {editor && <div className="pc-editor-layer" onMouseDown={(event) => { if (event.target === event.currentTarget) setEditor(null); }}>
        <form className="pc-editor" onSubmit={saveForm} role="dialog" aria-modal="true">
          <header><div><small>{editor.id ? "ویرایش برنامه" : "برنامه جدید"}</small><h3>{editor.id ? (draft.title || "ویرایش آیتم") : "ایجاد آیتم حقوقی"}</h3></div><button type="button" className="pc-icon" aria-label="بستن" onClick={() => setEditor(null)}><Icon name="close" /></button></header>
          <div className="pc-editor-body">
            {formError && <div className="pc-form-error" role="alert">{formError}</div>}
            <label className="pc-field full">عنوان<input autoFocus required maxLength="160" value={draft.title} onChange={(event) => setDraft({ ...draft, title: event.target.value })} placeholder="برای مثال: تنظیم لایحه اعتراض" /></label>
            <div className="pc-form-grid">
              <label className="pc-field">نوع<select value={draft.kind} onChange={(event) => setDraft({ ...draft, kind: event.target.value })}>{Object.entries(KINDS).map(([key, label]) => <option key={key} value={key}>{label}</option>)}</select></label>
              <label className="pc-field">اولویت<select value={draft.priority} onChange={(event) => setDraft({ ...draft, priority: event.target.value })}>{Object.entries(PRIORITIES).map(([key, label]) => <option key={key} value={key}>{label}</option>)}</select></label>
              <label className="pc-field">وضعیت<select value={draft.status} onChange={(event) => setDraft({ ...draft, status: event.target.value })}>{Object.entries(STATUSES).map(([key, label]) => <option key={key} value={key}>{label}</option>)}</select></label>
              <label className="pc-field">تکرار<select value={draft.recurrence} onChange={(event) => setDraft({ ...draft, recurrence: event.target.value })}>{Object.entries(REPEATS).map(([key, label]) => <option key={key} value={key}>{label}</option>)}</select></label>
            </div>
            <section><h4>زمان‌بندی</h4><div className="pc-form-grid">
              <label className="pc-field">تاریخ<input type="date" value={draft.dueDate} onChange={(event) => setDraft({ ...draft, dueDate: event.target.value })} />{draft.dueDate && <small>{formatPersianDate(draft.dueDate)}</small>}</label>
              <label className="pc-check"><input type="checkbox" checked={draft.allDay} onChange={(event) => setDraft({ ...draft, allDay: event.target.checked, startTime: event.target.checked ? "" : draft.startTime, endTime: event.target.checked ? "" : draft.endTime })} />تمام‌روز</label>
              {!draft.allDay && <><label className="pc-field">شروع<input type="time" value={draft.startTime} onChange={(event) => setDraft({ ...draft, startTime: event.target.value })} /></label><label className="pc-field">پایان<input type="time" value={draft.endTime} onChange={(event) => setDraft({ ...draft, endTime: event.target.value })} /></label></>}
            </div></section>
            <section><h4>اطلاعات حقوقی</h4><div className="pc-form-grid">
              <label className="pc-field">پرونده<input value={draft.caseReference} onChange={(event) => setDraft({ ...draft, caseReference: event.target.value })} /></label>
              <label className="pc-field">منشأ مهلت<input value={draft.source} onChange={(event) => setDraft({ ...draft, source: event.target.value })} /></label>
              <label className="pc-field">مکان<input value={draft.location} onChange={(event) => setDraft({ ...draft, location: event.target.value })} /></label>
              <label className="pc-field">برچسب‌ها<input value={draft.tagsText} onChange={(event) => setDraft({ ...draft, tagsText: event.target.value })} placeholder="با ویرگول جدا کنید" /></label>
            </div><label className="pc-field full">توضیحات<textarea value={draft.description} onChange={(event) => setDraft({ ...draft, description: event.target.value })} /></label></section>
            <section><h4>یادآورها</h4><div className="pc-reminders">{REMINDERS.map(([minutes, label]) => {
              const checked = draft.reminderMinutes.includes(minutes);
              return <label key={minutes} data-checked={checked}><input type="checkbox" checked={checked} onChange={() => setDraft({ ...draft, reminderMinutes: checked ? draft.reminderMinutes.filter((value) => value !== minutes) : [...draft.reminderMinutes, minutes].slice(0, 8) })} />{label}</label>;
            })}</div></section>
            <section><div className="pc-section-head"><h4>چک‌لیست</h4><button type="button" onClick={() => setDraft({ ...draft, checklist: [...draft.checklist, { id: uid("check"), label: "", done: false }] })}><Icon name="plus" />افزودن</button></div>
              <div className="pc-checklist">{draft.checklist.length === 0 && <small>چک‌لیستی تعریف نشده است.</small>}{draft.checklist.map((entry, index) => <div key={entry.id}>
                <input type="checkbox" checked={entry.done} onChange={(event) => { const list = clone(draft.checklist); list[index].done = event.target.checked; setDraft({ ...draft, checklist: list }); }} />
                <input value={entry.label} onChange={(event) => { const list = clone(draft.checklist); list[index].label = event.target.value; setDraft({ ...draft, checklist: list }); }} placeholder="عنوان مرحله" />
                <button type="button" className="pc-icon small" aria-label="حذف مرحله" onClick={() => setDraft({ ...draft, checklist: draft.checklist.filter((item) => item.id !== entry.id) })}><Icon name="trash" /></button>
              </div>)}</div>
            </section>
          </div>
          <footer><button type="button" className="pc-secondary" onClick={() => setEditor(null)}>انصراف</button><button className="pc-primary" type="submit">ذخیره برنامه</button></footer>
        </form>
      </div>}
      {(toast || undoState) && <div className={`pc-toast ${toast?.tone === "error" ? "error" : ""}`} role="status"><span>{toast?.text || undoState?.message}</span>{undoState && <button onClick={undo}>بازگردانی</button>}</div>}
    </div>;
  }

  function install() {
    const modules = window.MODULES;
    if (!modules?.clock) return;
    modules.clock = { ...modules.clock, title: "تقویم و ساعت حقوقی", View: AdvancedCalendarView };
    modules.calendar = modules.calendar
      ? { ...modules.calendar, title: "تقویم و ساعت حقوقی", View: AdvancedCalendarView }
      : modules.clock;
  }
  window.AdvancedCalendarView = AdvancedCalendarView;
  install();
})();
