const { useMemo: useMemoSched, useState: useStateSched } = React;

const SCHED_SECTORS = [
  "Arts & Culture",
  "Education",
  "Healthcare",
  "Human Services",
  "Foundations",
  "Religious Orgs",
  "Public Benefit",
];

const SCHED_SERVICES = [
  "Nonprofit Audit",
  "Single Audit (UG)",
  "Form 990 Filing",
  "Reviewed Financials",
];

const SCHED_PROFILE = {
  "Nonprofit Audit": {
    fee: 52000,
    hours: 300,
    roles: [
      { role: "Partner", hours: 12 },
      { role: "Manager", hours: 42 },
      { role: "Senior", hours: 104 },
      { role: "Associate", hours: 142 },
    ],
  },
  "Single Audit (UG)": {
    fee: 74000,
    hours: 430,
    roles: [
      { role: "Partner", hours: 18 },
      { role: "Manager", hours: 64 },
      { role: "Senior", hours: 152 },
      { role: "Associate", hours: 196 },
    ],
  },
  "Form 990 Filing": {
    fee: 18500,
    hours: 120,
    roles: [
      { role: "Partner", hours: 5 },
      { role: "Manager", hours: 24 },
      { role: "Senior", hours: 38 },
      { role: "Associate", hours: 53 },
    ],
  },
  "Reviewed Financials": {
    fee: 39000,
    hours: 220,
    roles: [
      { role: "Partner", hours: 9 },
      { role: "Manager", hours: 34 },
      { role: "Senior", hours: 78 },
      { role: "Associate", hours: 99 },
    ],
  },
};

const SCHED_DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri"];
const SCHED_TODAY = new Date(2026, 4, 24);

const SCHED_ROLE_ORDER = {
  Partner: 0,
  Manager: 1,
  Senior: 2,
  Associate: 3,
};

const SCHED_ROLE_META = {
  Partner: { billable: 275, cost: 118, weeklyCapacity: 28 },
  Manager: { billable: 195, cost: 82, weeklyCapacity: 36 },
  Senior: { billable: 155, cost: 58, weeklyCapacity: 40 },
  Associate: { billable: 105, cost: 38, weeklyCapacity: 40 },
};

const SCHED_ROLE_COLORS = {
  Partner: "partner",
  Manager: "manager",
  Senior: "senior",
  Associate: "associate",
};

const SCHED_CERTS_BY_INITIALS = {
  JH: ["CPA", "Nonprofit"],
  MP: ["CPA", "CNAP", "Single Audit"],
  MS: ["CPA", "Single Audit"],
  RT: ["CNAP"],
  DC: ["CPA"],
  LM: ["CPA"],
  EN: ["CPA", "Single Audit"],
  PV: ["CPA"],
  KB: ["CNAP"],
};

const SCHED_FORWARD_CAPACITY = {
  JH: { plannedUtil: 62, note: "Partner review block held" },
  MP: { plannedUtil: 68, note: "New audit starts open" },
  DC: { plannedUtil: 66, note: "Healthcare work tapering" },
  SO: { plannedUtil: 72, note: "Tax work wrapping" },
  MS: { plannedUtil: 60, note: "Meridian prelim frees in June" },
  EN: { plannedUtil: 64, note: "Single Audit block available" },
  LM: { plannedUtil: 68, note: "Fieldwork reserve" },
  PV: { plannedUtil: 54, note: "Best near-term flex" },
  RT: { plannedUtil: 52, note: "Meridian hours release" },
  KB: { plannedUtil: 56, note: "Open associate block" },
};

function schedMoney(n) {
  return `$${Number(n || 0).toLocaleString("en-US")}`;
}

function schedShortMoney(n) {
  return `$${Math.round(Number(n || 0) / 1000).toLocaleString("en-US")}k`;
}

function schedOnboardingHref(form) {
  const params = new URLSearchParams({
    client: form.orgName || "New Client",
    legal: form.legalName || form.orgName || "New Client",
    contact: form.contactName || "Primary Contact",
    email: form.contactEmail || "",
    sector: form.sector || "Nonprofit",
    entity: form.entityType || "501(c)(3)",
    fye: form.fye || "06/30/2026",
    service: form.service || "Nonprofit Audit",
  });
  return `Audit Onboarding.html?${params.toString()}`;
}

function schedClamp(n, min, max) {
  return Math.max(min, Math.min(max, n));
}

function schedServiceFromProject(project) {
  const type = project.type || "";
  if (type.includes("Single Audit")) return "Single Audit (UG)";
  if (type.includes("Form 990")) return "Form 990 Filing";
  if (type.includes("Reviewed")) return "Reviewed Financials";
  if (type.includes("Nonprofit")) return "Nonprofit Audit";
  return "Reviewed Financials";
}

function schedSectorFromProject(project) {
  const text = `${project.client} ${project.type}`.toLowerCase();
  if (text.includes("arts") || text.includes("museum") || text.includes("conservancy")) return "Arts & Culture";
  if (text.includes("school") || text.includes("university") || text.includes("education") || text.includes("library")) return "Education";
  if (text.includes("medical") || text.includes("hospital") || text.includes("health")) return "Healthcare";
  if (text.includes("foundation") || text.includes("trust")) return "Foundations";
  if (text.includes("church") || text.includes("religious")) return "Religious Orgs";
  if (text.includes("nonprofit") || text.includes("community")) return "Human Services";
  return "Public Benefit";
}

function schedResourceRate(role) {
  return SCHED_ROLE_META[role]?.billable || 105;
}

function schedResourceCost(role) {
  return SCHED_ROLE_META[role]?.cost || 38;
}

function schedResourceCapacity(role) {
  return SCHED_ROLE_META[role]?.weeklyCapacity || 40;
}

function schedDateFromValue(value) {
  if (!value) return null;
  const parts = String(value).split("-").map(Number);
  if (parts.length === 3 && parts.every(Boolean)) return new Date(parts[0], parts[1] - 1, parts[2]);
  return null;
}

function schedWeeksUntil(deadline) {
  const target = schedDateFromValue(deadline);
  if (!target) return 12;
  const days = Math.max(1, Math.round((target - SCHED_TODAY) / 86400000));
  return Math.max(1, Math.ceil(days / 7));
}

function schedIsSingleAudit(form) {
  return form.service === "Single Audit (UG)" || Number(form.federalFunding || 0) >= 750000;
}

function schedComplexity(form) {
  let score = 1;
  const funding = Number(form.federalFunding || 0);

  if (schedIsSingleAudit(form)) score += 0.32;
  if (funding >= 2000000) score += 0.12;
  if (form.donorRestricted) score += 0.13;
  if (form.multiProgram) score += 0.09;
  if (!form.priorAudit) score += 0.12;
  if (form.endowment) score += 0.08;
  if (form.sector === "Healthcare" || form.sector === "Education") score += 0.06;

  const weeks = schedWeeksUntil(form.deadline);
  if (weeks <= 6) score += 0.18;
  else if (weeks <= 10) score += 0.08;

  return schedClamp(score, 0.85, 1.85);
}

function schedEstimateProject(form) {
  const profile = SCHED_PROFILE[form.service] || SCHED_PROFILE["Nonprofit Audit"];
  const complexity = schedComplexity(form);
  const budgetScale = Number(form.budgetHours || profile.hours) / profile.hours;
  const blendedScale = schedClamp((complexity * 0.72) + (budgetScale * 0.28), 0.75, 2.1);
  const singleAudit = schedIsSingleAudit(form);
  const roles = profile.roles.map((need) => {
    let multiplier = blendedScale;
    if (singleAudit && need.role === "Senior") multiplier += 0.18;
    if (singleAudit && need.role === "Associate") multiplier += 0.12;
    if (form.donorRestricted && need.role === "Senior") multiplier += 0.08;
    if (schedWeeksUntil(form.deadline) <= 6 && (need.role === "Manager" || need.role === "Senior")) multiplier += 0.08;

    return {
      ...need,
      id: `base-${need.role}`,
      hours: Math.max(4, Math.round(need.hours * multiplier / 2) * 2),
      baseHours: need.hours,
      custom: false,
    };
  });

  const totalHours = roles.reduce((sum, role) => sum + role.hours, 0);
  const roleCost = roles.reduce((sum, role) => sum + role.hours * schedResourceCost(role.role), 0);
  const fee = Number(form.fee || profile.fee);
  const margin = fee > 0 ? ((fee - roleCost) / fee) * 100 : 0;
  const weeks = schedWeeksUntil(form.deadline);
  const weeklyDemand = Math.ceil(totalHours / Math.max(1, weeks));

  const flags = [];
  if (singleAudit) flags.push("Uniform Guidance");
  if (form.donorRestricted) flags.push("Donor restrictions");
  if (form.endowment) flags.push("Endowment testing");
  if (!form.priorAudit) flags.push("First-year audit");
  if (weeks <= 6) flags.push("Compressed deadline");

  return {
    profile,
    complexity,
    roles,
    totalHours,
    roleCost,
    margin,
    weeks,
    weeklyDemand,
    flags,
    singleAudit,
  };
}

function schedRoleCanCover(resourceRole, requiredRole) {
  if (resourceRole === requiredRole) return true;
  if (requiredRole === "Associate" && resourceRole === "Senior") return true;
  if (requiredRole === "Senior" && resourceRole === "Manager") return true;
  return false;
}

function schedRoleMatchScore(resourceRole, requiredRole) {
  if (resourceRole === requiredRole) return 20;
  if (requiredRole === "Associate" && resourceRole === "Senior") return 12;
  if (requiredRole === "Senior" && resourceRole === "Manager") return 10;
  return -20;
}

function schedStatusFromScore(score) {
  if (score >= 82) return "green";
  if (score >= 66) return "yellow";
  return "red";
}

function schedRoleLoadWeeks(weeks, role) {
  const cap =
    role === "Partner" ? 14 :
    role === "Manager" ? 12 : 10;
  return Math.max(2, Math.min(weeks, cap));
}

function schedWeeklyLoadForRole(hours, estimate, role) {
  const loadWeeks = schedRoleLoadWeeks(estimate.weeks, role);
  return Math.max(1, Math.ceil(Number(hours || 0) / loadWeeks));
}

function schedCapacitySnapshot(resource, weeklyLoad) {
  const projectedWeek = resource.scheduledWeek + weeklyLoad;
  const projectedUtilization = Math.round((projectedWeek / resource.weeklyCapacity) * 100);
  const overCapacityHours = Math.max(0, projectedWeek - resource.weeklyCapacity);
  const overCapacityPct = Math.round((overCapacityHours / resource.weeklyCapacity) * 100);
  const availableAfterProject = Math.max(0, resource.weeklyCapacity - projectedWeek);
  const capacityStatus =
    overCapacityPct >= 15 ? "hard" :
    overCapacityPct > 0 ? "soft" : "clear";

  return {
    projectedWeek,
    projectedUtilization,
    overCapacityHours,
    overCapacityPct,
    availableAfterProject,
    capacityStatus,
  };
}

function schedBuildResources(projects) {
  const map = new Map();
  const capacity = new Map((window.MANAGER_DATA?.capacity || []).map((r) => [r.initials, r]));

  projects.forEach((project) => {
    const service = schedServiceFromProject(project);
    const sector = schedSectorFromProject(project);
    project.team.forEach((member) => {
      const role = member.role === "Associate" ? "Associate" : member.role;
      if (!map.has(member.initials)) {
        map.set(member.initials, {
          id: member.initials.toLowerCase(),
          initials: member.initials,
          name: member.name,
          role,
          hoursToDate: 0,
          projects: new Set(),
          services: new Set(),
          sectors: new Set(),
          clients: [],
          serviceCounts: {},
          sectorCounts: {},
        });
      }
      const resource = map.get(member.initials);
      resource.role = resource.role || role;
      resource.hoursToDate += member.hours;
      resource.projects.add(project.id);
      resource.services.add(service);
      resource.sectors.add(sector);
      resource.clients.push(project.client);
      resource.serviceCounts[service] = (resource.serviceCounts[service] || 0) + 1;
      resource.sectorCounts[sector] = (resource.sectorCounts[sector] || 0) + 1;
    });
  });

  return Array.from(map.values()).map((resource) => {
    const cap = capacity.get(resource.initials);
    const forward = SCHED_FORWARD_CAPACITY[resource.initials];
    const activeProjects = resource.projects.size;
    const weeklyCapacity = schedResourceCapacity(resource.role);
    const baseUtil =
      resource.role === "Partner" ? 72 :
      resource.role === "Manager" ? 84 :
      resource.role === "Senior" ? 80 : 74;
    const currentUtilization = cap ? cap.util : schedClamp(baseUtil + activeProjects * 3 + Math.floor(resource.hoursToDate / 90), 42, 116);
    const planningUtilization = forward ? forward.plannedUtil : schedClamp(currentUtilization - 14, 48, 90);
    const scheduledWeek = Math.round((planningUtilization / 100) * weeklyCapacity);
    const charSeed = resource.initials.charCodeAt(0) + resource.initials.charCodeAt(1);
    const calendar = SCHED_DAYS.map((day, index) => {
      const jitter = ((charSeed + index * 3) % 5) - 2;
      return schedClamp(Math.round(scheduledWeek / 5) + jitter, resource.role === "Partner" ? 1 : 2, 11);
    });
    const certifications = SCHED_CERTS_BY_INITIALS[resource.initials] || (
      resource.role === "Partner" || resource.role === "Manager" ? ["CPA"] :
      resource.role === "Senior" ? ["CPA"] : []
    );
    const realization = schedClamp(78 + (resource.services.size * 3) - Math.max(0, planningUtilization - 95) * 0.6, 55, 96);

    return {
      ...resource,
      services: Array.from(resource.services),
      sectors: Array.from(resource.sectors),
      activeProjects,
      utilization: planningUtilization,
      currentUtilization,
      capacityNote: forward?.note || "Modeled forward availability",
      scheduledWeek,
      weeklyCapacity,
      availableHours: Math.max(0, weeklyCapacity - scheduledWeek),
      hourlyRate: schedResourceRate(resource.role),
      costRate: schedResourceCost(resource.role),
      certifications,
      realization: Math.round(realization),
      calendar,
    };
  }).sort((a, b) => {
    return (SCHED_ROLE_ORDER[a.role] ?? 9) - (SCHED_ROLE_ORDER[b.role] ?? 9) || a.name.localeCompare(b.name);
  });
}

function schedDefaultForm(projects) {
  const first = projects[0] || {};
  const profile = SCHED_PROFILE["Nonprofit Audit"];
  return {
    clientMode: "new",
    existingClientId: first.id || "",
    orgName: "Meridian Arts Foundation",
    legalName: "Meridian Arts Foundation",
    contactName: "Lena Park",
    contactEmail: "lena.park@meridianarts.org",
    sector: "Arts & Culture",
    entityType: "501(c)(3)",
    fye: "06/30/2026",
    service: "Nonprofit Audit",
    deadline: "2026-08-18",
    fee: profile.fee,
    budgetHours: profile.hours,
    federalFunding: 420000,
    donorRestricted: true,
    multiProgram: true,
    priorAudit: true,
    endowment: true,
    notes: "Annual audit with grants and donor-restricted funds. Client expects fieldwork before the August board meeting.",
  };
}

function schedApplyExistingClient(projects, form, clientId) {
  const project = projects.find((p) => p.id === clientId) || projects[0];
  if (!project) return form;
  const service = schedServiceFromProject(project);
  const profile = SCHED_PROFILE[service] || SCHED_PROFILE["Nonprofit Audit"];
  return {
    ...form,
    clientMode: "existing",
    existingClientId: project.id,
    orgName: project.client,
    legalName: project.client,
    sector: schedSectorFromProject(project),
    fye: project.fye,
    service,
    fee: project.fee || profile.fee,
    budgetHours: project.budgetHours || profile.hours,
    deadline: "2026-08-18",
    federalFunding: service === "Single Audit (UG)" ? 1250000 : form.federalFunding,
    donorRestricted: project.type.includes("Nonprofit") || service === "Single Audit (UG)",
  };
}

function schedBuildRecommendations(form, resources, currentUser) {
  const estimate = schedEstimateProject(form);
  const directTeam = new Set((window.MANAGER_DATA?.capacity || []).map((r) => r.initials));
  const federalThreshold = estimate.singleAudit;
  const clientToken = (form.orgName || "").split(" ")[0]?.toLowerCase();

  return estimate.roles.map((need, index) => {
    const candidates = resources
      .filter((resource) => schedRoleCanCover(resource.role, need.role))
      .map((resource) => {
        const reasons = [];
        const warnings = [];
        let score = 42;
        const weeklyLoad = schedWeeklyLoadForRole(need.hours, estimate, need.role);
        const capacity = schedCapacitySnapshot(resource, weeklyLoad);
        const sectorDepth = resource.sectorCounts?.[form.sector] || 0;
        const serviceDepth = resource.serviceCounts?.[form.service] || 0;

        const roleScore = schedRoleMatchScore(resource.role, need.role);
        score += roleScore;
        reasons.push(roleScore >= 20 ? `${need.role} coverage` : `${resource.role} can cover`);

        if (sectorDepth > 0) {
          score += Math.min(18, 10 + sectorDepth * 2);
          reasons.push(`${form.sector} history`);
        } else if (["Healthcare", "Education", "Foundations"].includes(form.sector)) {
          score -= 4;
          warnings.push("limited sector history");
        }

        if (serviceDepth > 0) {
          score += Math.min(18, 9 + serviceDepth * 3);
          reasons.push(`${form.service} experience`);
        } else if (form.service === "Single Audit (UG)") {
          score -= 8;
        }

        if (federalThreshold && resource.certifications.includes("Single Audit")) {
          score += 18;
          reasons.push("UG qualified");
        } else if (federalThreshold) {
          score -= 16;
          warnings.push("UG credential gap");
        }

        if ((need.role === "Partner" || need.role === "Manager" || need.role === "Senior") && resource.certifications.includes("CPA")) {
          score += 6;
          reasons.push("CPA");
        }

        if ((form.donorRestricted || form.entityType === "Private foundation") && resource.certifications.includes("CNAP")) {
          score += 8;
          reasons.push("CNAP");
        }

        if (form.donorRestricted && resource.sectors.some((s) => ["Foundations", "Arts & Culture", "Human Services"].includes(s))) {
          score += 5;
          reasons.push("restricted funds");
        }

        if (currentUser.roleKey === "manager" && directTeam.has(resource.initials)) {
          score += 5;
          reasons.push("manager team");
        }

        if (clientToken && resource.clients.some((client) => client.toLowerCase().includes(clientToken))) {
          score += 7;
          reasons.push("client continuity");
        }

        if (capacity.capacityStatus === "clear") {
          score += 17;
          reasons.push(`${capacity.availableAfterProject}h slack`);
        } else if (capacity.capacityStatus === "hard") {
          score -= 30 + capacity.overCapacityPct;
          warnings.push(`${capacity.overCapacityPct}% over capacity`);
        } else {
          score -= Math.max(6, capacity.overCapacityPct);
          warnings.push(`${capacity.overCapacityPct}% over capacity`);
          if (resource.availableHours > 0) reasons.push(`${resource.availableHours}h flex`);
        }

        if (estimate.margin < 30 && resource.costRate / resource.hourlyRate > 0.45) {
          score -= 5;
          warnings.push("margin pressure");
        } else {
          score += Math.round((resource.realization - 70) / 5);
        }

        return {
          ...resource,
          fitScore: schedClamp(score, 12, 99),
          status: schedStatusFromScore(schedClamp(score, 12, 99)),
          neededHours: need.hours,
          weeklyLoad,
          ...capacity,
          marginPct: Math.round(((resource.hourlyRate - resource.costRate) / resource.hourlyRate) * 100),
          warnings: warnings.slice(0, 3),
          reasons: reasons.slice(0, 5),
        };
      })
      .sort((a, b) => b.fitScore - a.fitScore || a.utilization - b.utilization);

    return {
      id: need.id || `base-${need.role}-${index}`,
      role: need.role,
      requiredRole: need.role,
      hours: need.hours,
      baseHours: need.baseHours,
      custom: !!need.custom,
      label: need.custom ? need.label : need.role,
      candidates,
    };
  });
}

function schedBuildBacklog(form) {
  const singleAudit = form.service === "Single Audit (UG)" || Number(form.federalFunding || 0) >= 750000;
  const nonprofit = form.service === "Nonprofit Audit" || singleAudit;

  const tasks = [
    { title: "Engagement setup and onboarding packet", role: "Manager", hours: 6, phase: "Activation" },
    { title: "Planning analytics and risk assessment", role: "Senior", hours: nonprofit ? 18 : 14, phase: "Planning" },
    { title: "PBC request list and client portal cadence", role: "Associate", hours: 8, phase: "Activation" },
    { title: "Internal controls walkthroughs", role: "Senior", hours: nonprofit ? 22 : 16, phase: "Prelim" },
    { title: "Cash and investment testing", role: "Associate", hours: form.endowment ? 24 : 16, phase: "Fieldwork" },
    { title: "Contributions and donor restrictions testing", role: "Senior", hours: form.donorRestricted ? 28 : 14, phase: "Fieldwork" },
    { title: "Manager review and budget checkpoint", role: "Manager", hours: 12, phase: "Review" },
    { title: "Partner final review and board deliverable", role: "Partner", hours: 7, phase: "Wrap" },
  ];

  if (singleAudit) {
    tasks.splice(5, 0,
      { title: "SEFA tie-out and major program selection", role: "Senior", hours: 22, phase: "Single Audit" },
      { title: "Uniform Guidance compliance testing", role: "Senior", hours: 34, phase: "Single Audit" },
      { title: "Federal award voucher sampling", role: "Associate", hours: 26, phase: "Single Audit" },
    );
  }

  return tasks.map((task, index) => ({
    ...task,
    id: `draft-task-${index + 1}`,
    project: form.orgName,
  }));
}

function schedBuildCustomRecommendation(slot, form, resources, currentUser) {
  const estimate = schedEstimateProject(form);
  const slotHours = Math.max(1, Number(slot.hours || 16));
  const weeklyLoad = schedWeeklyLoadForRole(slotHours, estimate, slot.role);
  const base = schedBuildRecommendations(form, resources, currentUser).find((rec) => rec.role === slot.role);
  const sourceCandidates = base?.candidates || resources.filter((resource) => schedRoleCanCover(resource.role, slot.role));
  const candidates = sourceCandidates.map((resource) => {
    const capacity = schedCapacitySnapshot(resource, weeklyLoad);
    const capacityPenalty = capacity.capacityStatus === "hard" ? -18 - capacity.overCapacityPct : capacity.capacityStatus === "soft" ? -8 : 0;
    const fitScore = schedClamp((resource.fitScore || 56 + schedRoleMatchScore(resource.role, slot.role) + capacity.availableAfterProject) + capacityPenalty, 20, 92);
    return {
      ...resource,
      fitScore,
      status: schedStatusFromScore(fitScore),
      neededHours: slotHours,
      weeklyLoad,
      ...capacity,
      marginPct: Math.round(((resource.hourlyRate - resource.costRate) / resource.hourlyRate) * 100),
      warnings: capacity.overCapacityPct > 0 ? [`${capacity.overCapacityPct}% over capacity`] : resource.warnings || [],
      reasons: [...(resource.reasons || []).slice(0, 3), "manual role"].slice(0, 5),
    };
  }).sort((a, b) => b.fitScore - a.fitScore || a.utilization - b.utilization);

  return {
    id: slot.id,
    role: slot.role,
    requiredRole: slot.role,
    hours: slotHours,
    baseHours: slotHours,
    custom: true,
    label: slot.label || "Added specialist",
    candidates,
  };
}

function schedCapacityPenalty(utilization) {
  if (utilization > 115) return -24;
  if (utilization > 100) return -14;
  return 0;
}

function schedSelectedRoster(recommendations, edits) {
  return recommendations.map((rec) => {
    const edit = edits[rec.id] || {};
    if (edit.removed) return null;
    const selectedId = edit.resourceId || rec.candidates[0]?.id;
    const selected = rec.candidates.find((candidate) => candidate.id === selectedId) || rec.candidates[0];
    if (!selected) return null;
    const hours = Math.max(1, Number(edit.hours ?? rec.hours));
    const baseHours = Math.max(1, Number(selected.neededHours || rec.hours || hours));
    const weeklyLoad = Math.max(1, Math.ceil((selected.weeklyLoad || Math.ceil(baseHours / 6)) * (hours / baseHours)));
    const capacity = schedCapacitySnapshot(selected, weeklyLoad);
    const fitScore = schedClamp(
      (selected.fitScore || 60) + schedCapacityPenalty(capacity.projectedUtilization) - schedCapacityPenalty(selected.projectedUtilization || 0),
      12,
      99
    );
    const warnings = (selected.warnings || []).filter((warning) => !warning.includes("over capacity") && warning !== "hard capacity conflict");
    if (capacity.overCapacityPct > 0) warnings.push(`${capacity.overCapacityPct}% over capacity`);

    return {
      id: rec.id,
      role: rec.role,
      label: rec.label,
      custom: rec.custom,
      requiredRole: rec.requiredRole,
      hours,
      resource: {
        ...selected,
        neededHours: hours,
        weeklyLoad,
        ...capacity,
        fitScore,
        status: schedStatusFromScore(fitScore),
        warnings,
      },
      candidates: rec.candidates,
      manual: !!edit.resourceId || edit.hours != null || rec.custom,
    };
  }).filter(Boolean);
}

function schedPlanMetrics(form, estimate, roster) {
  const plannedHours = roster.reduce((sum, item) => sum + item.hours, 0);
  const plannedCost = roster.reduce((sum, item) => sum + item.hours * item.resource.costRate, 0);
  const plannedBillable = roster.reduce((sum, item) => sum + item.hours * item.resource.hourlyRate, 0);
  const fee = Number(form.fee || 0);
  const margin = fee > 0 ? ((fee - plannedCost) / fee) * 100 : 0;
  const avgFit = Math.round(roster.reduce((sum, item) => sum + item.resource.fitScore, 0) / Math.max(1, roster.length));
  const conflicts = roster.filter((item) => item.resource.overCapacityPct > 0);
  const severeConflicts = roster.filter((item) => item.resource.capacityStatus === "hard");
  const maxOverCapacityPct = roster.reduce((max, item) => Math.max(max, item.resource.overCapacityPct || 0), 0);
  const openCapacity = roster.reduce((sum, item) => sum + Math.max(0, item.resource.weeklyCapacity - item.resource.projectedWeek), 0);
  const coverage = estimate.totalHours > 0 ? Math.round((plannedHours / estimate.totalHours) * 100) : 0;
  const confidence = schedClamp(Math.round(avgFit - severeConflicts.length * 12 - conflicts.length * 4 - Math.ceil(maxOverCapacityPct / 2) + (margin >= 40 ? 6 : margin < 25 ? -10 : 0)), 28, 98);
  const alerts = [];

  if (estimate.singleAudit && !roster.some((item) => item.resource.certifications.includes("Single Audit"))) {
    alerts.push("No Single Audit-qualified staff currently selected.");
  }
  if (severeConflicts.length) alerts.push(`${severeConflicts.length} selected staff member${severeConflicts.length > 1 ? "s have" : " has"} a hard capacity conflict; highest overage is ${maxOverCapacityPct}%.`);
  else if (conflicts.length) alerts.push(`${conflicts.length} selected staff member${conflicts.length > 1 ? "s" : ""} will exceed weekly capacity; highest overage is ${maxOverCapacityPct}%.`);
  if (margin < 25) alerts.push("Projected margin is below the firm's 25% review threshold.");
  if (coverage < 90) alerts.push("Staffing plan is under the estimated role demand.");
  if (!alerts.length) alerts.push("Plan is ready for manager review.");

  return {
    plannedHours,
    plannedCost,
    plannedBillable,
    margin,
    avgFit,
    conflicts,
    severeConflicts,
    maxOverCapacityPct,
    openCapacity,
    coverage,
    confidence,
    alerts,
  };
}

function schedBuildAllocations(backlog, roster) {
  const byRole = new Map();
  roster.forEach((item) => {
    if (!byRole.has(item.role)) byRole.set(item.role, item.resource);
  });
  const roleOffset = { Partner: 4, Manager: 1, Senior: 2, Associate: 0 };

  return backlog.map((task, index) => {
    const resource = byRole.get(task.role);
    return {
      ...task,
      resourceId: resource?.id,
      resourceName: resource?.name || "Unassigned",
      initials: resource?.initials || "--",
      dayIndex: (index + (roleOffset[task.role] || 0)) % SCHED_DAYS.length,
      scheduledHours: Math.min(8, Math.max(2, Math.round(task.hours / 2))),
    };
  });
}

function ScheduleWorkspace({ currentUser, projects }) {
  const resources = useMemoSched(() => schedBuildResources(projects), [projects]);
  const [form, setForm] = useStateSched(() => schedDefaultForm(projects));
  const [activeTab, setActiveTab] = useStateSched("project");
  const [staffEdits, setStaffEdits] = useStateSched({});
  const [customStaff, setCustomStaff] = useStateSched([]);
  const [generated, setGenerated] = useStateSched(false);

  const estimate = useMemoSched(() => schedEstimateProject(form), [form]);
  const baseRecommendations = useMemoSched(
    () => schedBuildRecommendations(form, resources, currentUser),
    [form, resources, currentUser]
  );
  const customRecommendations = useMemoSched(
    () => customStaff.map((slot) => schedBuildCustomRecommendation(slot, form, resources, currentUser)),
    [customStaff, form, resources, currentUser]
  );
  const recommendations = useMemoSched(
    () => [...baseRecommendations, ...customRecommendations],
    [baseRecommendations, customRecommendations]
  );
  const roster = useMemoSched(() => schedSelectedRoster(recommendations, staffEdits), [recommendations, staffEdits]);
  const backlog = useMemoSched(() => schedBuildBacklog(form), [form]);
  const allocations = schedBuildAllocations(backlog, roster);
  const profile = estimate.profile;
  const planMetrics = schedPlanMetrics(form, estimate, roster);

  const update = (key, value) => {
    setGenerated(false);
    setForm((prev) => ({ ...prev, [key]: value }));
  };

  const updateService = (service) => {
    const nextProfile = SCHED_PROFILE[service] || SCHED_PROFILE["Nonprofit Audit"];
    setGenerated(false);
    setForm((prev) => ({
      ...prev,
      service,
      fee: nextProfile.fee,
      budgetHours: nextProfile.hours,
      federalFunding: service === "Single Audit (UG)" ? Math.max(Number(prev.federalFunding || 0), 900000) : prev.federalFunding,
    }));
  };

  const switchExistingClient = (clientId) => {
    setGenerated(false);
    setStaffEdits({});
    setCustomStaff([]);
    setForm((prev) => schedApplyExistingClient(projects, prev, clientId));
  };

  const setStaffEdit = (recommendationId, patch) => {
    setGenerated(false);
    setStaffEdits((prev) => ({
      ...prev,
      [recommendationId]: {
        ...(prev[recommendationId] || {}),
        ...patch,
      },
    }));
  };

  const removeStaff = (recommendationId, custom) => {
    setGenerated(false);
    if (custom) {
      setCustomStaff((prev) => prev.filter((slot) => slot.id !== recommendationId));
      setStaffEdits((prev) => {
        const next = { ...prev };
        delete next[recommendationId];
        return next;
      });
      return;
    }
    setStaffEdits((prev) => ({
      ...prev,
      [recommendationId]: {
        ...(prev[recommendationId] || {}),
        removed: true,
      },
    }));
  };

  const restoreBaseStaff = () => {
    setGenerated(false);
    setStaffEdits((prev) => {
      const next = { ...prev };
      baseRecommendations.forEach((rec) => {
        if (next[rec.id]?.removed) delete next[rec.id];
      });
      return next;
    });
  };

  const addSpecialist = () => {
    setGenerated(false);
    setCustomStaff((prev) => [
      ...prev,
      {
        id: `custom-${Date.now()}`,
        role: "Senior",
        label: "Specialist",
        hours: 16,
      },
    ]);
  };

  const updateCustomSlot = (recommendationId, patch) => {
    setGenerated(false);
    setCustomStaff((prev) => prev.map((slot) => (
      slot.id === recommendationId ? { ...slot, ...patch } : slot
    )));
  };

  const handleGenerate = () => {
    setGenerated(true);
    setActiveTab("project");
  };

  if (currentUser.roleKey === "associate") {
    return (
      <div className="dash-body">
        <div className="card">
          <div className="k">Schedule</div>
          <h4>Manager and partner workspace</h4>
          <p className="sched-muted">Resource scheduling is scoped to project creators and engagement managers.</p>
        </div>
      </div>
    );
  }

  return (
    <div className="dash-body scheduler-workspace">
      <div className="sched-hero">
        <div>
          <div className="k">Resource Scheduling</div>
          <h3>Create New Project</h3>
          <div className="sh-sub">Build the engagement, generate client onboarding, and approve the recommended team.</div>
        </div>
        <div className="sched-tabs">
          {[
            ["project", "Project Intake"],
            ["planner", "Capacity Planner"],
            ["board", "Staffing Board"],
          ].map(([key, label]) => (
            <button key={key} className={`filter-chip ${activeTab === key ? "on" : ""}`} onClick={() => setActiveTab(key)}>
              {label}
            </button>
          ))}
        </div>
      </div>

      {activeTab === "project" && (
        <div className="sched-intake-grid">
          <div className="card sched-form-card">
            <div className="sched-card-head">
              <div>
                <div className="k">Intake</div>
                <h4>Client and engagement details</h4>
              </div>
              <div className="sched-mini-stat">
                <span>{profile.hours}h</span>
                baseline
              </div>
            </div>

            <div className="sched-choice-row">
              <button className={`filter-chip ${form.clientMode === "new" ? "on" : ""}`} onClick={() => update("clientMode", "new")}>New client</button>
              <button className={`filter-chip ${form.clientMode === "existing" ? "on" : ""}`} onClick={() => switchExistingClient(form.existingClientId)}>Existing client</button>
            </div>

            {form.clientMode === "existing" && (
              <div className="sched-field">
                <label className="field-label">Client list</label>
                <select className="sched-input" value={form.existingClientId} onChange={(e) => switchExistingClient(e.target.value)}>
                  {projects.slice(0, 18).map((project) => (
                    <option key={project.id} value={project.id}>{project.client}</option>
                  ))}
                </select>
              </div>
            )}

            <div className="sched-form-grid two">
              <div className="sched-field">
                <label className="field-label">Organization name</label>
                <input className="sched-input" value={form.orgName} onChange={(e) => update("orgName", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Legal name</label>
                <input className="sched-input" value={form.legalName} onChange={(e) => update("legalName", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Primary contact</label>
                <input className="sched-input" value={form.contactName} onChange={(e) => update("contactName", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Contact email</label>
                <input className="sched-input" value={form.contactEmail} onChange={(e) => update("contactEmail", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Nonprofit sector</label>
                <select className="sched-input" value={form.sector} onChange={(e) => update("sector", e.target.value)}>
                  {SCHED_SECTORS.map((sector) => <option key={sector} value={sector}>{sector}</option>)}
                </select>
              </div>
              <div className="sched-field">
                <label className="field-label">Entity type</label>
                <select className="sched-input" value={form.entityType} onChange={(e) => update("entityType", e.target.value)}>
                  <option>501(c)(3)</option>
                  <option>501(c)(4)</option>
                  <option>Private foundation</option>
                  <option>Public charity</option>
                  <option>Religious organization</option>
                </select>
              </div>
            </div>

            <div className="sched-divider" />

            <div className="sched-form-grid two">
              <div className="sched-field">
                <label className="field-label">Engagement type</label>
                <select className="sched-input" value={form.service} onChange={(e) => updateService(e.target.value)}>
                  {SCHED_SERVICES.map((service) => <option key={service} value={service}>{service}</option>)}
                </select>
              </div>
              <div className="sched-field">
                <label className="field-label">Fiscal year end</label>
                <input className="sched-input" value={form.fye} onChange={(e) => update("fye", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Target delivery</label>
                <input className="sched-input" type="date" value={form.deadline} onChange={(e) => update("deadline", e.target.value)} />
              </div>
              <div className="sched-field">
                <label className="field-label">Federal funding</label>
                <input className="sched-input" type="number" value={form.federalFunding} onChange={(e) => update("federalFunding", Number(e.target.value))} />
              </div>
              <div className="sched-field">
                <label className="field-label">Expected fee</label>
                <input className="sched-input" type="number" value={form.fee} onChange={(e) => update("fee", Number(e.target.value))} />
              </div>
              <div className="sched-field">
                <label className="field-label">Budget hours</label>
                <input className="sched-input" type="number" value={form.budgetHours} onChange={(e) => update("budgetHours", Number(e.target.value))} />
              </div>
            </div>

            <div className="sched-switch-grid">
              {[
                ["donorRestricted", "Donor restricted funds"],
                ["multiProgram", "Multiple programs"],
                ["priorAudit", "Prior year audit"],
                ["endowment", "Endowment or investments"],
              ].map(([key, label]) => (
                <button key={key} className={`sched-toggle ${form[key] ? "on" : ""}`} onClick={() => update(key, !form[key])}>
                  <span className="toggle-knob" />
                  <span>{label}</span>
                </button>
              ))}
            </div>

            <div className="sched-field">
              <label className="field-label">Planning notes</label>
              <textarea className="sched-input sched-textarea" value={form.notes} onChange={(e) => update("notes", e.target.value)} />
            </div>
          </div>

          <ScheduleRecommendationPanel
            form={form}
            estimate={estimate}
            recommendations={recommendations}
            roster={roster}
            generated={generated}
            planMetrics={planMetrics}
            staffEdits={staffEdits}
            onStaffEdit={setStaffEdit}
            onAddSpecialist={addSpecialist}
            onRemoveStaff={removeStaff}
            onRestoreBaseStaff={restoreBaseStaff}
            onUpdateCustomSlot={updateCustomSlot}
            onGenerate={handleGenerate}
            onOpenPlanner={() => setActiveTab("planner")}
          />
        </div>
      )}

      {activeTab === "planner" && (
        <ScheduleCapacityPlanner
          form={form}
          estimate={estimate}
          roster={roster}
          backlog={backlog}
          allocations={allocations}
          planMetrics={planMetrics}
          onBack={() => setActiveTab("project")}
        />
      )}

      {activeTab === "board" && (
        <ScheduleStaffingBoard
          form={form}
          roster={roster}
          backlog={backlog}
          allocations={allocations}
          planMetrics={planMetrics}
          generated={generated}
          onGenerate={handleGenerate}
        />
      )}
    </div>
  );
}

function ScheduleRecommendationPanel({
  form,
  estimate,
  recommendations,
  roster,
  generated,
  planMetrics,
  staffEdits,
  onStaffEdit,
  onAddSpecialist,
  onRemoveStaff,
  onRestoreBaseStaff,
  onUpdateCustomSlot,
  onGenerate,
  onOpenPlanner,
}) {
  const onboardingHref = schedOnboardingHref(form);
  const hasRemovedBase = recommendations.some((rec) => !rec.custom && staffEdits[rec.id]?.removed);
  const confidenceStatus = planMetrics.confidence >= 78 ? "green" : planMetrics.confidence >= 62 ? "yellow" : "red";

  return (
    <div className="sched-side-stack">
      <div className="card">
        <div className="sched-card-head">
          <div>
            <div className="k">Recommended Staff</div>
            <h4>Live fit, capacity, and margin</h4>
          </div>
          <div className={`sched-fit-badge ${confidenceStatus}`}>{planMetrics.confidence}% confidence</div>
        </div>

        <div className="sched-metric-row">
          <div>
            <span>{schedShortMoney(form.fee)}</span>
            fee
          </div>
          <div>
            <span>{planMetrics.plannedHours}h</span>
            planned
          </div>
          <div>
            <span>{Math.round(planMetrics.margin)}%</span>
            margin
          </div>
          <div>
            <span>{estimate.singleAudit ? "UG" : "Std"}</span>
            scope
          </div>
        </div>

        <div className="sched-decision-strip">
          <div><span>Demand</span>{estimate.totalHours}h / {estimate.weeks}w</div>
          <div><span>Coverage</span>{planMetrics.coverage}%</div>
          <div><span>Open cap.</span>{planMetrics.openCapacity}h</div>
        </div>

        <div className="sched-alert-list">
          {planMetrics.alerts.map((alert, index) => (
            <div className={index === 0 && alert.includes("ready") ? "good" : ""} key={alert}>{alert}</div>
          ))}
        </div>

        <div className="sched-staff-list">
          {recommendations.map((rec) => {
            const edit = staffEdits[rec.id] || {};
            if (edit.removed) return null;
            const rosterItem = roster.find((item) => item.id === rec.id);
            const selectedId = edit.resourceId || rec.candidates[0]?.id;
            const selected = rosterItem?.resource || rec.candidates.find((candidate) => candidate.id === selectedId) || rec.candidates[0];
            const selectedHours = rosterItem?.hours || Math.max(1, Number(edit.hours ?? rec.hours));
            if (!selected) return null;

            return (
              <div className="sched-staff-row" key={rec.id}>
                <div className="sched-staff-avatar">{selected.initials}</div>
                <div className="sched-staff-main">
                  <div className="sched-staff-top">
                    <div>
                      <div className="sched-role">
                        {rec.custom ? rec.label : rec.role} - {selectedHours}h
                        {edit.resourceId && <span className="sched-manual-tag">Edited</span>}
                      </div>
                      <div className="sched-name">{selected.name}</div>
                    </div>
                    <div className={`sched-score ${selected.status || (selected.fitScore >= 80 ? "green" : selected.fitScore >= 65 ? "yellow" : "red")}`}>
                      {selected.fitScore}
                    </div>
                  </div>
                  <div className="sched-fit-meter"><span style={{ width: `${selected.fitScore}%` }} /></div>
                  <div className="sched-staff-facts">
                    <span>{selected.projectedUtilization}% projected util.</span>
                    <span>{selected.weeklyLoad}h / week</span>
                    {selected.overCapacityPct > 0 ? (
                      <span className="over">{selected.overCapacityPct}% over cap.</span>
                    ) : (
                      <span>{selected.availableAfterProject}h slack</span>
                    )}
                    <span>{selected.marginPct}% GM</span>
                  </div>
                  <div className="sched-reasons">
                    {(selected.reasons || []).map((reason, index) => <span key={`${reason}-${index}`}>{reason}</span>)}
                    {(selected.warnings || []).map((warning, index) => <span className="warn" key={`${warning}-${index}`}>{warning}</span>)}
                  </div>
                  <div className="sched-staff-controls">
                    {rec.custom && (
                      <label>
                        <span>Role</span>
                        <select className="sched-select-inline" value={rec.role} onChange={(e) => onUpdateCustomSlot(rec.id, { role: e.target.value })}>
                          {Object.keys(SCHED_ROLE_META).map((role) => <option key={role} value={role}>{role}</option>)}
                        </select>
                      </label>
                    )}
                    <label>
                      <span>Staff</span>
                      <select className="sched-select-inline" value={selected.id} onChange={(e) => onStaffEdit(rec.id, { resourceId: e.target.value })}>
                        {rec.candidates.slice(0, 8).map((candidate) => (
                          <option key={candidate.id} value={candidate.id}>
                            {candidate.name} - {candidate.fitScore}% - {candidate.overCapacityPct > 0 ? `${candidate.overCapacityPct}% over` : `${candidate.availableAfterProject}h slack`}
                          </option>
                        ))}
                      </select>
                    </label>
                    <label className="hours">
                      <span>Hours</span>
                      <input
                        className="sched-select-inline sched-hour-input"
                        type="number"
                        min="1"
                        value={selectedHours}
                        onChange={(e) => onStaffEdit(rec.id, { hours: Number(e.target.value) })}
                      />
                    </label>
                    <button className="sched-remove-btn" onClick={() => onRemoveStaff(rec.id, rec.custom)}>Remove</button>
                  </div>
                </div>
              </div>
            );
          })}
        </div>

        <div className="sched-staff-actions">
          <button className="filter-chip" onClick={onAddSpecialist}>Add specialist</button>
          {hasRemovedBase && <button className="filter-chip" onClick={onRestoreBaseStaff}>Restore default roles</button>}
        </div>

        <button className="btn sched-wide-btn" onClick={onGenerate} disabled={!roster.length}>
          Approve team and generate onboarding
        </button>
      </div>

      <div className={`card sched-onboarding-result ${generated ? "ready" : ""}`}>
        <div className="k">Client Onboarding</div>
        <h4>{generated ? "Onboarding flow generated" : "Ready after approval"}</h4>
        <div className="sched-onboarding-steps">
          {[
            "Planning questionnaire",
            "Points of contact",
            "Document request list",
            "Audit timeline",
            "Submission deadline",
            "Reminder cadence",
          ].map((step, index) => (
            <div key={step} className={generated ? "done" : ""}>
              <span>{String(index + 1).padStart(2, "0")}</span>
              {step}
            </div>
          ))}
        </div>
        {generated && (
          <div className="sched-result-actions">
            <a className="filter-chip on" href={onboardingHref} target="_blank">Open onboarding</a>
            <button className="filter-chip" onClick={onOpenPlanner}>View schedule</button>
          </div>
        )}
      </div>
    </div>
  );
}

function ScheduleCapacityPlanner({ form, estimate, roster, backlog, allocations, planMetrics, onBack }) {
  const rows = Array.from(new Map(roster.map((item) => [item.resource.id, item.resource])).values());
  const riskLabel = planMetrics.severeConflicts.length ? "Escalate" : planMetrics.conflicts.length ? "Watch" : "Clear";

  return (
    <div className="sched-planner-wrap">
      <div className="section-head">
        <div>
          <h3>{form.orgName}</h3>
          <div className="sh-sub">{form.service} - {estimate.weeklyDemand}h weekly demand against selected team capacity</div>
        </div>
        <div className="filters">
          <button className="filter-chip" onClick={onBack}>Edit intake</button>
          <button className="filter-chip on">Auto recommended</button>
        </div>
      </div>

      <div className="sched-analysis-grid">
        <div className="card">
          <span>Confidence</span>
          <strong>{planMetrics.confidence}%</strong>
        </div>
        <div className="card">
          <span>Staffed hours</span>
          <strong>{planMetrics.plannedHours}h</strong>
        </div>
        <div className="card">
          <span>Projected margin</span>
          <strong>{Math.round(planMetrics.margin)}%</strong>
        </div>
        <div className="card">
          <span>Capacity risk</span>
          <strong>{riskLabel}</strong>
        </div>
      </div>

      <div className="sched-planner-shell">
        <div className="sched-calendar card">
          <div className="sched-calendar-head">
            <div>Team</div>
            {SCHED_DAYS.map((day) => <div key={day}>{day}</div>)}
          </div>
          {rows.map((resource) => (
            <div className="sched-calendar-row" key={resource.id}>
              <div className="sched-resource-cell">
                <div className="sched-staff-avatar">{resource.initials}</div>
                <div>
                  <div className="sched-name">{resource.name}</div>
                  <div className="sched-role">{resource.role} - {resource.currentUtilization}% now / {resource.utilization}% planned</div>
                  <div className="sched-cap-note">{resource.capacityNote}</div>
                </div>
              </div>
              {SCHED_DAYS.map((day, index) => {
                const dayAllocs = allocations.filter((alloc) => alloc.resourceId === resource.id && alloc.dayIndex === index);
                const existing = resource.calendar[index] || 0;
                const added = dayAllocs.reduce((sum, alloc) => sum + alloc.scheduledHours, 0);
                const total = existing + added;
                const status = total > 8 ? "over" : total >= 7 ? "full" : "open";
                return (
                  <div className={`sched-day-cell ${status}`} key={day}>
                    <div className="sched-day-load">
                      <span style={{ width: `${Math.min(100, (total / 8) * 100)}%` }} />
                    </div>
                    <div className="sched-day-hours">{total}h</div>
                    {dayAllocs.map((alloc) => (
                      <div className="sched-alloc" key={alloc.id}>
                        <span>{alloc.phase}</span>
                        {alloc.scheduledHours}h
                      </div>
                    ))}
                  </div>
                );
              })}
            </div>
          ))}
        </div>

        <div className="card sched-backlog">
          <div className="k">Generated Backlog</div>
          <h4>{backlog.length} planning tasks</h4>
          <div className="sched-backlog-list">
            {backlog.map((task) => (
              <div className="sched-task-card" key={task.id}>
                <div className="sched-task-title">{task.title}</div>
                <div className="sched-task-meta">
                  <span>{task.role}</span>
                  <span>{task.hours}h</span>
                  <span>{task.phase}</span>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

function ScheduleStaffingBoard({ form, roster, backlog, allocations, planMetrics, generated, onGenerate }) {
  const onboardingHref = schedOnboardingHref(form);

  return (
    <div className="sched-board-grid">
      <div className="card">
        <div className="k">Proposed Team</div>
        <h4>{roster.length} staff assigned</h4>
        <div className="sched-board-summary">
          <div><span>Confidence</span>{planMetrics.confidence}%</div>
          <div><span>Margin</span>{Math.round(planMetrics.margin)}%</div>
          <div><span>Coverage</span>{planMetrics.coverage}%</div>
        </div>
        <div className="sched-staff-list">
          {roster.map((item) => (
            <div className="team-row" key={item.id}>
              <div className="av">{item.resource.initials}</div>
              <div>
                <div className="nm">{item.resource.name}</div>
                <div className="rl">{item.role} - {item.hours} budgeted hours</div>
              </div>
              <div style={{ flex: 1 }} />
              <div className="hr">{item.resource.fitScore}%</div>
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <div className="k">Assignments</div>
        <h4>{form.service} work plan</h4>
        <div className="sched-board-tasks">
          {allocations.map((task) => (
            <div className="sched-task-card" key={task.id}>
              <div className="sched-task-title">{task.title}</div>
              <div className="sched-task-meta">
                <span>{task.initials}</span>
                <span>{SCHED_DAYS[task.dayIndex]}</span>
                <span>{task.scheduledHours}h</span>
              </div>
            </div>
          ))}
        </div>
      </div>

      <div className="card">
        <div className="k">Onboarding Packet</div>
        <h4>{generated ? "Generated" : "Draft"}</h4>
        <div className="sched-packet">
          <div><span>Client</span>{form.orgName}</div>
          <div><span>Contact</span>{form.contactName}</div>
          <div><span>FYE</span>{form.fye}</div>
          <div><span>Target</span>{form.deadline}</div>
          <div><span>Projected margin</span>{Math.round(planMetrics.margin)}%</div>
          <div><span>Plan confidence</span>{planMetrics.confidence}%</div>
          <div><span>Document request</span>{backlog.length + 5} sections</div>
          <div><span>Reminder cadence</span>3 messages</div>
        </div>
        {!generated ? (
          <button className="btn sched-wide-btn" onClick={onGenerate}>Generate onboarding</button>
        ) : (
          <a className="btn sched-wide-btn" href={onboardingHref} target="_blank">Open onboarding</a>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { ScheduleWorkspace });
