const { useEffect, useMemo, useRef, useState } = React;
const APP_NAME = "Nex Central";
const CHAT_UPLOAD_LIMIT_BYTES = 5 * 1024 * 1024;
const CHAT_ALLOWED_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "webp", "pdf", "doc", "docx", "xls", "xlsx", "csv"];
const chatEmojiOptions = ["👍", "🔥", "🎉", "🏡", "✅", "🙏", "💯", "🚀", "👏", "❤️"];

const API = {
  async request(path, options = {}) {
    const { retries = 1, retryDelay = 450, ...fetchOptions } = options;
    let lastError;

    for (let attempt = 0; attempt <= retries; attempt += 1) {
      try {
        const response = await fetch(path, {
          headers: { "Content-Type": "application/json", ...(fetchOptions.headers || {}) },
          credentials: "same-origin",
          ...fetchOptions
        });
        if (!response.ok) {
          const error = await response.json().catch(() => ({ error: response.statusText }));
          const requestError = new Error(error.error || "Request failed.");
          requestError.status = response.status;
          throw requestError;
        }
        return response.json();
      } catch (error) {
        lastError = error;
        if (error.status || attempt === retries) throw error;
        await new Promise((resolve) => setTimeout(resolve, retryDelay * (attempt + 1)));
      }
    }
    throw lastError;
  }
};

function friendlyNetworkError(error, fallback = "Load failed. Please refresh and try again.") {
  const message = String(error?.message || "");
  if (/load failed|failed to fetch|networkerror|network request failed/i.test(message)) {
    return "Nex Central could not finish loading. Please refresh the page and try again.";
  }
  return message || fallback;
}

const marketingTypes = [
  "Just Listed",
  "Just Sold",
  "Open House",
  "For Rent",
  "Price Improvement",
  "Buyer Education",
  "Seller Lead Generation",
  "Recruiting",
  "Agent Bio",
  "Agent Award / Production Recognition",
  "New Agent Announcement",
  "Quick Video / Reel",
  "Postcard",
  "Business Card",
  "Vendor / Partner"
];

const toneOptions = [
  "Professional",
  "Luxury",
  "Hype / high-energy",
  "Short and punchy",
  "Warm and consumer-friendly",
  "Agent recruiting style"
];

const platformOptions = [
  "Multi-platform",
  "Facebook",
  "Instagram",
  "LinkedIn",
  "Instagram Story",
  "Reel Script",
  "Quick Video",
  "Flyer",
  "Postcard",
  "Business Card",
  "Email",
  "SMS"
];

const businessCardStyles = ["Signature Red", "Executive Black", "Clean White", "Luxury Gold"];
const headshotShapes = ["Square", "Circle", "Arch"];
const headshotFitOptions = ["Cover", "Contain"];
const defaultLogoBlackUrl = "/assets/nex-logo-black.png";
const defaultLogoWhiteUrl = "/assets/nex-logo-white.png";

const studioTemplateDefaults = [
  { id: "just-listed", name: "Just Listed", category: "Listing", headline: "JUST LISTED", badge: "New Listing", accent: "#e30613", style: "classic" },
  { id: "luxury-listing", name: "Luxury Listing", category: "Listing", headline: "LUXURY LISTING", badge: "Private Offering", accent: "#c9a24a", style: "luxury" },
  { id: "modern-open-house", name: "Modern Open House", category: "Event", headline: "OPEN HOUSE", badge: "Tour This Home", accent: "#e30613", style: "magazine" },
  { id: "for-rent", name: "For Rent", category: "Rental", headline: "FOR RENT", badge: "Available Now", accent: "#e30613", style: "classic" },
  { id: "rental-luxe", name: "Rental Spotlight", category: "Rental", headline: "RENTAL SPOTLIGHT", badge: "Lease Today", accent: "#c9a24a", style: "luxury" },
  { id: "just-sold", name: "Just Sold", category: "Listing", headline: "JUST SOLD", badge: "Sold", accent: "#050505", style: "classic" },
  { id: "price-improvement", name: "Price Improvement", category: "Listing", headline: "PRICE IMPROVEMENT", badge: "New Price", accent: "#c9a24a", style: "magazine" },
  { id: "coming-soon", name: "Coming Soon", category: "Listing", headline: "COMING SOON", badge: "First Look", accent: "#050505", style: "magazine" },
  { id: "under-contract", name: "Under Contract", category: "Listing", headline: "UNDER CONTRACT", badge: "Buyer Secured", accent: "#e30613", style: "classic" },
  { id: "buyer-need", name: "Buyer Need", category: "Lead Generation", headline: "BUYER NEED", badge: "Have A Match?", accent: "#050505", style: "clean" },
  { id: "seller-lead-ad", name: "Seller Lead Ad", category: "Lead Generation", headline: "SELL WITH CONFIDENCE", badge: "Home Value", accent: "#e30613", style: "social" },
  { id: "home-value-offer", name: "Home Value Offer", category: "Lead Generation", headline: "WHAT IS YOUR HOME WORTH?", badge: "Free Valuation", accent: "#c9a24a", style: "social" },
  { id: "fsbo-outreach", name: "FSBO Outreach", category: "Lead Generation", headline: "SELLING SOLO?", badge: "Agent Support", accent: "#e30613", style: "clean" },
  { id: "expired-listing", name: "Expired Listing", category: "Lead Generation", headline: "YOUR HOME DESERVES A NEW STRATEGY", badge: "Fresh Plan", accent: "#050505", style: "luxury" },
  { id: "buyer-education", name: "Buyer Education", category: "Education", headline: "BUYER GUIDE", badge: "Smart Moves", accent: "#050505", style: "clean" },
  { id: "seller-guide", name: "Seller Guide", category: "Education", headline: "SELLER GUIDE", badge: "Market Ready", accent: "#050505", style: "clean" },
  { id: "market-update", name: "Market Update", category: "Education", headline: "MARKET UPDATE", badge: "Local Insight", accent: "#c9a24a", style: "clean" },
  { id: "testimonial", name: "Testimonial", category: "Social", headline: "CLIENT SUCCESS STORY", badge: "Five-Star Experience", accent: "#c9a24a", style: "clean" },
  { id: "holiday-social", name: "Holiday / Social Graphic", category: "Social", headline: "FROM NEX REALTY", badge: "Community Moment", accent: "#e30613", style: "social" },
  { id: "new-agent", name: "New Agent Announcement", category: "Agent Marketing", headline: "WELCOME TO NEX REALTY", badge: "New Agent", accent: "#050505", style: "social" },
  { id: "agent-award", name: "Agent Award / Production Recognition", category: "Agent Marketing", headline: "AGENT RECOGNITION", badge: "Top Performance", accent: "#c9a24a", style: "luxury" },
  { id: "agent-spotlight", name: "Agent Spotlight", category: "Agent Marketing", headline: "AGENT SPOTLIGHT", badge: "Nex Realty", accent: "#e30613", style: "magazine" },
  { id: "referral-partner", name: "Referral Partner", category: "Agent Marketing", headline: "TRUSTED PARTNER", badge: "Referral Ready", accent: "#e30613", style: "clean" },
  { id: "recruiting", name: "Recruiting / Join Nex Realty", category: "Recruiting", headline: "JOIN NEX REALTY", badge: "Grow Faster", accent: "#e30613", style: "social" },
  { id: "recruiting-post", name: "Recruiting Post", category: "Recruiting", headline: "READY FOR A BETTER BROKERAGE?", badge: "Join Nex Realty", accent: "#e30613", style: "luxury" },
  { id: "cloud-brokerage", name: "Cloud Brokerage Recruiting", category: "Recruiting", headline: "BUILD WITHOUT LIMITS", badge: "Agent Platform", accent: "#c9a24a", style: "luxury" },
  { id: "business-card", name: "Business Card", category: "Agent Marketing", headline: "NEX REALTY", badge: "Agent Card", accent: "#e30613", style: "classic" },
  { id: "email-header", name: "Email Header", category: "Email", headline: "NEX REALTY UPDATE", badge: "Email Campaign", accent: "#050505", style: "clean" },
  { id: "story-reel-cover", name: "Story/Reel Cover", category: "Social", headline: "NEW LISTING TOUR", badge: "Watch Now", accent: "#e30613", style: "social" }
];

const marketingStudioToolTabs = [
  ["Templates", "LayoutTemplate"],
  ["Uploads", "UploadCloud"],
  ["Text", "Type"],
  ["Elements", "Shapes"],
  ["QR Code", "QrCode"],
  ["Agent Info", "Contact"],
  ["Property Info", "Home"],
  ["Edit", "SlidersHorizontal"],
  ["Brand Kit", "Palette"],
  ["Export", "Send"],
  ["Saved Projects", "FolderOpen"]
];

const leadCtaBlocks = [
  "Schedule a showing",
  "Request home value",
  "Download buyer guide",
  "Get pre-approved",
  "Scan for more info",
  "Contact agent",
  "Join Nex Realty",
  "Book discovery call"
];

const brandTaglines = [
  "Modern brokerage. Bold marketing.",
  "Cloud-based real estate brokerage.",
  "Built for high-performance agents.",
  "Luxury presentation. Local expertise.",
  "Smarter systems. Stronger results."
];

const brandFontPairings = [
  "Bold Sans / Clean Sans",
  "Editorial Serif / Modern Sans",
  "Condensed Display / Neutral Sans"
];

const copyQuickActions = [
  ["Make it more luxury", "Rewrite this marketing content in a luxury real estate tone."],
  ["Make it more hype", "Rewrite this with high-energy, punchy real estate marketing language."],
  ["Make it shorter", "Make this shorter, sharper, and easier to scan."],
  ["Add emojis", "Add tasteful emojis for social media while keeping it professional."],
  ["Investor-focused", "Rewrite this for real estate investors and ROI-minded buyers."],
  ["Buyer-focused", "Rewrite this for buyers and lifestyle benefits."],
  ["Seller-focused", "Rewrite this for sellers and lead generation."],
  ["Facebook", "Rewrite this as a Facebook post."],
  ["Instagram", "Rewrite this as an Instagram caption with hashtags."],
  ["Email", "Rewrite this as an email blast."],
  ["SMS", "Rewrite this as a short SMS text."]
];

const flyerLayoutOptions = [
  { id: "classic", name: "Classic Listing", icon: "Rows3" },
  { id: "luxury", name: "Luxury Editorial", icon: "Gem" },
  { id: "magazine", name: "Magazine Cover", icon: "BookOpen" },
  { id: "social", name: "Social Ad", icon: "Megaphone" },
  { id: "clean", name: "Clean Guide", icon: "PanelTop" }
];

const flyerFinishOptions = [
  { id: "red-signature", name: "Red Signature" },
  { id: "black-luxury", name: "Black Luxury" },
  { id: "gold-accent", name: "Gold Accent" },
  { id: "clean-white", name: "Clean White" }
];

const photoTreatmentOptions = [
  { id: "full-bleed", name: "Full Bleed" },
  { id: "dramatic-overlay", name: "Dramatic Overlay" },
  { id: "soft-contrast", name: "Soft Contrast" },
  { id: "gallery-strip", name: "Gallery Strip" }
];

const creativeFormatOptions = [
  { id: "letter-flyer", name: "Letter Flyer", icon: "FileText", width: 8.5, height: 11, exportLabel: "8.5 x 11 in" },
  { id: "open-house-flyer", name: "Open House Flyer", icon: "CalendarDays", width: 8.5, height: 11, exportLabel: "8.5 x 11 in" },
  { id: "facebook-post", name: "Facebook Post", icon: "Share2", width: 1200, height: 630, exportLabel: "1200 x 630" },
  { id: "instagram-square", name: "IG Post", icon: "Camera", width: 1080, height: 1080, exportLabel: "1080 x 1080" },
  { id: "instagram-story", name: "IG Story", icon: "Smartphone", width: 1080, height: 1920, exportLabel: "1080 x 1920" },
  { id: "instagram-reel", name: "Reel Cover", icon: "Clapperboard", width: 1080, height: 1920, exportLabel: "1080 x 1920" },
  { id: "linkedin-post", name: "LinkedIn Post", icon: "Linkedin", width: 1200, height: 1200, exportLabel: "1200 x 1200" },
  { id: "youtube-thumbnail", name: "YouTube Thumbnail", icon: "Youtube", width: 1280, height: 720, exportLabel: "1280 x 720" },
  { id: "postcard", name: "Postcard 6 x 4", icon: "Mail", width: 6, height: 4, exportLabel: "6 x 4 in" },
  { id: "postcard-large", name: "Postcard 9 x 6", icon: "Mail", width: 9, height: 6, exportLabel: "9 x 6 in" },
  { id: "horizontal-video", name: "Video Wide", icon: "MonitorPlay", width: 1920, height: 1080, exportLabel: "1920 x 1080" },
  { id: "business-card", name: "Business Card", icon: "Contact", width: 3.5, height: 2, exportLabel: "3.5 x 2 in" },
  { id: "email-header", name: "Email Header", icon: "Mail", width: 1200, height: 400, exportLabel: "1200 x 400" },
  { id: "open-house-sign", name: "Open House Sign", icon: "Signpost", width: 18, height: 24, exportLabel: "18 x 24 in" },
  { id: "yard-sign", name: "Yard Sign", icon: "Signpost", width: 24, height: 18, exportLabel: "24 x 18 in" },
  { id: "qr-code-flyer", name: "QR Code Flyer", icon: "QrCode", width: 8.5, height: 11, exportLabel: "8.5 x 11 in" }
];

const studioQuickStarts = [
  { title: "Just Listed Flyer", description: "Print-ready listing flyer", icon: "FileText", templateId: "just-listed", formatId: "letter-flyer", nextTool: "Property Info" },
  { title: "Business Card", description: "3.5 x 2 agent card", icon: "Contact", templateId: "business-card", formatId: "business-card", nextTool: "Agent Info" },
  { title: "Instagram Post", description: "1080 x 1080 social graphic", icon: "Camera", templateId: "just-listed", formatId: "instagram-square", nextTool: "Text" },
  { title: "Reel / Story Cover", description: "Vertical 1080 x 1920", icon: "Clapperboard", templateId: "story-reel-cover", formatId: "instagram-reel", nextTool: "Text" },
  { title: "Facebook Post", description: "Wide feed graphic", icon: "Share2", templateId: "seller-lead-ad", formatId: "facebook-post", nextTool: "Text" },
  { title: "Open House", description: "Flyer or social promo", icon: "CalendarDays", templateId: "modern-open-house", formatId: "letter-flyer", nextTool: "Property Info" }
];

const videoOrientationOptions = [
  { id: "vertical", name: "Vertical Reel", icon: "Smartphone", width: 1080, height: 1920, label: "9:16 reel/story" },
  { id: "horizontal", name: "Horizontal Video", icon: "MonitorPlay", width: 1920, height: 1080, label: "16:9 wide video" }
];

const defaultFields = {
  agentName: "",
  agentPhone: "",
  agentEmail: "",
  propertyAddress: "",
  city: "",
  state: "",
  zip: "",
  price: "",
  bedrooms: "",
  bathrooms: "",
  garageSpaces: "",
  squareFootage: "",
  lotSize: "",
  propertyType: "",
  status: "For sale",
  openHouseDateTime: "",
  rentalAvailableDate: "",
  propertyHighlights: "",
  mlsRemarks: "",
  cta: "Schedule Your Tour Today",
  marketingType: "Just Listed",
  tone: "Professional",
  platform: "Multi-platform"
};

const localDefaultBrand = {
  companyName: "Nex Realty",
  primaryColor: "#e30613",
  black: "#050505",
  charcoal: "#24272d",
  white: "#ffffff",
  gold: "#c9a24a",
  logoDataUrl: "",
  logoBlackUrl: defaultLogoBlackUrl,
  logoWhiteUrl: defaultLogoWhiteUrl,
  logoBlackDataUrl: "",
  logoWhiteDataUrl: "",
  defaultDisclaimer:
    "Information deemed reliable but not guaranteed. Equal Housing Opportunity. Advertised by Nex Realty. Agent is responsible for verifying all listing and advertising information prior to publication.",
  recruitingDisclaimer:
    "Commission plans, fees, caps, and incentives are subject to change and may vary by agreement. Contact Nex Realty for current details."
};

const defaultFlyerCopy = {
  headline: "JUST LISTED",
  badge: "New Listing",
  body: "Premium Nex Realty marketing copy will appear here after generation.",
  cta: "Schedule Your Tour Today",
  footer: localDefaultBrand.defaultDisclaimer,
  layout: "classic",
  finish: "red-signature",
  photoTreatment: "dramatic-overlay",
  canvasFormat: "letter-flyer",
  videoOrientation: "vertical",
  fontScale: 100,
  fontWeight: 900,
  textAlign: "left",
  showPrice: true,
  showStats: true,
  showAgent: true,
  showDisclaimer: true,
  showQr: false,
  qrUrl: "",
  agentTitle: "Real Estate Professional",
  agentSocial: "@nexrealty",
  businessCardSide: "front",
  headlineOffsetX: 0,
  headlineOffsetY: 0,
  headlineWidth: 360,
  heroHeight: 274,
  addressScale: 100,
  priceScale: 100,
  statsScale: 100,
  bodyScale: 100,
  agentScale: 100,
  disclaimerScale: 100,
  qrScale: 100,
  backgroundColor: "#ffffff",
  backgroundImage: "",
  backgroundImageName: "",
  backgroundOpacity: 12,
  videoMotion: "Subtle zoom",
  videoDuration: 8,
  heroImageFit: "Cover",
  heroImagePosition: "Center"
};

const defaultBusinessCardCopy = {
  style: "Signature Red",
  displayName: "",
  email: "",
  phone: "",
  title: "Real Estate Professional",
  licenseLine: "Advertised by Nex Realty",
  website: "nexrealty.com",
  social: "@nexrealty",
  tagline: "Modern brokerage. Bold marketing. Smarter moves.",
  headshotShape: "Square",
  headshotFit: "Cover",
  headshotZoom: 100,
  headshotX: 0,
  headshotY: 0
};

function normalizeSvgAttribute(key) {
  return key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
}

function Icon({ name, size = 18, className = "" }) {
  const node = window.lucide && window.lucide.icons && window.lucide.icons[name];
  if (!node) return <span className={className} aria-hidden="true" />;

  function render(iconNode, index = 0) {
    const [tag, attrs = {}, children = []] = iconNode;
    const normalized = Object.entries(attrs).reduce((next, [key, value]) => {
      next[normalizeSvgAttribute(key)] = value;
      return next;
    }, {});
    if (tag === "svg") {
      normalized.width = size;
      normalized.height = size;
      normalized.className = className;
      normalized["aria-hidden"] = "true";
    }
    return React.createElement(
      tag,
      { ...normalized, key: index },
      children.map((child, childIndex) => render(child, childIndex))
    );
  }

  return render(node);
}

function classNames(...values) {
  return values.filter(Boolean).join(" ");
}

function isAdminRole(role) {
  return role === "Admin" || role === "Super Admin";
}

function money(value) {
  if (!value) return "";
  const number = Number(String(value).replace(/[^0-9.]/g, ""));
  if (!Number.isFinite(number) || number <= 0) return value;
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0
  }).format(number);
}

function titleCaseDate(value) {
  if (!value) return "";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleString([], {
    weekday: "short",
    month: "short",
    day: "numeric",
    hour: "numeric",
    minute: "2-digit"
  });
}

function slugify(value) {
  return String(value || "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)/g, "");
}

function mergeTemplates(templateList = []) {
  const byId = new Map(studioTemplateDefaults.map((template) => [template.id, template]));
  for (const template of templateList) {
    if (!template || !template.id) continue;
    byId.set(template.id, { ...(byId.get(template.id) || {}), ...template });
  }
  return Array.from(byId.values());
}

function makeStockVisual({ title, category, colorA, colorB, accent, pattern }) {
  const roof = pattern === "exterior" || pattern === "open-house";
  const skyline = pattern === "skyline" || pattern === "recruiting";
  const interior = pattern === "interior" || pattern === "kitchen";
  const award = pattern === "award";
  const svg = `
    <svg width="1200" height="820" viewBox="0 0 1200 820" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0" stop-color="${colorA}"/>
          <stop offset="1" stop-color="${colorB}"/>
        </linearGradient>
        <linearGradient id="shine" x1="0" y1="0" x2="1" y2="0">
          <stop offset="0" stop-color="#fff" stop-opacity=".18"/>
          <stop offset=".55" stop-color="#fff" stop-opacity=".04"/>
          <stop offset="1" stop-color="#fff" stop-opacity=".22"/>
        </linearGradient>
      </defs>
      <rect width="1200" height="820" fill="url(#bg)"/>
      <rect x="70" y="64" width="1060" height="690" fill="none" stroke="#fff" stroke-opacity=".18" stroke-width="2"/>
      <circle cx="980" cy="150" r="92" fill="${accent}" opacity=".32"/>
      <circle cx="160" cy="700" r="150" fill="#fff" opacity=".08"/>
      ${skyline ? `
        <g fill="#111318" opacity=".62">
          <rect x="126" y="320" width="126" height="330"/>
          <rect x="280" y="244" width="152" height="406"/>
          <rect x="468" y="350" width="118" height="300"/>
          <rect x="622" y="205" width="170" height="445"/>
          <rect x="830" y="290" width="142" height="360"/>
        </g>
        <g fill="#fff" opacity=".32">
          ${Array.from({ length: 38 }, (_, index) => `<rect x="${150 + (index % 10) * 78}" y="${350 + Math.floor(index / 10) * 74}" width="28" height="20"/>`).join("")}
        </g>
      ` : ""}
      ${roof ? `
        <path d="M185 520 L600 190 L1015 520 Z" fill="#ffffff" opacity=".2"/>
        <path d="M270 520 L600 258 L930 520 Z" fill="#111318" opacity=".42"/>
        <rect x="320" y="520" width="560" height="170" fill="#ffffff" opacity=".16"/>
        <rect x="550" y="570" width="104" height="120" fill="${accent}" opacity=".72"/>
        <rect x="390" y="560" width="92" height="72" fill="#fff" opacity=".32"/>
        <rect x="720" y="560" width="92" height="72" fill="#fff" opacity=".32"/>
      ` : ""}
      ${interior ? `
        <rect x="130" y="150" width="940" height="370" fill="#ffffff" opacity=".16"/>
        <rect x="190" y="205" width="260" height="240" fill="#111318" opacity=".34"/>
        <rect x="490" y="205" width="260" height="240" fill="#ffffff" opacity=".22"/>
        <rect x="790" y="205" width="220" height="240" fill="#111318" opacity=".24"/>
        <rect x="250" y="590" width="700" height="72" rx="36" fill="#111318" opacity=".48"/>
        <rect x="354" y="510" width="492" height="114" rx="24" fill="${accent}" opacity=".55"/>
      ` : ""}
      ${award ? `
        <path d="M520 180 H680 L650 480 H550 Z" fill="${accent}" opacity=".72"/>
        <path d="M482 210 C338 226 352 438 536 392" fill="none" stroke="#fff" stroke-opacity=".32" stroke-width="46"/>
        <path d="M718 210 C862 226 848 438 664 392" fill="none" stroke="#fff" stroke-opacity=".32" stroke-width="46"/>
        <rect x="500" y="520" width="200" height="40" fill="#111318" opacity=".62"/>
        <rect x="430" y="560" width="340" height="80" fill="#111318" opacity=".42"/>
      ` : ""}
      <rect x="0" y="0" width="1200" height="820" fill="url(#shine)"/>
      <path d="M80 108 H360" stroke="${accent}" stroke-width="10"/>
      <text x="80" y="702" fill="#fff" fill-opacity=".86" font-family="Arial, sans-serif" font-size="52" font-weight="900" letter-spacing="4">${title.toUpperCase()}</text>
      <text x="82" y="742" fill="#fff" fill-opacity=".62" font-family="Arial, sans-serif" font-size="22" font-weight="700" letter-spacing="5">${category.toUpperCase()}</text>
    </svg>`;
  return `data:image/svg+xml,${encodeURIComponent(svg)}`;
}

const stockPhotoPresets = [
  { id: "luxury-exterior", title: "Luxury Exterior", category: "Luxury", pattern: "exterior", colorA: "#111318", colorB: "#5d6470", accent: "#c9a24a" },
  { id: "modern-interior", title: "Modern Interior", category: "Luxury", pattern: "interior", colorA: "#272c35", colorB: "#b8bdc7", accent: "#e30613" },
  { id: "designer-kitchen", title: "Designer Kitchen", category: "Listing", pattern: "kitchen", colorA: "#111318", colorB: "#ece7dd", accent: "#c9a24a" },
  { id: "open-house", title: "Open House", category: "Open House", pattern: "open-house", colorA: "#17191d", colorB: "#7c1d24", accent: "#e30613" },
  { id: "city-skyline", title: "City Skyline", category: "Listing", pattern: "skyline", colorA: "#08090b", colorB: "#343a46", accent: "#e30613" },
  { id: "rental-lifestyle", title: "Rental Lifestyle", category: "Rental", pattern: "interior", colorA: "#1f2933", colorB: "#6c7887", accent: "#e30613" },
  { id: "seller-consult", title: "Seller Consult", category: "Seller", pattern: "kitchen", colorA: "#111318", colorB: "#755f35", accent: "#c9a24a" },
  { id: "buyer-guide", title: "Buyer Guide", category: "Education", pattern: "interior", colorA: "#f4f5f7", colorB: "#9aa2af", accent: "#e30613" },
  { id: "agent-platform", title: "Agent Platform", category: "Recruiting", pattern: "recruiting", colorA: "#070708", colorB: "#303744", accent: "#e30613" },
  { id: "award-night", title: "Award Night", category: "Recognition", pattern: "award", colorA: "#111318", colorB: "#5a4521", accent: "#c9a24a" }
].map((photo) => ({ ...photo, dataUrl: makeStockVisual(photo) }));

function getCreativeFormat(id) {
  return creativeFormatOptions.find((option) => option.id === id) || creativeFormatOptions[0];
}

function getRecommendedFormatForTemplate(template = {}) {
  const id = String(template.id || "").toLowerCase();
  const name = String(template.name || "").toLowerCase();
  const category = String(template.category || "").toLowerCase();

  if (id.includes("business-card") || name.includes("business card")) return "business-card";
  if (id.includes("email-header") || name.includes("email header") || category === "email") return "email-header";
  if (id.includes("youtube") || name.includes("youtube")) return "youtube-thumbnail";
  if (id.includes("linkedin") || name.includes("linkedin")) return "linkedin-post";
  if (id.includes("story") || name.includes("story")) return "instagram-story";
  if (id.includes("reel") || name.includes("reel")) return "instagram-reel";
  if (id.includes("open-house") || name.includes("open house")) return "open-house-flyer";
  if (category === "social" || id.includes("testimonial") || id.includes("holiday")) return "instagram-square";
  if (id.includes("qr")) return "qr-code-flyer";
  if (id.includes("postcard-large") || name.includes("9 x 6")) return "postcard-large";
  if (id.includes("postcard") || name.includes("postcard")) return "postcard";
  if (id.includes("yard") || name.includes("yard")) return "yard-sign";
  if (id.includes("sign") || name.includes("sign")) return "open-house-sign";
  return template.canvasFormat || template.format || "letter-flyer";
}

function getFormatDefaults(formatId) {
  if (formatId === "business-card") {
    return {
      showPrice: false,
      showStats: false,
      showDisclaimer: false,
      showQr: false,
      heroImageFit: "Cover",
      textAlign: "left"
    };
  }
  if (formatId === "email-header") {
    return {
      showPrice: false,
      showStats: false,
      showDisclaimer: false,
      showQr: false
    };
  }
  if (["instagram-square", "instagram-story", "instagram-reel", "facebook-post", "linkedin-post", "youtube-thumbnail", "postcard", "postcard-large", "horizontal-video", "yard-sign"].includes(formatId)) {
    return {
      showDisclaimer: false,
      showQr: false
    };
  }
  return {
    showPrice: true,
    showStats: true,
    showAgent: true,
    showDisclaimer: true
  };
}

function clampStudioNumber(value, min, max) {
  const number = Number(value);
  if (!Number.isFinite(number)) return min;
  return Math.min(Math.max(number, min), max);
}

function layerScaleStyle(flyerCopy, elementId) {
  const scale = clampStudioNumber(flyerCopy[`${elementId}Scale`] || 100, 50, 180) / 100;
  return { "--layer-scale": scale };
}

function getQrCodeUrl(value) {
  const target = value || "https://mynexrealty.com";
  return `https://quickchart.io/qr?text=${encodeURIComponent(target)}&size=320&margin=2`;
}

function sortStudioTemplates(templates = [], templateCategory = "All", query = "") {
  const priority = [
    "just-listed",
    "business-card",
    "modern-open-house",
    "story-reel-cover",
    "seller-lead-ad",
    "for-rent",
    "luxury-listing",
    "recruiting-post"
  ];
  if (templateCategory !== "All" || query) return templates;
  return [...templates].sort((a, b) => {
    const aIndex = priority.indexOf(a.id);
    const bIndex = priority.indexOf(b.id);
    if (aIndex !== -1 || bIndex !== -1) {
      return (aIndex === -1 ? 99 : aIndex) - (bIndex === -1 ? 99 : bIndex);
    }
    return String(a.name || "").localeCompare(String(b.name || ""));
  });
}

function getVideoOrientation(id) {
  return videoOrientationOptions.find((option) => option.id === id) || videoOrientationOptions[0];
}

function canvasImage(src) {
  return new Promise((resolve) => {
    if (!src) {
      resolve(null);
      return;
    }
    const image = new Image();
    image.crossOrigin = "anonymous";
    image.onload = () => resolve(image);
    image.onerror = () => resolve(null);
    image.src = src;
  });
}

function drawCoverImage(ctx, image, x, y, width, height, zoom = 1) {
  if (!image) return;
  const imageRatio = image.width / image.height;
  const boxRatio = width / height;
  let sourceWidth = image.width;
  let sourceHeight = image.height;
  if (imageRatio > boxRatio) sourceWidth = image.height * boxRatio;
  else sourceHeight = image.width / boxRatio;
  sourceWidth /= zoom;
  sourceHeight /= zoom;
  const sourceX = (image.width - sourceWidth) / 2;
  const sourceY = (image.height - sourceHeight) / 2;
  ctx.drawImage(image, sourceX, sourceY, sourceWidth, sourceHeight, x, y, width, height);
}

function drawContainImage(ctx, image, x, y, width, height) {
  if (!image) return;
  const ratio = Math.min(width / image.width, height / image.height);
  const drawWidth = image.width * ratio;
  const drawHeight = image.height * ratio;
  ctx.drawImage(image, x, y + (height - drawHeight) / 2, drawWidth, drawHeight);
}

function drawWrappedText(ctx, text, x, y, maxWidth, lineHeight, maxLines = 4) {
  const words = String(text || "").split(/\s+/).filter(Boolean);
  const lines = [];
  let line = "";
  words.forEach((word) => {
    const testLine = line ? `${line} ${word}` : word;
    if (ctx.measureText(testLine).width > maxWidth && line) {
      lines.push(line);
      line = word;
    } else {
      line = testLine;
    }
  });
  if (line) lines.push(line);
  lines.slice(0, maxLines).forEach((lineText, index) => {
    ctx.fillText(lineText, x, y + index * lineHeight);
  });
}

function downloadBlob(blob, fileName) {
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = fileName;
  link.click();
  setTimeout(() => URL.revokeObjectURL(url), 1200);
}

async function createQuickMarketingVideo({ brand, fields, flyerCopy, propertyPhotos, orientation }) {
  const spec = getVideoOrientation(orientation);
  const canvas = document.createElement("canvas");
  canvas.width = spec.width;
  canvas.height = spec.height;
  const ctx = canvas.getContext("2d");
  if (!ctx || !canvas.captureStream || !window.MediaRecorder) {
    throw new Error("Quick video export requires a browser with canvas video recording support.");
  }

  const imageSource = propertyPhotos[0]?.dataUrl || stockPhotoPresets[0]?.dataUrl;
  const heroImage = await canvasImage(imageSource);
  const logo = await canvasImage(brand.logoWhiteDataUrl || brand.logoWhiteUrl || defaultLogoWhiteUrl);
  const stream = canvas.captureStream(30);
  const mimeType = ["video/webm;codecs=vp9", "video/webm;codecs=vp8", "video/webm"].find((type) => MediaRecorder.isTypeSupported(type)) || "";
  const recorder = new MediaRecorder(stream, mimeType ? { mimeType } : undefined);
  const chunks = [];
  const duration = 5200;
  const isVertical = spec.height > spec.width;

  recorder.ondataavailable = (event) => {
    if (event.data && event.data.size) chunks.push(event.data);
  };

  function drawFrame(progress) {
    const width = canvas.width;
    const height = canvas.height;
    const red = brand.primaryColor || "#e30613";
    const gold = brand.gold || "#c9a24a";
    const headline = flyerCopy.headline || fields.marketingType || "Nex Realty";
    const badge = flyerCopy.badge || fields.status || "Featured";
    const address = fields.propertyAddress || "Your next move starts here";
    const price = money(fields.price) || "Contact for details";
    const cta = flyerCopy.cta || fields.cta || "Schedule Your Tour Today";
    const zoom = 1.03 + progress * 0.08;
    ctx.clearRect(0, 0, width, height);

    const background = ctx.createLinearGradient(0, 0, width, height);
    background.addColorStop(0, "#070708");
    background.addColorStop(0.58, "#17191d");
    background.addColorStop(1, red);
    ctx.fillStyle = background;
    ctx.fillRect(0, 0, width, height);

    const photoHeight = isVertical ? height * 0.58 : height;
    drawCoverImage(ctx, heroImage, 0, 0, width, photoHeight, zoom);
    const overlay = ctx.createLinearGradient(0, 0, isVertical ? 0 : width, height);
    overlay.addColorStop(0, "rgba(0,0,0,.25)");
    overlay.addColorStop(isVertical ? 0.58 : 0.42, "rgba(0,0,0,.58)");
    overlay.addColorStop(1, "rgba(0,0,0,.92)");
    ctx.fillStyle = overlay;
    ctx.fillRect(0, 0, width, height);

    const pad = isVertical ? 78 : 86;
    if (logo) drawContainImage(ctx, logo, pad, pad, isVertical ? 230 : 250, isVertical ? 92 : 100);
    else {
      ctx.fillStyle = "#ffffff";
      ctx.font = `900 ${isVertical ? 42 : 46}px Arial`;
      ctx.fillText("NEX REALTY", pad, pad + 44);
    }

    ctx.fillStyle = red;
    ctx.fillRect(pad, isVertical ? height * 0.62 : height * 0.54, isVertical ? 260 : 310, isVertical ? 54 : 58);
    ctx.fillStyle = "#ffffff";
    ctx.font = `900 ${isVertical ? 28 : 31}px Arial`;
    ctx.letterSpacing = "2px";
    ctx.fillText(String(badge).toUpperCase(), pad + 22, (isVertical ? height * 0.62 : height * 0.54) + (isVertical ? 36 : 39));

    ctx.fillStyle = "#ffffff";
    ctx.font = `900 ${isVertical ? 80 : 84}px Arial`;
    drawWrappedText(ctx, String(headline).toUpperCase(), pad, isVertical ? height * 0.71 : height * 0.67, width - pad * 2, isVertical ? 86 : 90, isVertical ? 4 : 3);

    ctx.fillStyle = gold;
    ctx.font = `900 ${isVertical ? 34 : 36}px Arial`;
    drawWrappedText(ctx, address, pad, isVertical ? height - 265 : height - 190, width - pad * 2, 42, 2);

    ctx.fillStyle = "#ffffff";
    ctx.font = `900 ${isVertical ? 42 : 44}px Arial`;
    ctx.fillText(price, pad, isVertical ? height - 178 : height - 116);

    ctx.fillStyle = red;
    ctx.fillRect(pad, height - (isVertical ? 120 : 74), isVertical ? width - pad * 2 : 520, isVertical ? 62 : 52);
    ctx.fillStyle = "#ffffff";
    ctx.font = `900 ${isVertical ? 27 : 24}px Arial`;
    ctx.fillText(String(cta).toUpperCase().slice(0, 42), pad + 24, height - (isVertical ? 80 : 40));
  }

  return new Promise((resolve, reject) => {
    recorder.onerror = () => reject(new Error("Could not render the quick video."));
    recorder.onstop = () => resolve(new Blob(chunks, { type: mimeType || "video/webm" }));
    recorder.start();
    const start = performance.now();
    function tick(now) {
      const progress = Math.min(1, (now - start) / duration);
      drawFrame(progress);
      if (progress < 1) requestAnimationFrame(tick);
      else setTimeout(() => recorder.stop(), 260);
    }
    requestAnimationFrame(tick);
  });
}

const promptStarters = [
  { label: "Luxury listing campaign", prompt: "Create a luxury listing caption, flyer copy, email blast, and SMS for this property." },
  { label: "Open house push", prompt: "Create an open house promo with Facebook copy, Instagram caption, flyer text, email invitation, and SMS invite." },
  { label: "Seller lead ad", prompt: "Create a seller lead generation ad for homeowners considering selling this year." },
  { label: "Recruiting post", prompt: "Make a high-energy recruiting post for agents considering Nex Realty." }
];

const portalPages = [
  ["Dashboard", "LayoutDashboard"],
  ["Marketing Studio", "Sparkles"],
  ["Bulletin Board", "Megaphone"],
  ["Quick Links", "PanelsTopLeft"],
  ["Nex Agent Assist", "Bot"],
  ["Nex Chat", "MessagesSquare"],
  ["Nex lite CRM", "UsersRound"],
  ["Production Tracker", "Calculator"],
  ["Buyer Contract Tracker", "ClipboardCheck"],
  ["NexU / Training Center", "GraduationCap"],
  ["Transaction Help Center", "ClipboardList"],
  ["Support Desk", "LifeBuoy"],
  ["Admin Panel", "ShieldCheck"]
];

const marketingStudioPages = [
  ["Chat Marketing Assistant", "MessageSquare", "Generate complete campaign copy"],
  ["Flyer Builder", "FileText", "Build branded PDF and PNG flyers"],
  ["Quick Video Builder", "Clapperboard", "Create vertical reels or horizontal videos"],
  ["Business Card Builder", "Contact", "Create print-ready business cards"],
  ["Social Media Generator", "Megaphone", "Captions, hashtags, stories, and reels"],
  ["Email/SMS Generator", "Mail", "Email blasts and text scripts"],
  ["Saved Projects", "FolderOpen", "Reload saved marketing work"],
  ["Template Library", "LayoutTemplate", "Manage design templates"]
];

const announcementCategories = ["Training", "Compliance", "Marketing", "Operations", "Events", "Recruiting", "General"];
const supportCategories = ["Transaction/compliance", "Brokermint", "BoldTrail", "Marketing", "Commission/CDA", "Tech support", "Listing support", "General question"];
const supportUrgencies = ["Normal", "High", "Urgent"];
const ticketStatuses = ["Open", "In Progress", "Waiting on Agent", "Closed"];
const trainingCategories = [
  "Internal Office Documents",
  "Documents and Addendas",
  "Lead Generation",
  "Working with buyers",
  "Working with Sellers",
  "Social Media",
  "Nex Level Learning"
];
const NEXU_URL = "https://sites.google.com/mynexrealty.com/nexu/home?authuser=2";
const agentAssistDisclaimer =
  "Nex Agent Assist provides general brokerage guidance and support. Always confirm compliance, legal, MLS, and transaction-specific questions with the broker or appropriate professional.";
const agentAssistStarters = [
  "What do I do after going under contract?",
  "How do I submit a file in Brokermint?",
  "How do I request a CDA?",
  "How do I set up a showing?",
  "Help me write a buyer follow-up text.",
  "Help me create a listing post.",
  "What do I do after signing a buyer broker agreement?",
  "What is the rental transaction process?"
];

const legacyTrainingCategoryMap = {
  "Getting Started": "Nex Level Learning",
  "Buyer Process": "Working with buyers",
  "Seller Process": "Working with Sellers",
  Rentals: "Documents and Addendas",
  Contracts: "Documents and Addendas",
  Compliance: "Internal Office Documents",
  Brokermint: "Internal Office Documents",
  BoldTrail: "Lead Generation",
  Marketing: "Social Media",
  Recruiting: "Nex Level Learning",
  "Advanced Agent Training": "Nex Level Learning"
};

function normalizeTrainingCategory(category) {
  const value = String(category || "").trim();
  if (!value) return trainingCategories[0];
  return legacyTrainingCategoryMap[value] || value;
}

function trainingResourceUrl(resource) {
  const url = String(resource?.url || "").trim();
  return url && url !== "#" ? url : NEXU_URL;
}

function videoTitleFromUrl(url, fallbackIndex = 1) {
  const text = String(url || "");
  try {
    const parsed = new URL(text);
    const pathname = parsed.pathname
      .split("/")
      .filter(Boolean)
      .slice(-2)
      .join(" ")
      .replace(/[-_]+/g, " ");
    return pathname ? pathname.replace(/\b\w/g, (letter) => letter.toUpperCase()) : `NexU Video ${fallbackIndex}`;
  } catch (error) {
    return `NexU Video ${fallbackIndex}`;
  }
}

function parseBulkTrainingVideoLine(line, index = 1) {
  const text = String(line || "").trim();
  if (!text) return null;
  const parts = text.split("|").map((part) => part.trim()).filter(Boolean);
  if (parts.length >= 2) {
    return { title: parts[0], url: parts.slice(1).join("|") };
  }
  return { title: videoTitleFromUrl(text, index), url: text };
}

function getTrainingEmbedUrl(url) {
  const rawUrl = String(url || "").trim();
  if (!rawUrl || rawUrl === "#") return "";
  try {
    const parsed = new URL(rawUrl);
    const host = parsed.hostname.replace(/^www\./, "").toLowerCase();

    if (host.includes("youtube.com")) {
      let id = "";
      if (parsed.pathname.startsWith("/embed/")) id = parsed.pathname.split("/embed/")[1]?.split("/")[0] || "";
      else if (parsed.pathname.startsWith("/shorts/")) id = parsed.pathname.split("/shorts/")[1]?.split("/")[0] || "";
      else id = parsed.searchParams.get("v") || "";
      return id ? `https://www.youtube.com/embed/${id}` : "";
    }

    if (host === "youtu.be") {
      const id = parsed.pathname.split("/").filter(Boolean)[0];
      return id ? `https://www.youtube.com/embed/${id}` : "";
    }

    if (host.includes("vimeo.com")) {
      const id = parsed.pathname.split("/").filter(Boolean).find((part) => /^\d+$/.test(part));
      return id ? `https://player.vimeo.com/video/${id}` : "";
    }

    if (host.includes("loom.com")) {
      const id = parsed.pathname.split("/").filter(Boolean).pop();
      return id ? `https://www.loom.com/embed/${id}` : "";
    }

    if (host.includes("synthesia.io")) {
      if (parsed.pathname.includes("/embeds/videos/")) return rawUrl;
      const id = parsed.pathname.split("/").filter(Boolean).pop();
      return id ? `https://share.synthesia.io/embeds/videos/${id}` : "";
    }

    if (host.includes("drive.google.com")) {
      const fileId = parsed.pathname.match(/\/file\/d\/([^/]+)/)?.[1] || parsed.searchParams.get("id");
      return fileId ? `https://drive.google.com/file/d/${fileId}/preview` : "";
    }

    if (host.includes("docs.google.com") && rawUrl.includes("/preview")) return rawUrl;
    if (rawUrl.includes("/embed/") || rawUrl.includes("/preview")) return rawUrl;
  } catch (error) {
    return "";
  }
  return "";
}

function isEmbeddableTrainingResource(resource) {
  return Boolean(getTrainingEmbedUrl(trainingResourceUrl(resource)));
}

const transactionGuideEnhancements = {
  "new-listing": {
    overview:
      "Use this guide when a seller is ready to list and the file needs to move from signed paperwork to clean launch. The goal is to confirm authority to market, gather accurate property information, and avoid MLS or advertising issues before anything goes public.",
    reminders: [
      "Verify the seller names, property address, listing dates, compensation terms, and signature pages before building marketing.",
      "Confirm photos, remarks, showing instructions, exclusions, HOA notes, and seller disclosures are consistent before MLS entry.",
      "Start the brokerage file early so admin has time to flag missing paperwork before launch pressure builds."
    ]
  },
  "new-buyer": {
    overview:
      "Use this when a buyer becomes active so the agent has representation, lender readiness, search criteria, and communication expectations organized before showings and offers.",
    reminders: [
      "Confirm whether buyer representation paperwork is required before showings in your market and brokerage workflow.",
      "Ask about financing, proof of funds, timing, must-haves, deal breakers, and communication preferences before touring homes.",
      "Set expectations around inspections, appraisal, financing, escrow deposits, and how quickly decisions may need to happen."
    ]
  },
  "buyer-broker-agreement": {
    overview:
      "Use this right after a buyer broker agreement is signed. The practical goal is to make sure the agreement is complete, stored, and aligned with the buyer’s active search and any future offer paperwork.",
    reminders: [
      "Check names, dates, term length, compensation language, signatures, initials, and any brokerage-required disclosures.",
      "Give the buyer a copy and explain what changes operationally now that representation is confirmed.",
      "Keep CRM notes and transaction files aligned so the agreement can be found quickly when an offer is written."
    ]
  },
  "showing-request": {
    overview:
      "Use this before scheduling a showing so access, buyer readiness, and listing instructions are handled cleanly. The goal is to avoid missed instructions, access issues, or unqualified showing activity.",
    reminders: [
      "Review showing instructions carefully, including appointment windows, lockbox/access rules, pets, alarms, and tenant notice.",
      "Confirm the buyer is available, ready, and understands any listing-specific rules before confirming the appointment.",
      "Capture feedback and next steps immediately after the showing while details are fresh."
    ]
  },
  "offer-submitted": {
    overview:
      "Use this when preparing or sending an offer package. The goal is a complete, clear, timely offer with all supporting documents and a tracked response deadline.",
    reminders: [
      "Review price, deposits, dates, contingencies, financing terms, inclusions, exclusions, and any special clauses with the buyer.",
      "Attach pre-approval or proof of funds and verify addenda are signed or initialed as required.",
      "Track the offer deadline and keep negotiation notes organized so counteroffers are handled quickly."
    ]
  },
  "under-contract": {
    overview:
      "Use this immediately after acceptance. This is the highest-risk handoff point because deadlines begin, escrow instructions matter, and the brokerage file needs to be opened or updated quickly.",
    reminders: [
      "Calendar every contract deadline immediately, then compare those dates against the signed agreement.",
      "Send the buyer a simple next-step summary covering escrow, inspections, lender items, appraisal, title, insurance, and closing.",
      "Use the Buyer Contract Tracker for deadline reminders and Brokermint for the official compliance file."
    ]
  },
  "inspection-period": {
    overview:
      "Use this during the inspection window to keep scheduling, report review, repair negotiations, and contingency timing organized.",
    reminders: [
      "Confirm the inspection deadline before recommending timelines or drafting repair requests.",
      "Encourage buyers to review findings with qualified inspectors and contractors where appropriate.",
      "Get signed written agreements for any negotiated repair, credit, or contingency change before relying on it."
    ]
  },
  "appraisal-issue": {
    overview:
      "Use this when appraisal value, appraisal timing, or lender appraisal conditions could affect the deal. The goal is to slow down, verify the contract terms, and involve the right people before changing deal language.",
    reminders: [
      "Confirm the lender’s exact issue and the contract’s appraisal or financing contingency language before advising next steps.",
      "Loop in broker support before sending sensitive negotiation language or value-related requests.",
      "Document any price change, credit, additional funds, extension, or cancellation path with signed paperwork."
    ]
  },
  "closing-process": {
    overview:
      "Use this as the file approaches closing so title, lender, buyer, seller side, brokerage, and commission details stay aligned.",
    reminders: [
      "Confirm closing date, time, location or remote process, walkthrough, utility planning, wire safety reminders, and cash-to-close review.",
      "Request CDA or commission review early enough for admin and title to process without last-minute pressure.",
      "Upload final signed documents and settlement statements after closing according to brokerage policy."
    ]
  },
  "cda-request": {
    overview:
      "Use this when a commission disbursement authorization is needed. The goal is to make sure admin has a complete file and clear commission instructions before sending anything to title.",
    reminders: [
      "Check final commission terms against the contract, amendments, broker instructions, referral agreements, and team split notes.",
      "Make sure Brokermint is complete enough for admin review before requesting the CDA.",
      "Confirm the CDA was delivered to the correct title or closing contact before closing day."
    ]
  },
  "rental-deal": {
    overview:
      "Use this for lease or rental transactions where application, lease, move-in, and commission details need to be tracked.",
    reminders: [
      "Verify available date, deposit terms, commission arrangement, lease signatures, and any property-management instructions.",
      "Keep tenant, landlord, listing side, and brokerage-required documents organized in the file.",
      "Confirm payment timing and who is responsible for sending commission or referral instructions."
    ]
  },
  "referral-deal": {
    overview:
      "Use this before and after referring a client so the referral agreement, receiving brokerage details, and commission terms are documented.",
    reminders: [
      "Get the referral agreement signed before the client is formally handed off when brokerage policy requires it.",
      "Confirm receiving agent, brokerage, license/contact details, referral percentage, payment trigger, and expiration terms.",
      "Track the client status periodically so the referral does not disappear after the introduction."
    ]
  },
  "cancellation-termination": {
    overview:
      "Use this when a deal may cancel or terminate. This is a broker-review moment because dates, deposits, releases, and contract language can have real consequences.",
    reminders: [
      "Pause before sending cancellation language and confirm the correct form/path with broker support.",
      "Review contract deadlines, contingency status, escrow/deposit handling, and signature requirements.",
      "Upload final signed documents and update file status once the cancellation or release is complete."
    ]
  }
};

const crmLeadTypes = ["Buyer", "Seller", "Renter", "Investor", "Agent Recruit", "Landlord", "Referral Partner", "Past Client", "Vendor", "Sphere", "Other"];
const crmPipelineStageMap = {
  Buyer: ["New Lead", "Attempted Contact", "Spoke", "Buyer Consultation Set", "Buyer Agreement Signed", "Showing Homes", "Offer Submitted", "Under Contract", "Closed", "Nurture", "Lost"],
  Seller: ["New Lead", "Attempted Contact", "Spoke", "Listing Appointment Set", "CMA Sent", "Listing Agreement Sent", "Listed", "Offer Received", "Under Contract", "Closed", "Nurture", "Lost"],
  Renter: ["New Lead", "Attempted Contact", "Spoke", "Showing Scheduled", "Application Sent", "Lease Signed", "Closed", "Nurture", "Lost"],
  "Agent Recruit": ["New Prospect", "Contacted", "Interested", "Discovery Call Booked", "Attended Call", "ICA Sent", "Signed ICA", "Transfer Started", "Active Agent", "Nurture", "Lost"],
  Investor: ["New Lead", "Attempted Contact", "Spoke", "Investor Criteria Set", "Deals Sent", "Offer Submitted", "Under Contract", "Closed", "Nurture", "Lost"]
};
const crmDefaultStages = Array.from(
  new Set([
    ...crmPipelineStageMap.Buyer,
    ...crmPipelineStageMap.Seller,
    ...crmPipelineStageMap.Renter,
    ...crmPipelineStageMap["Agent Recruit"],
    ...crmPipelineStageMap.Investor,
    "Connected",
    "Appointment Set",
    "Active Client",
    "Showing/Consultation",
    "Offer/Negotiation",
    "Lost/Inactive"
  ])
);
const crmTaskStatuses = ["Open", "In Progress", "Completed"];
const crmTaskPriorities = ["Normal", "High", "Urgent"];
const crmTaskTypes = ["Call", "Text", "Email", "Appointment", "CMA", "Showing", "Follow-up", "Note", "Other"];
const crmCommunicationTypes = ["Call", "Text", "Email", "Voicemail", "Appointment", "Note"];
const crmCommunicationOutcomes = ["Connected", "Left Message", "No Answer", "Replied", "Appointment Set", "Needs Follow-Up", "Not Interested"];
const crmLeadSources = ["Website", "Landing Page", "Open House", "QR Code", "Facebook", "Instagram", "Google", "Zillow", "Realtor.com", "Referral", "Sphere", "Past Client", "FSBO", "Expired", "Sign Call", "Walk-In", "Manual Entry", "Imported CSV", "Brokerage Lead", "Agent Generated", "Recruiting", "Other"];
const crmTemperatureOptions = ["All", "Hot", "Warm", "Cold", "Nurture", "Dead/DNC"];
const crmContactMethods = ["Call", "Text", "Email", "Any"];
const crmConsentOptions = ["Unknown", "Opted In", "Verbal Consent", "Website Form", "Open House Form", "Do Not Contact", "Unsubscribed"];
const crmWorkingAgentOptions = ["Unknown", "No", "Yes"];
const crmPreApprovedOptions = ["Unknown", "No", "Yes"];
const crmPages = ["Dashboard", "Contacts", "Leads", "Pipeline", "Smart Lists", "Follow-Up Tasks", "Import/Export", "CRM Settings"];
const crmDefaultTags = ["Hot Buyer", "Hot Seller", "DPA", "VA", "Investor", "Luxury", "First-Time Buyer", "New Construction", "FSBO", "Expired", "Open House", "Past Client", "Referral", "Recruiting", "Needs Financing", "Cash Buyer"];
const crmSmartPlans = [
  {
    id: "new-buyer-lead",
    name: "New Buyer Lead",
    leadTypes: ["Buyer", "Investor"],
    tasks: [
      [0, "Call immediately", "Call", "Urgent"],
      [0, "Send first buyer text", "Text", "High"],
      [1, "Follow-up call", "Call", "High"],
      [2, "Send buyer needs text", "Text", "Normal"],
      [4, "Email buyer guide", "Email", "Normal"],
      [7, "Follow-up call", "Call", "Normal"],
      [14, "Nurture check-in", "Follow-up", "Normal"],
      [30, "Reconnect and update timeline", "Follow-up", "Normal"]
    ],
    scripts: {
      text: "Hi [Name], this is [Agent] with Nex Realty. I saw you were looking around [Area]. What kind of home would make the move worth it for you?",
      email: "Subject: A simple plan for your home search\n\nHi [Name], I wanted to send a quick next-step plan so your search feels organized. If you send me your must-haves, budget range, and timeline, I can narrow the options and help you avoid wasting time."
    }
  },
  {
    id: "new-seller-lead",
    name: "New Seller Lead",
    leadTypes: ["Seller", "Past Client"],
    tasks: [
      [0, "Call immediately", "Call", "Urgent"],
      [0, "Send home value text", "Text", "High"],
      [1, "Ask property details", "Text", "High"],
      [2, "Offer CMA appointment", "Email", "Normal"],
      [5, "Seller follow-up", "Call", "Normal"],
      [10, "Send seller checklist", "Email", "Normal"],
      [30, "Send market update", "Email", "Normal"]
    ],
    scripts: {
      text: "Hi [Name], this is [Agent] with Nex Realty. I can help you get a realistic read on what your home could sell for in today's market. Want me to put together a quick value range?",
      email: "Subject: Your home value next steps\n\nHi [Name], I can help you understand pricing, prep, timing, and the most likely buyer profile for your property. The best next step is a quick property review."
    }
  },
  {
    id: "open-house-lead",
    name: "Open House Lead",
    leadTypes: ["Buyer", "Seller"],
    tasks: [
      [0, "Text open house thank-you", "Text", "High"],
      [0, "Call after open house", "Call", "High"],
      [1, "Send similar homes or seller value offer", "Email", "Normal"],
      [3, "Follow up on motivation and timeline", "Call", "Normal"],
      [7, "Invite to consult", "Follow-up", "Normal"]
    ],
    scripts: {
      text: "Hi [Name], thanks for stopping by the open house. What did you think of the home compared with what you are really looking for?",
      email: "Subject: Thanks for visiting the open house\n\nHi [Name], thanks for coming by. I can send a few similar homes or help you understand what your own home may be worth."
    }
  },
  {
    id: "agent-recruit-follow-up",
    name: "Agent Recruit Follow-Up",
    leadTypes: ["Agent Recruit"],
    tasks: [
      [0, "Send Nex opportunity text", "Text", "High"],
      [1, "Book discovery call", "Call", "High"],
      [3, "Send Nex model overview", "Email", "Normal"],
      [7, "Follow up on questions", "Call", "Normal"],
      [14, "Invite to leadership call", "Appointment", "Normal"]
    ],
    scripts: {
      text: "Hi [Name], I thought of you because Nex Realty is built for agents who want stronger systems, modern branding, and more leverage. Open to a quick conversation?",
      email: "Subject: A different brokerage model\n\nHi [Name], Nex Realty is building a modern platform for productive agents. If you are evaluating your next move, I would be happy to walk you through it."
    }
  },
  {
    id: "long-term-nurture",
    name: "Long-Term Nurture",
    leadTypes: ["Buyer", "Seller", "Renter", "Investor", "Past Client", "Sphere"],
    tasks: [
      [7, "Send helpful market check-in", "Email", "Normal"],
      [21, "Personal text check-in", "Text", "Normal"],
      [45, "Send value update", "Email", "Normal"],
      [90, "Reconnect call", "Call", "Normal"]
    ],
    scripts: {
      text: "Hi [Name], just checking in. Has anything changed with your real estate plans, timeline, or questions I can help with?",
      email: "Subject: Quick real estate check-in\n\nHi [Name], I wanted to stay in touch and be a resource when the timing is right. If anything has changed, I am happy to help."
    }
  }
];
const emptyCrmContact = {
  firstName: "",
  lastName: "",
  email: "",
  phone: "",
  secondaryPhone: "",
  secondaryEmail: "",
  leadType: "Buyer",
  status: "New",
  leadSource: "",
  assignedAgentEmail: "",
  assignedAgentName: "",
  stage: "New Lead",
  preferredContactMethod: "Any",
  budgetRange: "",
  desiredArea: "",
  timeline: "",
  buyingTimeline: "",
  sellingTimeline: "",
  propertyAddress: "",
  bedrooms: "",
  bathrooms: "",
  financingStatus: "",
  preApproved: "Unknown",
  workingWithAnotherAgent: "Unknown",
  consentStatus: "Unknown",
  optInSource: "",
  doNotContact: false,
  birthday: "",
  homeAnniversary: "",
  campaignName: "",
  formName: "",
  utmSource: "",
  utmMedium: "",
  utmCampaign: "",
  referringUrl: "",
  notes: "",
  tags: "",
  lastContactedDate: "",
  nextFollowUpDate: ""
};
const buyerStatuses = ["Active", "Closing Soon", "Closed", "Cancelled"];
const buyerTaskPriorities = ["Low", "Normal", "High", "Critical"];
const buyerTrackerTabs = ["Transactions", "Create New Transaction", "Notifications"];
const buyerTrackerDisclaimer =
  "This checklist is an internal organizational tool only and does not replace the purchase contract, brokerage compliance requirements, Brokermint file review, MLS rules, lender/title deadlines, or legal advice. Agents are responsible for verifying all contract deadlines and requirements.";
const emptyBuyerTransaction = {
  transactionName: "",
  buyerName: "",
  propertyAddress: "",
  mlsNumber: "",
  effectiveDate: "",
  closingDate: "",
  inspectionPeriodEndDate: "",
  financingPeriodEndDate: "",
  appraisalDeadline: "",
  hoaDeadline: "",
  emdDueDate: "",
  finalWalkthroughDate: "",
  agentName: "",
  agentEmail: "",
  agentPhone: "",
  notes: ""
};
const productionStorageKey = "nex-production-calculator";
const productionTrackerPeriods = ["Daily", "Weekly"];
const productionActivityFields = [
  ["callsMade", "Calls Made", "Phone"],
  ["conversationsHad", "Conversations Had", "MessagesSquare"],
  ["leadsGenerated", "Leads Generated", "UserPlus"],
  ["appointmentsSet", "Appointments Set", "CalendarCheck"],
  ["buyerAgreementsSigned", "Buyer Agreements Signed", "FileSignature"],
  ["listingAgreementsSigned", "Listing Agreements Signed", "Home"],
  ["contractsWritten", "Contracts Written", "PenLine"],
  ["contractsAccepted", "Contracts Accepted", "Handshake"],
  ["closings", "Closings", "BadgeDollarSign"]
];
const productionAssumptionFields = [
  ["callToConversationRate", "Call to conversation rate"],
  ["conversationToLeadRate", "Conversation to lead rate"],
  ["leadToAppointmentRate", "Lead to appointment rate"],
  ["appointmentToSignedClientRate", "Appointment to signed client rate"],
  ["signedClientToContractRate", "Signed client to contract rate"],
  ["contractToCloseRate", "Contract to close rate"]
];
const DEFAULT_PRODUCTION_ASSUMPTIONS = {
  monthlyClosingGoal: 2,
  callToConversationRate: 0.18,
  conversationToLeadRate: 0.3,
  leadToAppointmentRate: 0.25,
  appointmentToSignedClientRate: 0.5,
  signedClientToContractRate: 0.45,
  contractToCloseRate: 0.85,
  prospectingDaysPerMonth: 22,
  prospectingDaysPerWeek: 5,
  callsPerHour: 25,
  averageSalesPrice: 400000,
  averageCommissionRate: 0.025
};
const DEFAULT_ACTIVITY = {
  callsMade: 0,
  conversationsHad: 0,
  leadsGenerated: 0,
  appointmentsSet: 0,
  buyerAgreementsSigned: 0,
  listingAgreementsSigned: 0,
  contractsWritten: 0,
  contractsAccepted: 0,
  closings: 0
};

function fileToDataUrl(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve({ name: file.name, dataUrl: reader.result, type: file.type || "application/octet-stream", size: file.size || 0 });
    reader.onerror = reject;
    reader.readAsDataURL(file);
  });
}

function copyToClipboard(text, setToast) {
  navigator.clipboard
    .writeText(text)
    .then(() => setToast("Copied to clipboard."))
    .catch(() => setToast("Copy failed. Select the text and copy manually."));
}

function crmContactName(contact) {
  if (!contact) return "Select a contact";
  return [contact.firstName, contact.lastName].filter(Boolean).join(" ").trim() || contact.email || contact.phone || "Unnamed contact";
}

function normalizeEmail(value) {
  return String(value || "").trim().toLowerCase();
}

function crmTagsText(tags) {
  if (Array.isArray(tags)) return tags.join(", ");
  return tags || "";
}

function crmShortDate(value) {
  if (!value) return "Not set";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });
}

function crmIsDue(value) {
  if (!value) return false;
  const due = new Date(`${value}T23:59:59`);
  if (Number.isNaN(due.getTime())) return false;
  return due.getTime() < Date.now();
}

function crmIsDueToday(value) {
  const days = daysUntil(value);
  return days === 0;
}

function crmPipelineStagesForType(type, fallbackStages = crmDefaultStages) {
  const normalized = type === "Recruit" ? "Agent Recruit" : type;
  return crmPipelineStageMap[normalized] || fallbackStages || crmDefaultStages;
}

function crmLeadScore(contact, contactActivity = []) {
  if (!contact) return 0;
  let score = 0;
  const source = String(contact.leadSource || "").toLowerCase();
  const timeline = String(contact.timeline || contact.buyingTimeline || contact.sellingTimeline || "").toLowerCase();
  const tags = crmTagsText(contact.tags).toLowerCase();
  const stage = String(contact.stage || "").toLowerCase();
  const hasFastTimeline = /(0|30|60|90|asap|now|ready|this week|this month|soon)/i.test(timeline);

  if (contact.phone || contact.secondaryPhone) score += 25;
  if (hasFastTimeline) score += 20;
  if ((contact.leadType === "Seller" || contact.sellingTimeline) && contact.propertyAddress) score += 20;
  if (String(contact.preApproved || "").toLowerCase() === "yes") score += 15;
  if (contact.budgetRange) score += 15;
  if (contact.desiredArea) score += 10;
  if (source.includes("open house")) score += 10;
  if (source.includes("home value") || String(contact.campaignName || "").toLowerCase().includes("home value")) score += 10;
  if (contact.lastContactedDate || contact.firstActivityAt || contactActivity.length) score += 10;
  if (stage.includes("appointment") || stage.includes("consultation") || stage.includes("spoke")) score += 10;
  if (stage.includes("under contract")) score += 15;
  if (tags.includes("hot")) score += 12;
  if (contact.doNotContact || String(contact.consentStatus || "").toLowerCase().includes("do not")) score -= 100;
  if (stage.includes("lost") || tags.includes("unresponsive")) score -= 20;

  const activityDate = contact.lastContactedDate || contact.firstActivityAt || contact.updatedAt || contact.createdAt;
  const parsedActivityDate = activityDate ? new Date(String(activityDate).includes("T") ? activityDate : `${activityDate}T00:00:00`) : null;
  const daysSinceActivity = parsedActivityDate && !Number.isNaN(parsedActivityDate.getTime()) ? Math.max(0, Math.round((Date.now() - parsedActivityDate.getTime()) / 86400000)) : null;
  if (!contact.lastContactedDate && daysSinceActivity !== null && daysSinceActivity >= 7) score -= 10;

  return Math.max(-100, Math.min(100, Math.round(score)));
}

function crmTemperature(contact, contactActivity = []) {
  if (!contact) return "Cold";
  const score = typeof contact.leadScore === "number" ? contact.leadScore : crmLeadScore(contact, contactActivity);
  const stage = String(contact.stage || "").toLowerCase();
  if (contact.doNotContact || String(contact.consentStatus || "").toLowerCase().includes("do not") || score <= 0) return "Dead/DNC";
  if (stage.includes("nurture")) return "Nurture";
  if (score >= 70) return "Hot";
  if (score >= 40) return "Warm";
  return "Cold";
}

function crmTemperatureClass(value) {
  return String(value || "Cold").toLowerCase().replace(/[^a-z0-9]+/g, "-");
}

function crmContactActivityItems(contact, notes = [], activity = []) {
  if (!contact) return [];
  return [
    ...notes.filter((note) => note.contactId === contact.id).map((note) => ({ ...note, type: "note", title: "Note", body: note.body })),
    ...activity.filter((item) => item.contactId === contact.id)
  ].sort((a, b) => new Date(b.createdAt || 0) - new Date(a.createdAt || 0));
}

function crmDaysSinceContact(contact, notes = [], activity = []) {
  const latest = contact?.lastContactedDate || crmContactActivityItems(contact, notes, activity)[0]?.createdAt || contact?.createdAt;
  if (!latest) return null;
  const parsed = new Date(String(latest).includes("T") ? latest : `${latest}T00:00:00`);
  if (Number.isNaN(parsed.getTime())) return null;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  parsed.setHours(0, 0, 0, 0);
  return Math.max(0, Math.round((today.getTime() - parsed.getTime()) / 86400000));
}

function crmSpeedToLead(contact) {
  if (!contact?.createdAt) return "Not tracked";
  const first = contact.firstActivityAt || contact.lastContactedDate || "";
  if (!first) return "Not contacted";
  const created = new Date(contact.createdAt);
  const contacted = new Date(first.length <= 10 ? `${first}T12:00:00` : first);
  if (Number.isNaN(created.getTime()) || Number.isNaN(contacted.getTime())) return "Tracked";
  const minutes = Math.max(0, Math.round((contacted.getTime() - created.getTime()) / 60000));
  if (minutes <= 5) return "Under 5 min";
  if (minutes <= 15) return "Under 15 min";
  if (minutes <= 60) return "Under 1 hr";
  if (minutes <= 1440) return "Under 24 hr";
  return `${Math.round(minutes / 1440)}d`;
}

function crmSmartListDefinitions(contacts, tasks, notes, activity, permissions = {}) {
  const unassigned = (contact) => !contact.assignedAgentEmail;
  const daysSince = (contact) => crmDaysSinceContact(contact, notes, activity);
  const openTasksFor = (contact) => tasks.filter((task) => task.contactId === contact.id && task.status !== "Completed");
  return [
    { key: "new-not-contacted", label: "New leads not contacted", icon: "Zap", predicate: (contact) => /new lead|new prospect/i.test(contact.stage || "") && !contact.lastContactedDate },
    { key: "hot", label: "Hot leads", icon: "Flame", predicate: (contact) => crmTemperature(contact, crmContactActivityItems(contact, notes, activity)) === "Hot" },
    { key: "due-today", label: "Follow-up due today", icon: "CalendarClock", predicate: (contact) => openTasksFor(contact).some((task) => crmIsDueToday(task.dueDate)) || crmIsDueToday(contact.nextFollowUpDate) },
    { key: "overdue", label: "Overdue follow-ups", icon: "AlarmClock", predicate: (contact) => openTasksFor(contact).some((task) => crmIsDue(task.dueDate)) || crmIsDue(contact.nextFollowUpDate) },
    { key: "no-activity-3", label: "No activity in 3 days", icon: "Clock3", predicate: (contact) => (daysSince(contact) || 0) >= 3 },
    { key: "no-activity-7", label: "No activity in 7 days", icon: "Clock7", predicate: (contact) => (daysSince(contact) || 0) >= 7 },
    { key: "no-activity-14", label: "No activity in 14 days", icon: "CalendarX", predicate: (contact) => (daysSince(contact) || 0) >= 14 },
    { key: "buyers-90", label: "Buyers under 90-day timeline", icon: "Home", predicate: (contact) => contact.leadType === "Buyer" && /(0|30|60|90|asap|soon|ready)/i.test(`${contact.timeline} ${contact.buyingTimeline}`) },
    { key: "sellers-address", label: "Sellers with property address", icon: "MapPinned", predicate: (contact) => contact.leadType === "Seller" && Boolean(contact.propertyAddress) },
    { key: "open-house", label: "Open house leads", icon: "DoorOpen", predicate: (contact) => /open house/i.test(`${contact.leadSource} ${crmTagsText(contact.tags)}`) },
    { key: "website-form", label: "Website/form leads", icon: "Globe2", predicate: (contact) => /website|landing page|form|qr/i.test(`${contact.leadSource} ${contact.formName}`) },
    { key: "sphere", label: "Sphere contacts", icon: "UsersRound", predicate: (contact) => contact.leadType === "Sphere" || /sphere/i.test(`${contact.leadSource} ${crmTagsText(contact.tags)}`) },
    { key: "past-client", label: "Past clients", icon: "HeartHandshake", predicate: (contact) => contact.leadType === "Past Client" },
    { key: "dnc", label: "DNC / do not contact", icon: "Ban", predicate: (contact) => Boolean(contact.doNotContact) || /do not|unsubscribe/i.test(contact.consentStatus || "") },
    { key: "unassigned", label: "Unassigned leads", icon: "UserRoundX", predicate: (contact) => permissions.canViewAll && unassigned(contact) },
    { key: "no-next-follow-up", label: "Leads without next follow-up", icon: "CalendarPlus", predicate: (contact) => !contact.nextFollowUpDate && openTasksFor(contact).length === 0 },
    { key: "agent-recruits", label: "Agent recruits", icon: "BadgePlus", predicate: (contact) => contact.leadType === "Agent Recruit" || contact.leadType === "Recruit" },
    { key: "broker-attention", label: "Leads needing broker attention", icon: "ShieldAlert", predicate: (contact) => /broker|urgent|compliance|issue/i.test(`${contact.notes} ${crmTagsText(contact.tags)}`) }
  ];
}

function addDays(value, days) {
  const start = value ? new Date(`${value}T00:00:00`) : new Date();
  if (Number.isNaN(start.getTime())) return "";
  start.setDate(start.getDate() + Number(days || 0));
  return start.toISOString().slice(0, 10);
}

function dateOnly(value) {
  if (!value) return "";
  const date = new Date(`${value}T00:00:00`);
  if (Number.isNaN(date.getTime())) return "";
  return date.toISOString().slice(0, 10);
}

function daysUntil(value) {
  const dateString = dateOnly(value);
  if (!dateString) return null;
  const due = new Date(`${dateString}T00:00:00`);
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  return Math.round((due.getTime() - today.getTime()) / 86400000);
}

function formatDate(value) {
  if (!value) return "Not set";
  const date = new Date(`${value}T00:00:00`);
  if (Number.isNaN(date.getTime())) return value;
  return date.toLocaleDateString([], { month: "short", day: "numeric", year: "numeric" });
}

function buyerTaskTone(task) {
  if (task.completed) return "complete";
  if (!task.dueDate) return "none";
  const days = daysUntil(task.dueDate);
  if (days === null) return "none";
  if (days < 0) return "overdue";
  if (days === 0) return "today";
  if (days === 1) return "tomorrow";
  if (days <= 3) return "soon";
  return "none";
}

function buyerCompletionPercent(tasks) {
  if (!tasks.length) return 0;
  return Math.round((tasks.filter((task) => task.completed).length / tasks.length) * 100);
}

function nextBuyerDeadline(tasks) {
  return tasks
    .filter((task) => !task.completed && task.dueDate)
    .sort((a, b) => new Date(`${a.dueDate}T00:00:00`) - new Date(`${b.dueDate}T00:00:00`))[0] || null;
}

function buyerOverdueCount(tasks) {
  return tasks.filter((task) => !task.completed && task.dueDate && Number(daysUntil(task.dueDate)) < 0).length;
}

function buyerDisplayStatus(transaction) {
  if (!transaction) return "Active";
  if (transaction.status === "Closed" || transaction.status === "Cancelled") return transaction.status;
  const days = daysUntil(transaction.closingDate);
  if (days !== null && days <= 7 && days >= 0) return "Closing Soon";
  return transaction.status || "Active";
}

function clampRate(value) {
  const number = Number(value);
  if (!Number.isFinite(number)) return 0.01;
  if (number > 1) return Math.min(Math.max(number / 100, 0.01), 1);
  return Math.min(Math.max(number, 0.01), 1);
}

function roundUp(value) {
  return Math.ceil(Number.isFinite(value) ? value : 0);
}

function calculateProductionPlan(input) {
  const monthlyClosings = Math.max(1, Number(input.monthlyClosingGoal) || 1);
  const contractToCloseRate = clampRate(input.contractToCloseRate);
  const signedClientToContractRate = clampRate(input.signedClientToContractRate);
  const appointmentToSignedClientRate = clampRate(input.appointmentToSignedClientRate);
  const leadToAppointmentRate = clampRate(input.leadToAppointmentRate);
  const conversationToLeadRate = clampRate(input.conversationToLeadRate);
  const callToConversationRate = clampRate(input.callToConversationRate);
  const prospectingDaysPerMonth = Math.max(1, Number(input.prospectingDaysPerMonth) || 22);
  const prospectingDaysPerWeek = Math.max(1, Number(input.prospectingDaysPerWeek) || 5);
  const callsPerHour = Math.max(1, Number(input.callsPerHour) || 25);

  const contractsNeeded = monthlyClosings / contractToCloseRate;
  const signedClientsNeeded = contractsNeeded / signedClientToContractRate;
  const appointmentsNeeded = signedClientsNeeded / appointmentToSignedClientRate;
  const leadsNeeded = appointmentsNeeded / leadToAppointmentRate;
  const conversationsNeeded = leadsNeeded / conversationToLeadRate;
  const callsNeeded = conversationsNeeded / callToConversationRate;
  const weeksPerMonth = prospectingDaysPerMonth / prospectingDaysPerWeek;
  const weeklyCallsNeeded = callsNeeded / weeksPerMonth;
  const dailyCallsNeeded = callsNeeded / prospectingDaysPerMonth;
  const dailyHoursNeeded = dailyCallsNeeded / callsPerHour;
  const averageSalesPrice = Math.max(0, Number(input.averageSalesPrice) || 0);
  const averageCommissionRate = clampRate(input.averageCommissionRate || 0.025);
  const estimatedGci = monthlyClosings * averageSalesPrice * averageCommissionRate;

  return {
    monthlyClosings: roundUp(monthlyClosings),
    contractsNeeded: roundUp(contractsNeeded),
    signedClientsNeeded: roundUp(signedClientsNeeded),
    appointmentsNeeded: roundUp(appointmentsNeeded),
    leadsNeeded: roundUp(leadsNeeded),
    conversationsNeeded: roundUp(conversationsNeeded),
    callsNeeded: roundUp(callsNeeded),
    weeklyCallsNeeded: roundUp(weeklyCallsNeeded),
    dailyCallsNeeded: roundUp(dailyCallsNeeded),
    dailyHoursNeeded: Number(dailyHoursNeeded.toFixed(1)),
    estimatedGci: Math.round(estimatedGci)
  };
}

function progressRatio(actual, goal) {
  const safeGoal = Math.max(1, Number(goal) || 1);
  return Math.min((Number(actual) || 0) / safeGoal, 1);
}

function productionPeriodMultiplier(assumptions, period) {
  return period === "Weekly" ? Math.max(1, Number(assumptions.prospectingDaysPerWeek) || 5) : 1;
}

function calculateProductionActivityGoals(required, assumptions = DEFAULT_PRODUCTION_ASSUMPTIONS, period = "Daily") {
  const prospectingDays = Math.max(1, Number(assumptions.prospectingDaysPerMonth) || 22);
  const multiplier = productionPeriodMultiplier(assumptions, period);
  return {
    callsMade: required.dailyCallsNeeded * multiplier,
    conversationsHad: (required.conversationsNeeded / prospectingDays) * multiplier,
    leadsGenerated: (required.leadsNeeded / prospectingDays) * multiplier,
    appointmentsSet: (required.appointmentsNeeded / prospectingDays) * multiplier,
    signedClients: (required.signedClientsNeeded / prospectingDays) * multiplier,
    buyerAgreementsSigned: (required.signedClientsNeeded / prospectingDays) * multiplier,
    listingAgreementsSigned: (required.signedClientsNeeded / prospectingDays) * multiplier,
    contractsWritten: (required.contractsNeeded / prospectingDays) * multiplier,
    contractsAccepted: (required.contractsNeeded / prospectingDays) * multiplier,
    closings: (required.monthlyClosings / prospectingDays) * multiplier
  };
}

function calculateAccountabilityScore(actual, required, assumptions = DEFAULT_PRODUCTION_ASSUMPTIONS, period = "Daily") {
  const goals = calculateProductionActivityGoals(required, assumptions, period);
  const callScore = progressRatio(actual.callsMade, goals.callsMade);
  const conversationScore = progressRatio(actual.conversationsHad, goals.conversationsHad);
  const leadScore = progressRatio(actual.leadsGenerated, goals.leadsGenerated);
  const appointmentScore = progressRatio(actual.appointmentsSet, goals.appointmentsSet);
  const contractScore = progressRatio(actual.contractsAccepted, goals.contractsAccepted);
  const total = callScore * 30 + conversationScore * 20 + leadScore * 15 + appointmentScore * 15 + contractScore * 20;
  return Math.round(Math.min(total, 100));
}

function getAccountabilityStatus(score) {
  if (score >= 90) return "Elite Pace";
  if (score >= 75) return "On Track";
  if (score >= 50) return "Needs Push";
  return "Off Pace";
}

function getProductionPace(actual, goal) {
  const ratio = progressRatio(actual, goal);
  if (ratio >= 1.05) return "Ahead";
  if (ratio >= 0.85) return "On Track";
  return "Behind";
}

function formatCurrency(value) {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: "USD",
    maximumFractionDigits: 0
  }).format(Number(value) || 0);
}

function formatPercent(decimal) {
  return `${Math.round(clampRate(decimal) * 100)}%`;
}

function productionPercentInputValue(value, precision = 0) {
  if (value === "") return "";
  return Number((clampRate(value) * 100).toFixed(precision));
}

function csvEscape(value) {
  const text = Array.isArray(value) ? value.join(", ") : String(value || "");
  if (/[",\n]/.test(text)) return `"${text.replace(/"/g, '""')}"`;
  return text;
}

function parseCsvRows(csvText) {
  const rows = [];
  let current = "";
  let row = [];
  let inQuotes = false;
  const text = String(csvText || "").replace(/\r\n/g, "\n");
  for (let index = 0; index < text.length; index += 1) {
    const char = text[index];
    const next = text[index + 1];
    if (char === '"' && inQuotes && next === '"') {
      current += '"';
      index += 1;
    } else if (char === '"') {
      inQuotes = !inQuotes;
    } else if (char === "," && !inQuotes) {
      row.push(current);
      current = "";
    } else if (char === "\n" && !inQuotes) {
      row.push(current);
      rows.push(row);
      row = [];
      current = "";
    } else {
      current += char;
    }
  }
  if (current || row.length) {
    row.push(current);
    rows.push(row);
  }
  return rows.filter((line) => line.some((cell) => String(cell || "").trim()));
}

function crmContactFromCsvRow(headers, row) {
  const map = {};
  headers.forEach((header, index) => {
    const key = String(header || "").trim().toLowerCase().replace(/[\s_-]+/g, "");
    map[key] = row[index] || "";
  });
  return {
    firstName: map.firstname || map.first || "",
    lastName: map.lastname || map.last || "",
    email: map.email || "",
    phone: map.phone || "",
    secondaryEmail: map.secondaryemail || "",
    secondaryPhone: map.secondaryphone || "",
    leadType: crmLeadTypes.includes(map.leadtype) ? map.leadtype : "Other",
    leadSource: map.leadsource || map.source || "",
    status: map.status || "",
    assignedAgentEmail: map.assignedagentemail || map.assignedagent || "",
    stage: map.stage || map.status || "New Lead",
    budgetRange: map.budgetrange || map.price || map.pricerange || "",
    desiredArea: map.desiredarea || map.area || "",
    timeline: map.timeline || "",
    buyingTimeline: map.buyingtimeline || "",
    sellingTimeline: map.sellingtimeline || "",
    propertyAddress: map.propertyaddress || map.address || "",
    bedrooms: map.bedrooms || map.beds || "",
    bathrooms: map.bathrooms || map.baths || "",
    financingStatus: map.financingstatus || "",
    preApproved: map.preapproved || "",
    workingWithAnotherAgent: map.workingwithanotheragent || "",
    consentStatus: map.consentstatus || "",
    doNotContact: /^(true|yes|1)$/i.test(map.donotcontact || map.dnc || ""),
    campaignName: map.campaignname || "",
    formName: map.formname || "",
    utmSource: map.utmsource || "",
    utmMedium: map.utmmedium || "",
    utmCampaign: map.utmcampaign || "",
    referringUrl: map.referringurl || "",
    notes: map.notes || "",
    tags: map.tags || "",
    lastContactedDate: map.lastcontacteddate || "",
    nextFollowUpDate: map.nextfollowupdate || ""
  };
}

function fallbackGenerate({ fields, brandSettings, prompt }) {
  const brand = brandSettings || localDefaultBrand;
  const marketingType = fields.marketingType || "Just Listed";
  const headline = marketingType.toUpperCase();
  const price = money(fields.price);
  const address = fields.propertyAddress || "the featured property";
  const location = [fields.city, fields.state].filter(Boolean).join(", ");
  const cta = fields.cta || "Schedule your tour today";
  const isRecruiting = marketingType.toLowerCase().includes("recruit");
  const primary = isRecruiting
    ? `${brand.companyName} is built for modern agents who want bold branding, a cleaner cloud-based brokerage model, and the support to grow with confidence.`
    : `${headline}: ${address}${location ? ` in ${location}` : ""}${price ? ` is offered at ${price}` : ""}. ${fields.propertyHighlights || fields.mlsRemarks || "Designed for strong market attention and a polished first impression."} ${cta}.`;
  return {
    id: crypto.randomUUID ? crypto.randomUUID() : String(Date.now()),
    createdAt: new Date().toISOString(),
    marketingType,
    platform: fields.platform,
    tone: fields.tone,
    complianceReminder:
      "Please review all marketing for MLS, brokerage, Fair Housing, advertising, and local/state compliance before publishing.",
    suggestedHeadline: headline,
    suggestedCTA: cta,
    primary,
    shortVersion: `${headline}. ${cta}.`,
    longVersion: `${primary} Contact ${fields.agentName || "your Nex Realty agent"} for details.`,
    flyerReadyCopy: `${fields.propertyHighlights || "Premium Nex Realty marketing for this opportunity."}`,
    emailVersion: `Subject: ${headline}: ${address}\n\n${primary}\n\n${cta}.`,
    smsVersion: `${headline}: ${address}${price ? ` at ${price}` : ""}. ${cta}. Reply for details.`,
    hashtags: ["#NexRealty", "#RealEstate", "#HomeMarketing", "#JustListed"],
    missingSuggestions: prompt ? [] : ["chat request"],
    footerDisclaimer: isRecruiting ? brand.recruitingDisclaimer : brand.defaultDisclaimer
  };
}

function logoSource(brand, variant = "light") {
  if (variant === "dark") {
    return brand.logoWhiteDataUrl || brand.logoWhiteUrl || defaultLogoWhiteUrl;
  }
  return brand.logoBlackDataUrl || brand.logoDataUrl || brand.logoBlackUrl || defaultLogoBlackUrl;
}

function Wordmark({ brand, compact = false, variant = "light", className = "" }) {
  const source = logoSource(brand, variant);
  if (source) {
    return (
      <img
        src={source}
        alt={`${brand.companyName} logo`}
        className={classNames("brand-logo", compact && "brand-logo-compact", variant === "dark" && "brand-logo-white", className)}
      />
    );
  }
  return (
    <div className={classNames("wordmark", compact && "wordmark-compact")}>
      <span>NEX</span>
      <strong>REALTY</strong>
    </div>
  );
}

function Sidebar({ activePage, setActivePage, currentUser, onLogout, brand, chatUnreadTotal = 0 }) {
  const role = currentUser?.role || "Agent";
  const pages = portalPages.filter(([page]) => page !== "Admin Panel" || isAdminRole(role));
  const isMarketingActive = ["Marketing Studio", ...marketingStudioPages.map(([page]) => page)].includes(activePage);

  return (
    <aside className="sidebar">
      <div className="brand-lockup">
        <Wordmark brand={brand} variant="dark" className="platform-logo" />
        <p>{APP_NAME}</p>
      </div>

      <nav className="nav-list" aria-label="Main navigation">
        {pages.map(([page, icon]) => (
          <button
            key={page}
            className={classNames("nav-item", (activePage === page || (page === "Marketing Studio" && isMarketingActive)) && "active")}
            onClick={() => setActivePage(page)}
            title={page}
          >
            <Icon name={icon} size={17} />
            <span>{page}</span>
            {page === "Nex Chat" && chatUnreadTotal > 0 && <em className="nav-unread-badge">{chatUnreadTotal > 99 ? "99+" : chatUnreadTotal}</em>}
          </button>
        ))}
      </nav>

      <div className="role-card">
        <p className="text-xs uppercase tracking-[0.16em] text-white/45">Signed in</p>
        <div className="signed-in-card">
          <strong>{currentUser?.name || currentUser?.email || "Nex Realty user"}</strong>
          <span>{role}</span>
        </div>
        <p className="mt-3 text-xs leading-5 text-white/55">{currentUser?.email}</p>
        <button className="logout-btn" onClick={onLogout}>
          <Icon name="LogOut" size={15} />
          <span>Sign Out</span>
        </button>
      </div>
    </aside>
  );
}

function NotificationBell({ notifications = [], onOpenNotifications }) {
  const unread = notifications.filter((notification) => !notification.read).length;
  return (
    <button className="notification-bell" onClick={onOpenNotifications} title="Open notifications">
      <Icon name="Bell" />
      {unread > 0 && <span>{unread > 9 ? "9+" : unread}</span>}
    </button>
  );
}

function Header({ activePage, onGenerate, onSaveProject, isGenerating, isMarketingPage, canInstall, onInstall, notifications = [], onOpenNotifications }) {
  const hasHeaderActions = isMarketingPage || canInstall || Boolean(onOpenNotifications);
  return (
    <header className="topbar">
      <div>
        <p className="eyebrow">{APP_NAME}</p>
        <h2>{activePage}</h2>
      </div>
      {hasHeaderActions && (
        <div className="topbar-actions">
          {onOpenNotifications && <NotificationBell notifications={notifications} onOpenNotifications={onOpenNotifications} />}
          {canInstall && (
            <button className="btn secondary install-app-btn" onClick={onInstall}>
              <Icon name="Smartphone" />
              <span>Install App</span>
            </button>
          )}
          {isMarketingPage && (
            <>
              <button className="btn secondary" onClick={onSaveProject}>
                <Icon name="Save" />
                <span>Save Project</span>
              </button>
              <button className="btn primary" onClick={onGenerate} disabled={isGenerating}>
                <Icon name={isGenerating ? "LoaderCircle" : "Sparkles"} className={isGenerating ? "spin" : ""} />
                <span>{isGenerating ? "Generating" : "Generate"}</span>
              </button>
            </>
          )}
        </div>
      )}
    </header>
  );
}

function StatCard({ icon, label, value, tone = "dark" }) {
  return (
    <div className={classNames("stat-card", tone)}>
      <Icon name={icon} size={20} />
      <div>
        <p>{label}</p>
        <strong>{value}</strong>
      </div>
    </div>
  );
}

function Dashboard({ projects, templates, announcements, quickLinks, trainingResources, tickets, currentUser, latestChat, chatUnreadTotal = 0, buyerNotifications = [], setActivePage, setBuyerTrackerStartView, setSelectedTemplateId, setFields, brand, openQuickLink }) {
  const isAdmin = isAdminRole(currentUser?.role);
  const displayName = currentUser?.name || currentUser?.email?.split("@")[0] || "Nex Agent";
  const todayLabel = new Date().toLocaleDateString([], { weekday: "long", month: "short", day: "numeric" });
  const focusStorageKey = `nex-dashboard-focus-${currentUser?.email || "guest"}-${new Date().toISOString().slice(0, 10)}`;
  const [focusDone, setFocusDone] = useState(() => {
    try {
      return JSON.parse(localStorage.getItem(focusStorageKey) || "{}");
    } catch {
      return {};
    }
  });

  useEffect(() => {
    localStorage.setItem(focusStorageKey, JSON.stringify(focusDone));
  }, [focusDone, focusStorageKey]);

  function openTemplate(template) {
    setSelectedTemplateId(template.id);
    setFields((current) => ({
      ...current,
      marketingType: template.name,
      platform: template.category === "Agent Marketing" || template.category === "Recruiting" ? "Instagram" : current.platform
    }));
    setActivePage("Marketing Studio");
  }

  function openAppTile(tile) {
    if (tile.targetView) setBuyerTrackerStartView(tile.targetView);
    if (tile.linkTitle) {
      const link = quickLinks.find((item) => item.title === tile.linkTitle);
      if (link) {
        openQuickLink(link);
        return;
      }
    }
    if (tile.templateId) {
      const template = templates.find((item) => item.id === tile.templateId);
      if (template) {
        openTemplate(template);
        return;
      }
    }
    setActivePage(tile.page);
  }

  function runAiPrompt(promptText) {
    if (promptText.startsWith("Open ")) {
      const target = promptText.replace("Open ", "");
      const tile = launchpadTiles.find((item) => item.title.includes(target) || item.page === target);
      if (tile) openAppTile(tile);
      else setActivePage("Nex Agent Assist");
      return;
    }
    setActivePage("Nex Agent Assist");
  }

  function toggleFocus(id) {
    setFocusDone((current) => ({ ...current, [id]: !current[id] }));
  }

  function dashboardTime(value, fallback = "Live") {
    if (!value) return fallback;
    const parsed = new Date(value);
    if (Number.isNaN(parsed.getTime())) return fallback;
    return parsed.toLocaleString([], { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
  }

  const latest = announcements.slice(0, 5);
  const pinned = announcements.filter((item) => item.pinned || item.urgent).slice(0, 4);
  const featuredTraining = trainingResources.filter((item) => item.featured || item.isNew).slice(0, 4);
  const highlightedTemplates = ["just-listed", "luxury-listing", "modern-open-house", "for-rent", "just-sold", "recruiting-post", "home-value-offer"]
    .map((id) => templates.find((template) => template.id === id))
    .filter(Boolean);
  const openTickets = tickets.filter((ticket) => !["Closed", "Resolved"].includes(ticket.status)).length;
  const waitingTickets = tickets.filter((ticket) => String(ticket.status || "").includes("Waiting")).length;
  const resolvedTickets = tickets.filter((ticket) => ["Closed", "Resolved"].includes(ticket.status)).length;
  const unreadChatCount = Number(chatUnreadTotal || 0);
  const pendingTransactionTasks = buyerNotifications.filter((item) => !item.read).length;
  const newTrainingCount = trainingResources.filter((item) => item.isNew).length;
  const newAnnouncementCount = announcements.filter((item) => item.urgent || item.pinned).length || announcements.length;
  const latestChatText = latestChat?.text || (latestChat?.attachments?.length ? `${latestChat.attachments.length} attachment${latestChat.attachments.length === 1 ? "" : "s"}` : "No chat activity yet.");
  const latestChatTime = latestChat?.createdAt ? dashboardTime(latestChat.createdAt) : "Open chat";
  const trainingProgress = trainingResources.length ? Math.min(92, Math.max(18, Math.round((trainingResources.filter((item) => !item.isNew).length / trainingResources.length) * 100))) : 0;
  const agentInitials = displayName.split(" ").filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase();
  const focusItems = (isAdmin
    ? [
        ["tickets", "Review support tickets", "Support Desk"],
        ["agent-questions", "Check unread agent questions", "Nex Chat"],
        ["leads", "Review new leads", "Nex lite CRM"],
        ["compliance", "Review compliance reminders", "Bulletin Board"],
        ["training", "Check training activity", "NexU / Training Center"],
        ["activity", "Review agent activity", "Admin Panel"]
      ]
    : [
        ["leads", "Follow up with 5 leads", "Nex lite CRM"],
        ["training", "Complete 1 NexU training", "NexU / Training Center"],
        ["deadlines", "Check transaction deadlines", "Buyer Contract Tracker"],
        ["campaign", "Launch 1 marketing campaign", "Marketing Studio"],
        ["chat", "Respond to unread chats", "Nex Chat"],
        ["updates", "Review brokerage updates", "Bulletin Board"]
      ]);
  const focusComplete = focusItems.filter(([id]) => focusDone[id]).length;
  const focusProgress = Math.round((focusComplete / focusItems.length) * 100);

  const metrics = [
    { icon: "Megaphone", label: "New Announcements", value: newAnnouncementCount, status: announcements.some((item) => item.urgent) ? "Urgent" : "Updated", page: "Bulletin Board", tone: "red" },
    { icon: "GraduationCap", label: "New NexU Updates", value: newTrainingCount || trainingResources.length, status: newTrainingCount ? "New" : "Ready", page: "NexU / Training Center", tone: "gold" },
    { icon: "LifeBuoy", label: "Open Support Tickets", value: openTickets, status: openTickets ? "Action Needed" : "Clear", page: "Support Desk", tone: openTickets ? "red" : "dark" },
    { icon: "MessagesSquare", label: "Unread Nex Chat", value: unreadChatCount, status: unreadChatCount ? "Live" : "Synced", page: "Nex Chat", tone: "red" },
    { icon: "UsersRound", label: "Active Leads", value: 0, status: "CRM Ready", page: "Nex lite CRM", tone: "dark", sample: true },
    { icon: "ClipboardCheck", label: "Pending Transaction Tasks", value: pendingTransactionTasks, status: pendingTransactionTasks ? "Due Soon" : "Ready", page: "Buyer Contract Tracker", tone: "gold" },
    { icon: "Sparkles", label: "Marketing Projects", value: projects.length, status: projects.length ? "In Progress" : "Launch", page: "Marketing Studio", tone: "red" },
    { icon: "BarChart3", label: "Training Progress", value: `${trainingProgress}%`, status: trainingResources.length ? "Sample" : "Not Started", page: "NexU / Training Center", tone: "gold", sample: true }
  ];

  const launchpadTiles = [
    { title: "Marketing Studio", icon: "Sparkles", body: "Create campaigns, flyers, cards, emails, SMS, and social assets.", status: "Ready", page: "Marketing Studio" },
    { title: "Nex Agent Assist", icon: "Bot", body: "Ask about contracts, marketing, workflows, and brokerage support.", status: "AI Live", page: "Nex Agent Assist" },
    { title: "Nex Chat", icon: "MessagesSquare", body: "Live agent, admin, and support communication.", status: "Live", page: "Nex Chat" },
    { title: "Nex Lite CRM", icon: "UsersRound", body: "Manage contacts, lead stages, tasks, notes, and follow-ups.", status: "CRM Ready", page: "Nex lite CRM" },
    { title: "Lead Engine", icon: "Target", body: "Create lead campaigns, QR codes, open house capture, and follow-up assets.", status: "Build Leads", page: "Marketing Studio", templateId: "seller-lead-ad" },
    { title: "Production Tracker", icon: "Calculator", body: "Reverse-engineer closings into daily calls and pipeline activity.", status: "Launch", page: "Production Tracker" },
    { title: "Buyer Contract Checklist", icon: "ClipboardCheck", body: "Track buyer deadlines, EMD, inspection, appraisal, and closing tasks.", status: "Ready", page: "Buyer Contract Tracker", targetView: "Create New Transaction" },
    { title: "NexU", icon: "GraduationCap", body: "Watch internal training and continue agent development.", status: "Updated", page: "NexU / Training Center" },
    { title: "Brokermint", icon: "FileCheck2", body: "Open transaction management and file review resources.", status: "External", page: "Quick Links Center", linkTitle: "Brokermint" },
    { title: "SoWork", icon: "Building2", body: "Enter the Nex virtual office and collaboration space.", status: "External", page: "Quick Links Center", linkTitle: "SoWork" },
    { title: "Support Desk", icon: "LifeBuoy", body: "Submit or review brokerage, tech, compliance, and marketing requests.", status: openTickets ? "Action Needed" : "Ready", page: "Support Desk" },
    { title: "Template Library", icon: "LayoutTemplate", body: "Browse Nex-branded flyers, social graphics, cards, and campaigns.", status: "Ready", page: "Template Library" }
  ];

  const activityItems = [
    latestChat && { type: "Chat", tone: "live", user: latestChat.senderName || latestChat.senderEmail || "Nex Chat", text: latestChatText, time: latestChatTime, page: "Nex Chat" },
    ...announcements.slice(0, 2).map((item) => ({ type: item.urgent ? "Urgent" : item.category || "Bulletin", tone: item.urgent ? "urgent" : "normal", user: "Brokerage Bulletin", text: item.title, time: dashboardTime(item.createdAt, "Updated"), page: "Bulletin Board" })),
    ...featuredTraining.slice(0, 2).map((item) => ({ type: "NexU", tone: "new", user: "Training", text: `New training uploaded - ${item.title}`, time: dashboardTime(item.createdAt, "New"), page: "NexU / Training Center" })),
    ...projects.slice(0, 2).map((project) => ({ type: "Marketing", tone: "normal", user: currentUser?.name || "Marketing Studio", text: `${project.title || "New project"} saved`, time: dashboardTime(project.updatedAt || project.createdAt, "Recent"), page: "Saved Projects" })),
    openTickets > 0 && { type: "Support", tone: "urgent", user: "Support Desk", text: `${openTickets} ticket${openTickets === 1 ? "" : "s"} need review`, time: "Action Needed", page: "Support Desk" },
    { type: "Compliance", tone: "normal", user: "Nex Central", text: "Advertising reminder active before publishing marketing.", time: "Live", page: "Bulletin Board" }
  ].filter(Boolean).slice(0, 8);

  const pulseCards = [
    ["Agent activity today", isAdmin ? "Brokerage view" : "Personal view", "Sample until analytics are connected"],
    ["Leads generated this week", "CRM ready", "Connect CRM activity history next"],
    ["Marketing projects created", projects.length, projects.length ? "Live data" : "No saved work yet"],
    ["Training modules completed", `${trainingProgress}%`, "Sample progress until per-user tracking"],
    ["Support response status", openTickets ? `${openTickets} open` : "Clear", waitingTickets ? `${waitingTickets} waiting` : "No waiting items"],
    ["Production activity", "Ready", "Uses Production Tracker local activity"],
    ["CRM follow-ups due", "CRM Ready", "Will sync from CRM tasks next"]
  ];

  return (
    <section className="nex-dashboard">
      <section className="nex-hero">
        <div className="nex-hero-glow" />
        <div className="nex-hero-content">
          <div className="nex-hero-copy">
            <p className="eyebrow">Nex Central</p>
            <h1>Welcome back, {displayName}.</h1>
            <p>Your Nex operating system is live - one connected hub for leads, marketing, training, transactions, support, and brokerage growth.</p>
            <div className="nex-status-row">
              {["System Online", "Agent Support Active", "Nex Chat Live", "Marketing Studio Ready", "NexU Updated", "CRM Synced"].map((status) => (
                <span key={status}><i />{status}</span>
              ))}
            </div>
            <div className="nex-hero-actions">
              <button className="btn primary" onClick={() => setActivePage("Marketing Studio")}><Icon name="Sparkles" /><span>Launch Marketing Studio</span></button>
              <button className="btn light" onClick={() => setActivePage("Nex Agent Assist")}><Icon name="Bot" /><span>Ask Nex Agent Assist</span></button>
              <button className="btn light" onClick={() => openAppTile({ page: "Marketing Studio", templateId: "seller-lead-ad" })}><Icon name="Target" /><span>Create Lead Campaign</span></button>
              <button className="btn light" onClick={() => setActivePage("Nex Chat")}><Icon name="MessagesSquare" /><span>Open Nex Chat</span></button>
              <button className="btn light" onClick={() => setActivePage("Support Desk")}><Icon name="LifeBuoy" /><span>Submit Support Ticket</span></button>
              <button className="btn light" onClick={() => { setBuyerTrackerStartView("Create New Transaction"); setActivePage("Buyer Contract Tracker"); }}><Icon name="ClipboardCheck" /><span>Start Transaction Checklist</span></button>
            </div>
          </div>
          <aside className="nex-hero-agent">
            <Wordmark brand={brand} variant="dark" />
            <div className="nex-avatar">{agentInitials || "NX"}</div>
            <strong>{displayName}</strong>
            <span>{currentUser?.role || "Agent"} - {todayLabel}</span>
          </aside>
        </div>
      </section>

      <section className="nex-ai-bar">
        <Icon name="Sparkles" />
        <button onClick={() => setActivePage("Nex Agent Assist")}>Ask Nex anything... contracts, marketing, leads, support, training, transactions.</button>
        <div>
          {["What should I work on today?", "Create a seller lead campaign", "Help me with an under-contract checklist", "Draft a follow-up text", "Explain my next transaction step", "Open Marketing Studio", "Show my support tickets"].map((promptText) => (
            <button key={promptText} onClick={() => runAiPrompt(promptText)}>{promptText}</button>
          ))}
        </div>
      </section>

      <section className="nex-metrics-row">
        {metrics.map((metric) => (
          <DashboardMetricCard key={metric.label} metric={metric} onClick={() => setActivePage(metric.page)} />
        ))}
      </section>

      <section className="nex-dashboard-grid">
        <article className="nex-widget nex-focus-card">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Today's Nex Focus</p>
              <h2>{focusComplete}/{focusItems.length} complete</h2>
            </div>
            <span>{focusProgress}%</span>
          </div>
          <div className="nex-progress-track"><i style={{ width: `${focusProgress}%` }} /></div>
          <div className="nex-focus-list">
            {focusItems.map(([id, label, page]) => (
              <label key={id} className={focusDone[id] ? "complete" : ""}>
                <input type="checkbox" checked={Boolean(focusDone[id])} onChange={() => toggleFocus(id)} />
                <span>{label}</span>
                <button type="button" onClick={() => setActivePage(page)}>Open</button>
              </label>
            ))}
          </div>
        </article>

        <article className="nex-widget nex-activity-card">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Live Nex Activity</p>
              <h2>Brokerage pulse</h2>
            </div>
            <span className="live-badge"><i />Live</span>
          </div>
          <div className="nex-activity-feed">
            {activityItems.map((item, index) => (
              <button key={`${item.type}-${index}`} onClick={() => setActivePage(item.page)}>
                <em className={item.tone}>{item.type}</em>
                <strong>{item.user}</strong>
                <span>{item.text}</span>
                <small>{item.time}</small>
              </button>
            ))}
          </div>
        </article>
      </section>

      <section className="nex-launchpad">
        <div className="nex-section-head">
          <div>
            <p className="eyebrow">Nex Launchpad</p>
            <h2>Open your agent tech stack</h2>
          </div>
          <button className="btn secondary" onClick={() => setActivePage("Quick Links Center")}><Icon name="Grid3X3" /><span>All Links</span></button>
        </div>
        <div className="nex-launchpad-grid">
          {launchpadTiles.map((tile) => (
            <button key={tile.title} onClick={() => openAppTile(tile)}>
              <Icon name={tile.icon} />
              <span>{tile.status}</span>
              <strong>{tile.title}</strong>
              <p>{tile.body}</p>
            </button>
          ))}
        </div>
      </section>

      <section className="nex-dashboard-grid three">
        <article className="nex-widget nex-pulse-card">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Nex Pulse</p>
              <h2>Platform analytics</h2>
            </div>
            <span>Mixed live + sample</span>
          </div>
          <div className="nex-pulse-grid">
            {pulseCards.map(([label, value, note]) => (
              <div key={label}>
                <span>{label}</span>
                <strong>{value}</strong>
                <em>{note}</em>
              </div>
            ))}
          </div>
        </article>

        <article className="nex-widget nex-chat-card" onClick={() => setActivePage("Nex Chat")}>
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Nex Chat Preview</p>
              <h2>{latestChat ? latestChat.senderName || latestChat.senderEmail || "Nex Chat" : "No messages yet"}</h2>
            </div>
            <span className="live-badge"><i />Live</span>
          </div>
          <p>{latestChatText}</p>
          <div className="nex-chat-meta">
            <span>{latestChat?.channelName || "#general"}</span>
            <strong>{unreadChatCount ? `${unreadChatCount} unread` : "Open Chat"}</strong>
            <em>{latestChatTime}</em>
          </div>
        </article>

        <article className="nex-widget support-snapshot">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Support Center Snapshot</p>
              <h2>{openTickets} open</h2>
            </div>
            {openTickets > 0 && <span className="urgent-pill">Action Needed</span>}
          </div>
          <div className="support-snapshot-grid">
            <div><strong>{openTickets}</strong><span>Open tickets</span></div>
            <div><strong>{waitingTickets}</strong><span>Waiting</span></div>
            <div><strong>{resolvedTickets}</strong><span>Resolved</span></div>
          </div>
          <button className="btn primary" onClick={() => setActivePage("Support Desk")}><Icon name="LifeBuoy" /><span>Open Support Desk</span></button>
        </article>
      </section>

      <section className="nex-dashboard-grid three lower">
        <article className="nex-widget brokerage-bulletin">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Brokerage Bulletin</p>
              <h2>Latest updates</h2>
            </div>
            <button className="icon-btn" onClick={() => setActivePage("Bulletin Board")} title="Open bulletin board"><Icon name="ArrowRight" /></button>
          </div>
          <div className="bulletin-card-list">
            {(pinned.length ? pinned : latest).slice(0, 4).map((item) => (
              <button key={item.id} className={classNames(item.urgent && "urgent")} onClick={() => setActivePage("Bulletin Board")}>
                <span>{item.urgent ? "Urgent" : item.category || "General"}</span>
                <strong>{item.title}</strong>
                <p>{item.body}</p>
                <em>{dashboardTime(item.createdAt, item.pinned ? "Pinned" : "Updated")}</em>
              </button>
            ))}
          </div>
        </article>

        <article className="nex-widget nexu-progress-card">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">NexU Progress</p>
              <h2>{trainingProgress}% learning path</h2>
            </div>
            <span>{newTrainingCount ? `${newTrainingCount} New` : "Ready"}</span>
          </div>
          <div className="nex-progress-track"><i style={{ width: `${trainingProgress}%` }} /></div>
          <div className="nexu-mini-list">
            {(featuredTraining.length ? featuredTraining : trainingResources).slice(0, 3).map((item) => (
              <button key={item.id} onClick={() => setActivePage("NexU / Training Center")}>
                <span>{normalizeTrainingCategory(item.category)}{item.isNew ? " - New" : ""}</span>
                <strong>{item.title}</strong>
              </button>
            ))}
          </div>
          <button className="btn secondary" onClick={() => setActivePage("NexU / Training Center")}><Icon name="PlayCircle" /><span>Continue Learning</span></button>
        </article>

        <article className="nex-widget marketing-momentum">
          <div className="nex-widget-head">
            <div>
              <p className="eyebrow">Marketing Momentum</p>
              <h2>{projects.length} saved projects</h2>
            </div>
            <button className="btn secondary" onClick={() => setActivePage("Marketing Studio")}><Icon name="Sparkles" /><span>Start Design</span></button>
          </div>
          <div className="momentum-template-row">
            {(highlightedTemplates.length ? highlightedTemplates : templates).slice(0, 7).map((template) => (
              <button key={template.id} onClick={() => openTemplate(template)} style={{ "--accent": template.accent || brand.primaryColor }}>
                <span>{template.badge || "Template"}</span>
                <strong>{template.name}</strong>
              </button>
            ))}
          </div>
          <div className="recent-project-mini">
            {projects.slice(0, 3).map((project) => (
              <button key={project.id} onClick={() => setActivePage("Saved Projects")}>
                <span>{project.fields?.marketingType || "Marketing"}</span>
                <strong>{project.title}</strong>
              </button>
            ))}
            {!projects.length && <p>Start in Marketing Studio and your saved campaigns will appear here.</p>}
          </div>
        </article>
      </section>
    </section>
  );
}

function DashboardMetricCard({ metric, onClick }) {
  return (
    <button className={classNames("nex-metric-card", metric.tone, metric.sample && "sample")} onClick={onClick}>
      <span className="metric-icon"><Icon name={metric.icon} /></span>
      <span className="metric-status">{metric.status}</span>
      <strong>{metric.value}</strong>
      <em>{metric.label}</em>
    </button>
  );
}

function MarketingStudioHub({
  setActivePage,
  templates,
  brand,
  selectedTemplateId,
  setSelectedTemplateId,
  fields,
  setFields,
  flyerCopy,
  setFlyerCopy,
  propertyPhotos,
  setPropertyPhotos,
  agentHeadshot,
  setAgentHeadshot,
  flyerRef,
  output,
  prompt,
  setPrompt,
  isGenerating,
  onGenerate,
  onQuickAction,
  onSaveProject,
  onDuplicateProject,
  onDownloadPdf,
  onDownloadPng,
  onDownloadJpg,
  onPrint,
  onSharePlatform,
  onSaveAsTemplate,
  projects,
  loadProject,
  setToast,
  projectTitle,
  setProjectTitle,
  saveStatus
}) {
  const [activeTool, setActiveTool] = useState("Templates");
  const [templateQuery, setTemplateQuery] = useState("");
  const [templateCategory, setTemplateCategory] = useState("All");
  const [selectedElement, setSelectedElement] = useState("headline");
  const [selectedElements, setSelectedElements] = useState(["headline"]);
  const [history, setHistory] = useState([]);
  const [future, setFuture] = useState([]);
  const selectedTemplate = templates.find((template) => template.id === selectedTemplateId) || templates[0] || {};
  const selectedFormat = getCreativeFormat(flyerCopy.canvasFormat);
  const categories = ["All", ...Array.from(new Set(templates.map((template) => template.category || "Custom")))];
  const templatesForSelectedFormat = templates.filter((template) => getRecommendedFormatForTemplate(template) === selectedFormat.id);
  const templatePool = templatesForSelectedFormat.length ? templatesForSelectedFormat : templates;
  const filteredTemplates = sortStudioTemplates(templatePool.filter((template) => {
    const search = `${template.name} ${template.category} ${template.headline} ${template.badge}`.toLowerCase();
    const matchesSearch = !templateQuery || search.includes(templateQuery.toLowerCase());
    const matchesCategory = templateCategory === "All" || template.category === templateCategory;
    return matchesSearch && matchesCategory;
  }), templateCategory, templateQuery);

  function snapshot() {
    return { fields, flyerCopy, selectedTemplateId, propertyPhotos, agentHeadshot };
  }

  function restore(state) {
    if (!state) return;
    setFields(state.fields || defaultFields);
    setFlyerCopy({ ...defaultFlyerCopy, ...(state.flyerCopy || {}) });
    setSelectedTemplateId(state.selectedTemplateId || "just-listed");
    setPropertyPhotos(state.propertyPhotos || []);
    setAgentHeadshot(state.agentHeadshot || null);
  }

  function selectStudioElement(id, event) {
    const additive = Boolean(event?.shiftKey || event?.ctrlKey || event?.metaKey);
    setSelectedElement(id);
    setSelectedElements((current) => {
      if (!additive) return [id];
      if (current.includes(id)) {
        const next = current.filter((item) => item !== id);
        return next.length ? next : [id];
      }
      return [...current, id];
    });
  }

  function setSingleSelectedElement(id) {
    setSelectedElement(id);
    setSelectedElements([id]);
  }

  function remember() {
    setHistory((current) => [...current.slice(-14), snapshot()]);
    setFuture([]);
  }

  function undo() {
    if (!history.length) return;
    const previous = history[history.length - 1];
    setFuture((current) => [snapshot(), ...current].slice(0, 15));
    setHistory((current) => current.slice(0, -1));
    restore(previous);
    setToast("Undid last studio edit.");
  }

  function redo() {
    if (!future.length) return;
    const next = future[0];
    setHistory((current) => [...current, snapshot()].slice(-15));
    setFuture((current) => current.slice(1));
    restore(next);
    setToast("Redid studio edit.");
  }

  function updateField(name, value) {
    remember();
    setFields((current) => ({ ...current, [name]: value }));
  }

  function updateFlyer(name, value) {
    remember();
    setFlyerCopy((current) => ({ ...current, [name]: value }));
  }

  function updateFlyerLive(name, value) {
    setFlyerCopy((current) => ({ ...current, [name]: value }));
  }

  function updateCanvasFormat(formatId) {
    remember();
    setFlyerCopy((current) => ({
      ...current,
      canvasFormat: formatId,
      ...getFormatDefaults(formatId)
    }));
    setSingleSelectedElement(formatId === "business-card" ? "agent" : "headline");
  }

  function selectTemplate(template, requestedFormatId = "") {
    remember();
    const formatId = requestedFormatId || getRecommendedFormatForTemplate(template);
    setSelectedTemplateId(template.id);
    setFields((current) => ({
      ...current,
      marketingType: template.name,
      platform: formatId === "instagram-reel" ? "Instagram Reel / Story" : formatId === "instagram-square" ? "Instagram" : formatId === "facebook-post" ? "Facebook" : current.platform
    }));
    setFlyerCopy((current) => ({
      ...current,
      headline: template.headline || current.headline,
      badge: template.badge || current.badge,
      layout: template.style || current.layout,
      finish: template.style === "luxury" ? "black-luxury" : current.finish,
      canvasFormat: formatId,
      ...getFormatDefaults(formatId)
    }));
    setSingleSelectedElement(formatId === "business-card" ? "agent" : "headline");
  }

  function startFromPreset(item) {
    const template = templates.find((candidate) => candidate.id === item.templateId) || selectedTemplate;
    selectTemplate(template, item.formatId);
    setActiveTool(item.nextTool || "Templates");
    setToast(`${item.title} template loaded.`);
  }

  async function replacePhoto(event, index = 0) {
    const file = event.target.files && event.target.files[0];
    if (!file) return;
    remember();
    const upload = await fileToDataUrl(file);
    setPropertyPhotos((current) => {
      const next = [...current];
      next[index] = upload;
      return next.filter(Boolean).slice(0, 12);
    });
    event.target.value = "";
  }

  function removePhoto(index = 0) {
    remember();
    setPropertyPhotos((current) => current.filter((_, itemIndex) => itemIndex !== index));
    setToast("Photo removed from the design.");
  }

  async function onPhotoUpload(event) {
    const files = Array.from((event.target && event.target.files) || event.files || []);
    if (!files.length) return;
    remember();
    const uploads = await Promise.all(files.map(fileToDataUrl));
    setPropertyPhotos((current) => [...current, ...uploads].slice(0, 12));
  }

  async function onHeadshotUpload(event) {
    const file = event.target.files && event.target.files[0];
    if (!file) return;
    remember();
    setAgentHeadshot(await fileToDataUrl(file));
    event.target.value = "";
  }

  async function onBackgroundUpload(event) {
    const file = event.target.files && event.target.files[0];
    if (!file) return;
    remember();
    const upload = await fileToDataUrl(file);
    setFlyerCopy((current) => ({
      ...current,
      backgroundImage: upload.dataUrl,
      backgroundImageName: upload.name || "Uploaded background"
    }));
    event.target.value = "";
  }

  function assignPhoto(index, slot) {
    const photo = propertyPhotos[index];
    if (!photo) return;
    if (slot === "background") {
      remember();
      setFlyerCopy((current) => ({
        ...current,
        backgroundImage: photo.dataUrl,
        backgroundImageName: photo.name || `Photo ${index + 1}`
      }));
      setToast("Photo assigned as the design background.");
      return;
    }
    remember();
    setPropertyPhotos((current) => {
      const next = current.filter((_, itemIndex) => itemIndex !== index);
      const targetIndex = slot === "hero" ? 0 : slot === "secondary-1" ? 1 : slot === "secondary-2" ? 2 : 3;
      next.splice(Math.min(targetIndex, next.length), 0, photo);
      return next;
    });
    setToast(`Photo assigned to ${slot.replace("-", " ")}.`);
  }

  function applyCtaBlock(label) {
    updateField("cta", label);
    updateFlyer("cta", label);
  }

  function assistantPrompt(action) {
    const base = output?.primary || flyerCopy.body || `${fields.marketingType} for ${fields.propertyAddress || "a Nex Realty campaign"}`;
    return `${action}\n\nCurrent copy:\n${base}`;
  }

  return (
    <section className="canva-studio">
      <header className="studio-top-toolbar">
        <div className="project-title-wrap">
          <p className="eyebrow">Nex Marketing Studio</p>
          <input value={projectTitle} onChange={(event) => setProjectTitle(event.target.value)} placeholder="Untitled Nex marketing project" />
          <span>{saveStatus}</span>
        </div>
        <div className="studio-toolbar-actions">
          <button className="btn secondary" onClick={undo} disabled={!history.length}><Icon name="Undo2" /><span>Undo</span></button>
          <button className="btn secondary" onClick={redo} disabled={!future.length}><Icon name="Redo2" /><span>Redo</span></button>
          <button className="btn secondary" onClick={onDuplicateProject}><Icon name="CopyPlus" /><span>Duplicate</span></button>
          <button className="btn secondary" onClick={onSaveAsTemplate}><Icon name="BadgePlus" /><span>Save Template</span></button>
          <button className="btn primary" onClick={onSaveProject}><Icon name="Save" /><span>Save</span></button>
        </div>
      </header>

      <section className="studio-start-strip" aria-label="Start a marketing design">
        <div className="studio-start-copy">
          <p className="eyebrow">Start here</p>
          <strong>What are you making?</strong>
          <span>Pick a finished size first, then customize the design.</span>
        </div>
        <div className="studio-start-actions">
          {creativeFormatOptions.map((format) => (
            <button key={format.id} onClick={() => { updateCanvasFormat(format.id); setActiveTool("Templates"); setToast(`${format.name} canvas selected.`); }} className={flyerCopy.canvasFormat === format.id ? "selected" : ""}>
              <Icon name={format.icon} />
              <span>
                <strong>{format.name}</strong>
                <em>{format.exportLabel}</em>
              </span>
            </button>
          ))}
        </div>
      </section>

      <StudioExportStrip
        selectedFormat={selectedFormat}
        onDownloadPng={onDownloadPng}
        onDownloadPdf={onDownloadPdf}
        onDownloadJpg={onDownloadJpg}
        onPrint={onPrint}
        onSharePlatform={onSharePlatform}
      />

      <div className="canva-workspace">
        <aside className="studio-left-panel">
          <nav className="studio-tool-rail" aria-label="Marketing Studio tools">
            {marketingStudioToolTabs.map(([label, icon]) => (
              <button key={label} className={activeTool === label ? "active" : ""} onClick={() => setActiveTool(label)} title={label}>
                <Icon name={icon} />
                <span>{label}</span>
              </button>
            ))}
          </nav>
          <div className="studio-tool-panel">
            {activeTool === "Edit" ? (
              <StudioSettingsPanel
                selectedElement={selectedElement}
                setSelectedElement={setSingleSelectedElement}
                fields={fields}
                updateField={updateField}
                flyerCopy={flyerCopy}
                updateFlyer={updateFlyer}
                brand={brand}
                selectedTemplate={selectedTemplate}
                leadCtaBlocks={leadCtaBlocks}
                applyCtaBlock={applyCtaBlock}
                output={output}
                prompt={prompt}
                setPrompt={setPrompt}
                isGenerating={isGenerating}
                onGenerate={onGenerate}
                onQuickAction={(action) => onQuickAction(assistantPrompt(action))}
                setActivePage={setActivePage}
              />
            ) : activeTool === "Export" ? (
              <StudioExportPanel
                selectedFormat={selectedFormat}
                onDownloadPng={onDownloadPng}
                onDownloadPdf={onDownloadPdf}
                onDownloadJpg={onDownloadJpg}
                onPrint={onPrint}
                onSharePlatform={onSharePlatform}
              />
            ) : (
              <StudioToolPanel
                activeTool={activeTool}
                templates={filteredTemplates}
                allCategories={categories}
                templateQuery={templateQuery}
                setTemplateQuery={setTemplateQuery}
                templateCategory={templateCategory}
                setTemplateCategory={setTemplateCategory}
                selectedTemplateId={selectedTemplateId}
                onSelectTemplate={selectTemplate}
                selectedFormat={selectedFormat}
                brand={brand}
                propertyPhotos={propertyPhotos}
                setPropertyPhotos={setPropertyPhotos}
                onPhotoUpload={onPhotoUpload}
                onHeadshotUpload={onHeadshotUpload}
                agentHeadshot={agentHeadshot}
                assignPhoto={assignPhoto}
                setActivePage={setActivePage}
                projects={projects}
                loadProject={loadProject}
                fields={fields}
                updateField={updateField}
                flyerCopy={flyerCopy}
                updateFlyer={updateFlyer}
              />
            )}
          </div>
        </aside>

        <main className="studio-canvas-panel">
          <div className="canvas-floating-bar">
            <div>
              <strong>{selectedTemplate.name || "Nex Template"}</strong>
              <span>{selectedFormat.name} - {selectedFormat.exportLabel}</span>
            </div>
            <div className="canvas-format-pills">
              {creativeFormatOptions.map((format) => (
                <button key={format.id} className={flyerCopy.canvasFormat === format.id ? "selected" : ""} onClick={() => updateCanvasFormat(format.id)} title={format.name}>
                  <Icon name={format.icon} size={14} />
                  <span>{format.name}</span>
                </button>
              ))}
            </div>
          </div>
          <FlyerPreview
            brand={brand}
            fields={fields}
            templates={templates}
            selectedTemplateId={selectedTemplateId}
            flyerCopy={flyerCopy}
            propertyPhotos={propertyPhotos}
            agentHeadshot={agentHeadshot}
            flyerRef={flyerRef}
            onSelectElement={selectStudioElement}
            selectedElement={selectedElement}
            selectedElements={selectedElements}
            onUpdateField={updateField}
            onUpdateFlyer={updateFlyer}
            onUpdateFlyerLive={updateFlyerLive}
            onReplacePhoto={replacePhoto}
            onRemovePhoto={removePhoto}
            onHeadshotUpload={onHeadshotUpload}
          />
          <CanvasInspectorPanel
            selectedElement={selectedElement}
            selectedElements={selectedElements}
            setSelectedElement={setSelectedElement}
            setSelectedElements={setSelectedElements}
            fields={fields}
            updateField={updateField}
            flyerCopy={flyerCopy}
            updateFlyer={updateFlyer}
            brand={brand}
            selectedFormat={selectedFormat}
            onBackgroundUpload={onBackgroundUpload}
          />
        </main>
      </div>
    </section>
  );
}

function StudioExportStrip({ selectedFormat, onDownloadPng, onDownloadPdf, onDownloadJpg, onPrint, onSharePlatform }) {
  return (
    <section className="studio-export-strip" aria-label="Marketing Studio export actions">
      <div>
        <p className="eyebrow">Ready to use</p>
        <strong>{selectedFormat.name}</strong>
        <span>{selectedFormat.exportLabel} output with Nex branding applied.</span>
      </div>
      <div className="studio-export-actions">
        <button className="btn dark" onClick={onDownloadPng}><Icon name="ImageDown" /><span>PNG</span></button>
        <button className="btn secondary" onClick={onDownloadPdf}><Icon name="Download" /><span>PDF</span></button>
        <button className="btn secondary" onClick={onDownloadJpg}><Icon name="Image" /><span>JPG</span></button>
        <button className="btn secondary" onClick={onPrint}><Icon name="Printer" /><span>Print</span></button>
        <button className="btn primary" onClick={() => onSharePlatform("native")}><Icon name="Share2" /><span>Share</span></button>
      </div>
    </section>
  );
}

function StudioExportPanel({ selectedFormat, onDownloadPng, onDownloadPdf, onDownloadJpg, onPrint, onSharePlatform }) {
  const socialActions = [
    ["facebook", "Facebook", "Share2"],
    ["instagram", "Instagram", "Camera"],
    ["x", "X", "AtSign"],
    ["linkedin", "LinkedIn", "Linkedin"]
  ];

  return (
    <div className="studio-tool-content">
      <div className="studio-panel-head">
        <p className="eyebrow">Export</p>
        <h3>Download, print, or share</h3>
      </div>

      <div className="settings-card export-primary-card">
        <p className="studio-label">Current design</p>
        <strong>{selectedFormat.name}</strong>
        <span>{selectedFormat.exportLabel}. Download the final asset first when posting to social or sending to print.</span>
      </div>

      <div className="studio-export-grid">
        <button onClick={onDownloadPng}><Icon name="ImageDown" /><span>Download PNG</span></button>
        <button onClick={onDownloadPdf}><Icon name="Download" /><span>Download PDF</span></button>
        <button onClick={onDownloadJpg}><Icon name="Image" /><span>Download JPG</span></button>
        <button onClick={onPrint}><Icon name="Printer" /><span>Print with VistaPrint</span></button>
      </div>

      <div className="settings-card">
        <p className="studio-label">Social handoff</p>
        <div className="studio-social-grid">
          {socialActions.map(([platform, label, icon]) => (
            <button key={platform} onClick={() => onSharePlatform(platform)}>
              <Icon name={icon} />
              <span>{label}</span>
            </button>
          ))}
        </div>
        <small className="studio-note">
          Nex Central copies your caption and opens the network. Some platforms require you to upload the exported image from your account before posting.
        </small>
      </div>
    </div>
  );
}

function StudioToolPanel({
  activeTool,
  templates,
  allCategories,
  templateQuery,
  setTemplateQuery,
  templateCategory,
  setTemplateCategory,
  selectedTemplateId,
  onSelectTemplate,
  selectedFormat,
  brand,
  propertyPhotos,
  setPropertyPhotos,
  onPhotoUpload,
  onHeadshotUpload,
  agentHeadshot,
  assignPhoto,
  setActivePage,
  projects,
  loadProject,
  fields,
  updateField,
  flyerCopy,
  updateFlyer
}) {
  if (activeTool === "Templates") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head">
          <p className="eyebrow">Templates</p>
          <h3>Start from a Nex design</h3>
        </div>
        <div className="studio-size-note">
          <Icon name={selectedFormat?.icon || "Ruler"} size={14} />
          <span>Showing templates for {selectedFormat?.name || "selected size"} ({selectedFormat?.exportLabel || "current format"}).</span>
        </div>
        <input className="studio-search" value={templateQuery} onChange={(event) => setTemplateQuery(event.target.value)} placeholder="Search templates" />
        <div className="studio-filter-row">
          {allCategories.map((category) => (
            <button key={category} className={templateCategory === category ? "selected" : ""} onClick={() => setTemplateCategory(category)}>{category}</button>
          ))}
        </div>
        <div className="studio-template-browser">
          {templates.map((template) => (
            <button key={template.id} className={classNames("studio-template-preview", selectedTemplateId === template.id && "selected")} onClick={() => onSelectTemplate(template)} style={{ "--accent": template.accent || brand.primaryColor }}>
              <MiniTemplatePreview template={template} brand={brand} />
              <strong>{template.name}</strong>
              <span>{template.category} - {getCreativeFormat(getRecommendedFormatForTemplate(template)).name}</span>
            </button>
          ))}
        </div>
      </div>
    );
  }

  if (activeTool === "Brand Kit") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">Brand Kit</p><h3>Nex Realty defaults</h3></div>
        <div className="brand-kit-card dark"><Wordmark brand={brand} variant="dark" /><span>Dark background logo</span></div>
        <div className="brand-kit-card light"><Wordmark brand={brand} variant="light" /><span>Light background logo</span></div>
        <div className="brand-swatch-row">
          {[brand.primaryColor, brand.black, brand.white, brand.charcoal, brand.gold].map((color) => (
            <button key={color} style={{ background: color }} onClick={() => updateFlyer("accentColor", color)} title={color} />
          ))}
        </div>
        <div className="brand-font-list">
          {brandFontPairings.map((font) => <span key={font}>{font}</span>)}
        </div>
        <div className="tagline-list">
          {brandTaglines.map((tagline) => (
            <button key={tagline} onClick={() => updateFlyer("cta", tagline)}>{tagline}</button>
          ))}
        </div>
      </div>
    );
  }

  if (activeTool === "Uploads" || activeTool === "Photos") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">{activeTool}</p><h3>Media tray</h3></div>
        <FileDrop label="Upload property photos" multiple onChange={onPhotoUpload} count={propertyPhotos.length} />
        <FileDrop label="Upload agent headshot" onChange={onHeadshotUpload} preview={agentHeadshot?.dataUrl} />
        <div className="media-assignment-grid">
          {propertyPhotos.map((photo, index) => (
            <article key={`${photo.name}-${index}`}>
              <img src={photo.dataUrl} alt={photo.name} />
              <strong>{photo.name || `Photo ${index + 1}`}</strong>
              <div>
                <button onClick={() => assignPhoto(index, "hero")}>Hero</button>
                <button onClick={() => assignPhoto(index, "secondary-1")}>2</button>
                <button onClick={() => assignPhoto(index, "secondary-2")}>3</button>
                <button onClick={() => assignPhoto(index, "background")}>Background</button>
                <button onClick={() => setPropertyPhotos((current) => current.filter((_, itemIndex) => itemIndex !== index))}>Remove</button>
              </div>
            </article>
          ))}
        </div>
        <StockPhotoTray onAddStockPhoto={(photo) => {
          const image = { name: `${photo.title}.svg`, type: "image/svg+xml", source: "Nex Studio Stock", dataUrl: photo.dataUrl };
          setPropertyPhotos((current) => [image, ...current].slice(0, 12));
        }} />
      </div>
    );
  }

  if (activeTool === "Text") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">Text</p><h3>Campaign copy</h3></div>
        <div className="text-preset-grid">
          <button onClick={() => updateFlyer("headline", flyerCopy.headline || "JUST LISTED")}><Icon name="Heading1" size={14} /><span>Add heading</span></button>
          <button onClick={() => updateFlyer("badge", flyerCopy.badge || "New Listing")}><Icon name="Badge" size={14} /><span>Add badge</span></button>
          <button onClick={() => updateFlyer("body", flyerCopy.body || "Add polished listing copy here.")}><Icon name="AlignLeft" size={14} /><span>Add body</span></button>
          <button onClick={() => updateFlyer("cta", flyerCopy.cta || "Schedule Your Tour Today")}><Icon name="MousePointerClick" size={14} /><span>Add CTA</span></button>
        </div>
        <Field label="Headline" value={flyerCopy.headline} onChange={(value) => updateFlyer("headline", value)} />
        <Field label="Badge" value={flyerCopy.badge} onChange={(value) => updateFlyer("badge", value)} />
        <Field label="Body" value={flyerCopy.body} onChange={(value) => updateFlyer("body", value)} as="textarea" />
        <Field label="CTA" value={flyerCopy.cta} onChange={(value) => updateFlyer("cta", value)} />
        <Field label="Disclaimer" value={flyerCopy.footer} onChange={(value) => updateFlyer("footer", value)} as="textarea" />
      </div>
    );
  }

  if (activeTool === "Elements") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">Elements</p><h3>Visibility and QR</h3></div>
        {[
          ["showPrice", "Price section"],
          ["showStats", "Beds / baths / sqft"],
          ["showAgent", "Agent footer"],
          ["showDisclaimer", "Compliance footer"],
          ["showQr", "QR code block"]
        ].map(([field, label]) => (
          <label className="toggle-row" key={field}>
            <input type="checkbox" checked={flyerCopy[field] !== false} onChange={(event) => updateFlyer(field, event.target.checked)} />
            <span>{label}</span>
          </label>
        ))}
        <Field label="QR / lead capture URL" value={flyerCopy.qrUrl || ""} onChange={(value) => updateFlyer("qrUrl", value)} placeholder="https://..." />
        <ChoiceGrid label="Photo fit" value={flyerCopy.heroImageFit || "Cover"} options={headshotFitOptions.map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("heroImageFit", value)} />
      </div>
    );
  }

  if (activeTool === "QR Code") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">QR Code</p><h3>Add a scan action</h3></div>
        <label className="toggle-row">
          <input type="checkbox" checked={flyerCopy.showQr === true} onChange={(event) => updateFlyer("showQr", event.target.checked)} />
          <span>Show QR block on the design</span>
        </label>
        <Field label="QR destination URL" value={flyerCopy.qrUrl || ""} onChange={(value) => updateFlyer("qrUrl", value)} placeholder="https://agent-site.com/listing" />
        <div className="qr-purpose-grid">
          {[
            ["Agent website", flyerCopy.agentWebsite || "https://mynexrealty.com"],
            ["Property page", "https://mynexrealty.com/property"],
            ["Open house sign-in", "https://mynexrealty.com/open-house"],
            ["Recruiting page", "https://mynexrealty.com/careers"]
          ].map(([label, url]) => (
            <button key={label} onClick={() => updateFlyer("qrUrl", url)}>
              <Icon name="QrCode" size={15} />
              <span>{label}</span>
            </button>
          ))}
        </div>
        <small className="studio-note">MVP note: the preview shows a QR placeholder and saves the destination URL. A generated scannable QR image can be added in the next pass.</small>
      </div>
    );
  }

  if (activeTool === "Agent Info") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">Agent Info</p><h3>Personalize</h3></div>
        <FileDrop label="Upload agent headshot" onChange={onHeadshotUpload} preview={agentHeadshot?.dataUrl} />
        <Field label="Agent name" value={fields.agentName} onChange={(value) => updateField("agentName", value)} />
        <Field label="Agent phone" value={fields.agentPhone} onChange={(value) => updateField("agentPhone", value)} />
        <Field label="Agent email" value={fields.agentEmail} onChange={(value) => updateField("agentEmail", value)} />
        <Field label="Agent title" value={flyerCopy.agentTitle || ""} onChange={(value) => updateFlyer("agentTitle", value)} placeholder="Real Estate Professional" />
        <Field label="Agent website" value={flyerCopy.agentWebsite || ""} onChange={(value) => updateFlyer("agentWebsite", value)} placeholder="https://..." />
        <Field label="Social account" value={flyerCopy.agentSocial || ""} onChange={(value) => updateFlyer("agentSocial", value)} placeholder="@nexrealty" />
        <Field label="License number" value={flyerCopy.agentLicense || ""} onChange={(value) => updateFlyer("agentLicense", value)} />
      </div>
    );
  }

  if (activeTool === "Property Info") {
    return (
      <div className="studio-tool-content">
        <div className="studio-panel-head"><p className="eyebrow">Property Info</p><h3>Listing details</h3></div>
        <Field label="Address" value={fields.propertyAddress} onChange={(value) => updateField("propertyAddress", value)} />
        <Field label="City" value={fields.city} onChange={(value) => updateField("city", value)} />
        <Field label="State" value={fields.state} onChange={(value) => updateField("state", value)} />
        <Field label="Price" value={fields.price} onChange={(value) => updateField("price", value)} />
        <div className="mini-field-grid">
          <Field label="Beds" value={fields.bedrooms} onChange={(value) => updateField("bedrooms", value)} />
          <Field label="Baths" value={fields.bathrooms} onChange={(value) => updateField("bathrooms", value)} />
          <Field label="Garage" value={fields.garageSpaces} onChange={(value) => updateField("garageSpaces", value)} />
          <Field label="Sq ft" value={fields.squareFootage} onChange={(value) => updateField("squareFootage", value)} />
        </div>
        <Field label="Highlights" value={fields.propertyHighlights} onChange={(value) => updateField("propertyHighlights", value)} as="textarea" />
      </div>
    );
  }

  return (
    <div className="studio-tool-content">
      <div className="studio-panel-head"><p className="eyebrow">Saved Projects</p><h3>Recent work</h3></div>
      <div className="studio-saved-list">
        {projects.slice(0, 8).map((project) => (
          <button key={project.id} onClick={() => loadProject(project)}>
            <ProjectThumbnail project={project} brand={brand} />
            <span>{project.title}</span>
          </button>
        ))}
      </div>
      <button className="btn secondary" onClick={() => setActivePage("Saved Projects")}><Icon name="FolderOpen" /><span>Open library</span></button>
    </div>
  );
}

function StudioSettingsPanel({ selectedElement, setSelectedElement, fields, updateField, flyerCopy, updateFlyer, brand, selectedTemplate, leadCtaBlocks, applyCtaBlock, output, prompt, setPrompt, isGenerating, onGenerate, onQuickAction, setActivePage }) {
  const activeFormat = getCreativeFormat(flyerCopy.canvasFormat);

  return (
    <div className="studio-settings-content">
      <div className="studio-panel-head">
        <p className="eyebrow">Settings</p>
        <h3>{selectedElement === "headline" ? "Headline" : selectedElement === "agent" ? "Agent block" : selectedElement === "media" ? "Images" : "Design controls"}</h3>
      </div>
      <div className="selected-element-row">
        {["headline", "address", "price", "stats", "body", "agent", "disclaimer", "media"].map((item) => (
          <button key={item} className={selectedElement === item ? "selected" : ""} onClick={() => setSelectedElement(item)}>{item}</button>
        ))}
      </div>

      <div className="settings-card">
        <p className="studio-label">Template</p>
        <strong>{selectedTemplate.name || "Nex template"}</strong>
        <span>{selectedTemplate.category || "Marketing"} - {activeFormat.name} - {activeFormat.exportLabel}</span>
      </div>

      {activeFormat.id === "business-card" && (
        <div className="settings-card business-card-helper">
          <p className="studio-label">Business card mode</p>
          <strong>Use Agent Info for card details.</strong>
          <span>Headshot, website, social account, phone, and email all feed this card preview.</span>
          <button className="btn secondary" onClick={() => setActivePage("Business Card Builder")}>
            <Icon name="Contact" />
            <span>Open Full Card Builder</span>
          </button>
        </div>
      )}

      <div className="settings-card">
        <p className="studio-label">Typography</p>
        <RangeField label="Font size" value={flyerCopy.fontScale || 100} min={70} max={140} suffix="%" onChange={(value) => updateFlyer("fontScale", value)} />
        <RangeField label="Weight" value={flyerCopy.fontWeight || 900} min={500} max={950} step={50} onChange={(value) => updateFlyer("fontWeight", value)} />
        <ChoiceGrid label="Align" value={flyerCopy.textAlign || "left"} options={["left", "center", "right"].map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("textAlign", value)} />
      </div>

      <div className="settings-card">
        <p className="studio-label">Colors</p>
        <div className="brand-swatch-row">
          {[brand.primaryColor, brand.black, brand.white, brand.charcoal, brand.gold].map((color) => (
            <button key={color} style={{ background: color }} onClick={() => updateFlyer("accentColor", color)} title={color} />
          ))}
        </div>
        <Field label="Custom accent" value={flyerCopy.accentColor || selectedTemplate.accent || brand.primaryColor} onChange={(value) => updateFlyer("accentColor", value)} type="color" />
      </div>

      <div className="settings-card">
        <p className="studio-label">Lead CTA blocks</p>
        <div className="cta-chip-grid">
          {leadCtaBlocks.map((label) => <button key={label} onClick={() => applyCtaBlock(label)}>{label}</button>)}
        </div>
      </div>

      <div className="settings-card">
        <p className="studio-label">Quick editors</p>
        <Field label="Headline" value={flyerCopy.headline} onChange={(value) => updateFlyer("headline", value)} />
        <Field label="Address" value={fields.propertyAddress} onChange={(value) => updateField("propertyAddress", value)} />
        <Field label="Price" value={fields.price} onChange={(value) => updateField("price", value)} />
        <Field label="CTA" value={flyerCopy.cta} onChange={(value) => updateFlyer("cta", value)} />
      </div>

      <div className="settings-card assistant-card">
        <p className="studio-label">AI Copy Assistant</p>
        <div className="assistant-action-grid">
          {copyQuickActions.map(([label, action]) => (
            <button key={label} onClick={() => onQuickAction(action)}>{label}</button>
          ))}
        </div>
        <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} placeholder="Ask for a caption, email, SMS, luxury version, or flyer copy..." rows={4} />
        <button className="btn primary" onClick={() => onGenerate(prompt)} disabled={isGenerating}>
          <Icon name={isGenerating ? "LoaderCircle" : "Sparkles"} className={isGenerating ? "spin" : ""} />
          <span>{isGenerating ? "Generating" : "Generate Copy"}</span>
        </button>
        {output && <small>Latest output: {output.suggestedHeadline}</small>}
      </div>

      <div className="settings-card export-card">
        <p className="studio-label">More tools</p>
        {marketingStudioPages.map(([page, icon]) => (
          <button key={page} onClick={() => setActivePage(page)}>
            <Icon name={icon} size={15} />
            <span>{page}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

function CanvasInspectorPanel({
  selectedElement,
  selectedElements = [selectedElement],
  setSelectedElement,
  setSelectedElements,
  fields,
  updateField,
  flyerCopy,
  updateFlyer,
  brand,
  selectedFormat,
  onBackgroundUpload
}) {
  const layers = [
    ["headline", "Headline", "Type"],
    ["media", "Hero photo", "Image"],
    ["address", "Address", "MapPin"],
    ["price", "Price", "BadgeDollarSign"],
    ["stats", "Feature row", "Rows3"],
    ["body", "Body / CTA", "MessageSquareText"],
    ["qr", "QR code", "QrCode"],
    ["agent", "Agent block", "Contact"],
    ["disclaimer", "Disclaimer", "ShieldCheck"]
  ];
  const isTextLayer = ["headline", "address", "price", "stats", "body", "agent", "disclaimer"].includes(selectedElement);
  const selectedLayerCount = selectedElements.length;
  const hasVideoFormat = ["instagram-reel", "instagram-story", "horizontal-video", "youtube-thumbnail"].includes(selectedFormat.id);

  function chooseLayer(id, event) {
    const additive = Boolean(event?.shiftKey || event?.ctrlKey || event?.metaKey);
    if (!additive) {
      setSelectedElement(id);
      setSelectedElements([id]);
      return;
    }
    setSelectedElements((current) => {
      const next = current.includes(id) ? current.filter((item) => item !== id) : [...current, id];
      return next.length ? next : [id];
    });
  }

  function selectAllLayers() {
    const ids = layers.map(([id]) => id);
    setSelectedElements(ids);
    setSelectedElement(ids[0]);
  }

  function deleteSelected() {
    const targets = selectedElements.length ? selectedElements : [selectedElement];
    if (targets.includes("headline")) updateFlyer("headline", "");
    if (targets.includes("price")) updateFlyer("showPrice", false);
    if (targets.includes("stats")) updateFlyer("showStats", false);
    if (targets.includes("agent")) updateFlyer("showAgent", false);
    if (targets.includes("disclaimer")) updateFlyer("showDisclaimer", false);
    if (targets.includes("body")) updateFlyer("body", "");
    if (targets.includes("media")) updateFlyer("heroImageFit", "Cover");
    if (targets.includes("qr")) updateFlyer("showQr", false);
  }

  function duplicateSelected() {
    const targets = selectedElements.length ? selectedElements : [selectedElement];
    if (targets.includes("headline")) updateFlyer("body", `${flyerCopy.body || ""}\n\n${flyerCopy.headline || "New headline"}`.trim());
    if (targets.includes("body")) updateFlyer("body", `${flyerCopy.body || "Add your copy here."}\n\n${flyerCopy.body || "Add your copy here."}`);
    if (targets.includes("price")) updateFlyer("badge", fields.price || flyerCopy.badge || "Featured");
  }

  function updateSelectedScale(value) {
    selectedElements.forEach((id) => {
      if (id !== "headline" && id !== "media") updateFlyer(`${id}Scale`, value);
    });
  }

  return (
    <aside className="canvas-inspector-panel" aria-label="Selected design controls">
      <div className="inspector-head">
        <p className="eyebrow">Selected</p>
        <strong>{selectedLayerCount > 1 ? `${selectedLayerCount} layers selected` : layers.find(([id]) => id === selectedElement)?.[1] || "Design item"}</strong>
        <span>{selectedFormat.name} - {selectedFormat.exportLabel}</span>
      </div>

      <div className="inspector-selection-actions">
        <button type="button" onClick={selectAllLayers}><Icon name="MousePointer2" size={14} /><span>Select all</span></button>
        <button type="button" onClick={() => { setSelectedElements([selectedElement]); }}><Icon name="SquareMousePointer" size={14} /><span>Single layer</span></button>
        <small>Tip: hold Shift/Ctrl while clicking layers or canvas items to multi-select.</small>
      </div>

      <div className="inspector-layers">
        {layers.map(([id, label, icon]) => (
          <button key={id} className={selectedElements.includes(id) ? "selected" : ""} onClick={(event) => chooseLayer(id, event)} type="button">
            <Icon name={icon} size={14} />
            <span>{label}</span>
          </button>
        ))}
      </div>

      {selectedLayerCount > 1 && (
        <div className="inspector-card">
          <p className="studio-label">Group controls</p>
          <RangeField label="Group scale" value={flyerCopy[`${selectedElement}Scale`] || 100} min={70} max={160} suffix="%" onChange={updateSelectedScale} />
          <div className="inspector-action-grid">
            <button type="button" onClick={() => updateFlyer("accentColor", brand.primaryColor)}><Icon name="Palette" size={14} /><span>Nex red</span></button>
            <button type="button" onClick={() => updateFlyer("accentColor", brand.gold)}><Icon name="Gem" size={14} /><span>Gold accent</span></button>
            <button type="button" onClick={duplicateSelected}><Icon name="CopyPlus" size={14} /><span>Duplicate group</span></button>
            <button type="button" onClick={deleteSelected}><Icon name="Trash2" size={14} /><span>Hide group</span></button>
          </div>
        </div>
      )}

      {selectedElement === "headline" && (
        <div className="inspector-card">
          <p className="studio-label">Direct text controls</p>
          <RangeField label="Move X" value={Number(flyerCopy.headlineOffsetX) || 0} min={-140} max={140} step={1} suffix="px" onChange={(value) => updateFlyer("headlineOffsetX", value)} />
          <RangeField label="Move Y" value={Number(flyerCopy.headlineOffsetY) || 0} min={-140} max={140} step={1} suffix="px" onChange={(value) => updateFlyer("headlineOffsetY", value)} />
          <RangeField label="Width" value={flyerCopy.headlineWidth || 360} min={160} max={680} step={10} suffix="px" onChange={(value) => updateFlyer("headlineWidth", value)} />
          <RangeField label="Size" value={flyerCopy.fontScale || 100} min={70} max={150} suffix="%" onChange={(value) => updateFlyer("fontScale", value)} />
          <RangeField label="Weight" value={flyerCopy.fontWeight || 900} min={500} max={950} step={50} onChange={(value) => updateFlyer("fontWeight", value)} />
          <ChoiceGrid label="Align" value={flyerCopy.textAlign || "left"} options={["left", "center", "right"].map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("textAlign", value)} />
        </div>
      )}

      {selectedElement === "media" && (
        <div className="inspector-card">
          <p className="studio-label">Image controls</p>
          <RangeField label="Hero height" value={flyerCopy.heroHeight || 274} min={140} max={560} step={5} suffix="px" onChange={(value) => updateFlyer("heroHeight", value)} />
          <ChoiceGrid label="Fit" value={flyerCopy.heroImageFit || "Cover"} options={["Cover", "Contain", "Fill", "Center"].map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("heroImageFit", value)} />
          <ChoiceGrid label="Crop focus" value={flyerCopy.heroImagePosition || "center"} options={["center", "top", "bottom", "left", "right"].map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("heroImagePosition", value)} />
          <label className="toggle-row">
            <input type="checkbox" checked={flyerCopy.showQr === true} onChange={(event) => updateFlyer("showQr", event.target.checked)} />
            <span>Show QR block</span>
          </label>
          <Field label="QR destination" value={flyerCopy.qrUrl || ""} onChange={(value) => updateFlyer("qrUrl", value)} placeholder="https://..." />
        </div>
      )}

      {isTextLayer && selectedElement !== "headline" && (
        <div className="inspector-card">
          <p className="studio-label">Text shortcuts</p>
          {selectedElement === "address" && <Field label="Address" value={fields.propertyAddress || ""} onChange={(value) => updateField("propertyAddress", value)} />}
          {selectedElement === "price" && <Field label="Price" value={fields.price || ""} onChange={(value) => updateField("price", value)} />}
          {selectedElement === "body" && <Field label="Body copy" value={flyerCopy.body || ""} onChange={(value) => updateFlyer("body", value)} as="textarea" />}
          {selectedElement === "agent" && <Field label="Agent name" value={fields.agentName || ""} onChange={(value) => updateField("agentName", value)} />}
          {selectedElement === "disclaimer" && <Field label="Compliance footer" value={flyerCopy.footer || ""} onChange={(value) => updateFlyer("footer", value)} as="textarea" />}
          <RangeField label="Layer scale" value={flyerCopy[`${selectedElement}Scale`] || 100} min={70} max={160} suffix="%" onChange={(value) => updateFlyer(`${selectedElement}Scale`, value)} />
        </div>
      )}

      <div className="inspector-card">
        <p className="studio-label">Background</p>
        <Field label="Background color" value={flyerCopy.backgroundColor || "#ffffff"} onChange={(value) => updateFlyer("backgroundColor", value)} type="color" />
        <RangeField label="Background image opacity" value={flyerCopy.backgroundOpacity || 12} min={0} max={70} suffix="%" onChange={(value) => updateFlyer("backgroundOpacity", value)} />
        <label className="canvas-upload-button">
          <Icon name="ImagePlus" size={14} />
          <span>{flyerCopy.backgroundImage ? "Replace background" : "Upload background"}</span>
          <input type="file" accept="image/*" onChange={onBackgroundUpload} />
        </label>
        {flyerCopy.backgroundImage && (
          <button type="button" className="mini-danger-button" onClick={() => { updateFlyer("backgroundImage", ""); updateFlyer("backgroundImageName", ""); }}>
            <Icon name="X" size={14} />
            <span>Remove background image</span>
          </button>
        )}
      </div>

      {hasVideoFormat && (
        <div className="inspector-card">
          <p className="studio-label">Video / motion prep</p>
          <ChoiceGrid label="Motion style" value={flyerCopy.videoMotion || "Subtle zoom"} options={["Subtle zoom", "Slide up", "Pan left", "Static cover"].map((name) => ({ id: name, name }))} onChange={(value) => updateFlyer("videoMotion", value)} />
          <RangeField label="Duration" value={flyerCopy.videoDuration || 8} min={4} max={20} suffix=" sec" onChange={(value) => updateFlyer("videoDuration", value)} />
          <small className="studio-note">This prepares reel/story cover motion settings for the Quick Video Builder. Full timeline editing remains a later upgrade.</small>
        </div>
      )}

      <div className="inspector-card">
        <p className="studio-label">Layer actions</p>
        <div className="inspector-action-grid">
          <button type="button" onClick={() => updateFlyer("accentColor", brand.primaryColor)}><Icon name="Palette" size={14} /><span>Nex red</span></button>
          <button type="button" onClick={() => updateFlyer("accentColor", brand.black)}><Icon name="Circle" size={14} /><span>Black</span></button>
          <button type="button" onClick={duplicateSelected}><Icon name="CopyPlus" size={14} /><span>Duplicate</span></button>
          <button type="button" onClick={deleteSelected}><Icon name="Trash2" size={14} /><span>Delete</span></button>
        </div>
      </div>
    </aside>
  );
}

function MiniTemplatePreview({ template, brand }) {
  return (
    <div className="mini-template-preview" style={{ "--accent": template.accent || brand.primaryColor }}>
      <Wordmark brand={brand} compact variant={template.style === "luxury" ? "dark" : "light"} />
      <span>{template.badge}</span>
      <strong>{template.headline}</strong>
      <em>{template.category}</em>
    </div>
  );
}

function ProjectThumbnail({ project, brand }) {
  const headline = project.flyerCopy?.headline || project.fields?.marketingType || "Nex Project";
  const badge = project.flyerCopy?.badge || project.fields?.status || "Marketing";
  return (
    <div className="project-thumb" style={{ "--accent": project.flyerCopy?.accentColor || brand.primaryColor }}>
      <Wordmark brand={brand} compact variant="dark" />
      <span>{badge}</span>
      <strong>{headline}</strong>
    </div>
  );
}

function BulletinBoard({ announcements, role, setAnnouncements, setToast }) {
  const [query, setQuery] = useState("");
  const [category, setCategory] = useState("All");
  const [editingId, setEditingId] = useState("");
  const [editDraft, setEditDraft] = useState({});
  const [draft, setDraft] = useState({
    title: "",
    body: "",
    category: "General",
    pinned: false,
    urgent: false,
    expiresAt: ""
  });
  const canManage = isAdminRole(role);
  const filtered = announcements.filter((item) => {
    const matchesCategory = category === "All" || item.category === category;
    const matchesQuery = `${item.title} ${item.body}`.toLowerCase().includes(query.toLowerCase());
    return matchesCategory && matchesQuery;
  });

  async function addAnnouncement() {
    if (!draft.title.trim()) return;
    try {
      const saved = await API.request("/api/announcements", {
        method: "POST",
        body: JSON.stringify(draft)
      });
      setAnnouncements((current) => [saved, ...current]);
      setDraft({ title: "", body: "", category: "General", pinned: false, urgent: false, expiresAt: "" });
      setToast("Announcement posted.");
    } catch (error) {
      setToast(error.message || "Could not post announcement.");
    }
  }

  async function updateAnnouncement(id, patch) {
    try {
      const saved = await API.request(`/api/announcements/${encodeURIComponent(id)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
      setAnnouncements((current) => current.map((item) => (item.id === id ? saved : item)));
      setToast("Announcement updated.");
    } catch (error) {
      setToast(error.message || "Could not update announcement.");
    }
  }

  async function deleteAnnouncement(id) {
    try {
      await API.request(`/api/announcements/${encodeURIComponent(id)}`, { method: "DELETE" });
      setAnnouncements((current) => current.filter((item) => item.id !== id));
      setToast("Announcement deleted.");
    } catch (error) {
      setToast(error.message || "Could not delete announcement.");
    }
  }

  function startEditing(item) {
    setEditingId(item.id);
    setEditDraft({
      title: item.title || "",
      body: item.body || "",
      category: item.category || "General",
      expiresAt: item.expiresAt || ""
    });
  }

  async function saveEditing(id) {
    await updateAnnouncement(id, editDraft);
    setEditingId("");
    setEditDraft({});
  }

  return (
    <section className="portal-page">
      <div className="portal-toolbar">
        <Field label="Search announcements" value={query} onChange={setQuery} placeholder="Search updates..." />
        <Field label="Category" value={category} onChange={setCategory} as="select" options={["All", ...announcementCategories]} />
      </div>

      {canManage && (
        <div className="panel admin-compose">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Admin</p>
              <h3>Post announcement</h3>
            </div>
          </div>
          <div className="admin-form-grid">
            <Field label="Title" value={draft.title} onChange={(value) => setDraft((current) => ({ ...current, title: value }))} />
            <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} as="select" options={announcementCategories} />
            <Field label="Expires" value={draft.expiresAt} onChange={(value) => setDraft((current) => ({ ...current, expiresAt: value }))} type="date" />
            <label className="check-field"><input type="checkbox" checked={draft.pinned} onChange={(event) => setDraft((current) => ({ ...current, pinned: event.target.checked }))} />Pinned</label>
            <label className="check-field"><input type="checkbox" checked={draft.urgent} onChange={(event) => setDraft((current) => ({ ...current, urgent: event.target.checked }))} />Urgent</label>
            <Field label="Body" value={draft.body} onChange={(value) => setDraft((current) => ({ ...current, body: value }))} as="textarea" />
          </div>
          <button className="btn primary" onClick={addAnnouncement}>
            <Icon name="Plus" />
            <span>Post Announcement</span>
          </button>
        </div>
      )}

      <div className="announcement-grid">
        {filtered.map((item) => (
          <article className={classNames("announcement-card", item.urgent && "urgent", item.pinned && "pinned")} key={item.id}>
            <div className="announcement-meta">
              <span>{item.category}</span>
              {item.pinned && <em>Pinned</em>}
              {item.urgent && <em>Urgent</em>}
            </div>
            {editingId === item.id ? (
              <div className="announcement-edit-grid">
                <Field label="Title" value={editDraft.title || ""} onChange={(value) => setEditDraft((current) => ({ ...current, title: value }))} />
                <Field label="Category" value={editDraft.category || "General"} onChange={(value) => setEditDraft((current) => ({ ...current, category: value }))} as="select" options={announcementCategories} />
                <Field label="Expires" value={editDraft.expiresAt || ""} onChange={(value) => setEditDraft((current) => ({ ...current, expiresAt: value }))} type="date" />
                <Field label="Body" value={editDraft.body || ""} onChange={(value) => setEditDraft((current) => ({ ...current, body: value }))} as="textarea" />
              </div>
            ) : (
              <>
                <h3>{item.title}</h3>
                <p>{item.body}</p>
                {item.expiresAt && <small>Expires {new Date(item.expiresAt).toLocaleDateString()}</small>}
              </>
            )}
            {canManage && (
              <div className="row-actions">
                {editingId === item.id ? (
                  <>
                    <button className="btn secondary small-btn" onClick={() => saveEditing(item.id)}>Save</button>
                    <button className="btn secondary small-btn" onClick={() => setEditingId("")}>Cancel</button>
                  </>
                ) : (
                  <button className="btn secondary small-btn" onClick={() => startEditing(item)}>Edit</button>
                )}
                <button className="btn secondary small-btn" onClick={() => updateAnnouncement(item.id, { pinned: !item.pinned })}>Pin</button>
                <button className="btn secondary small-btn" onClick={() => updateAnnouncement(item.id, { urgent: !item.urgent })}>Urgent</button>
                <button className="icon-btn small" onClick={() => deleteAnnouncement(item.id)} title="Delete">
                  <Icon name="Trash2" size={15} />
                </button>
              </div>
            )}
          </article>
        ))}
      </div>
    </section>
  );
}

function QuickLinksCenter({ quickLinks, role, setQuickLinks, setToast, openQuickLink }) {
  const [draft, setDraft] = useState({ title: "", url: "", category: "General", description: "", icon: "ExternalLink", order: 99 });
  const canManage = isAdminRole(role);
  const groupedLinks = Object.entries(
    [...quickLinks]
      .sort((a, b) => Number(a.order || 999) - Number(b.order || 999))
      .reduce((groups, link) => {
        const category = link.category || "General";
        groups[category] = groups[category] || [];
        groups[category].push(link);
        return groups;
      }, {})
  );

  async function addLink() {
    if (!draft.title.trim()) return;
    try {
      const saved = await API.request("/api/quick-links", { method: "POST", body: JSON.stringify(draft) });
      setQuickLinks((current) => [...current, saved].sort((a, b) => Number(a.order) - Number(b.order)));
      setDraft({ title: "", url: "", category: "General", description: "", icon: "ExternalLink", order: 99 });
      setToast("Quick link added.");
    } catch (error) {
      setToast(error.message || "Could not add link.");
    }
  }

  async function deleteLink(id) {
    try {
      await API.request(`/api/quick-links/${encodeURIComponent(id)}`, { method: "DELETE" });
      setQuickLinks((current) => current.filter((item) => item.id !== id));
      setToast("Quick link removed.");
    } catch (error) {
      setToast(error.message || "Could not remove link.");
    }
  }

  async function updateLink(id, patch) {
    try {
      const saved = await API.request(`/api/quick-links/${encodeURIComponent(id)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
      setQuickLinks((current) =>
        current.map((item) => (item.id === id ? saved : item)).sort((a, b) => Number(a.order || 999) - Number(b.order || 999))
      );
    } catch (error) {
      setToast(error.message || "Could not update link.");
    }
  }

  return (
    <section className="portal-page">
      {canManage && (
        <div className="panel admin-compose">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Admin</p>
              <h3>Add quick link</h3>
            </div>
          </div>
          <div className="admin-form-grid">
            <Field label="Title" value={draft.title} onChange={(value) => setDraft((current) => ({ ...current, title: value }))} />
            <Field label="URL or app:Page Name" value={draft.url} onChange={(value) => setDraft((current) => ({ ...current, url: value }))} />
            <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} />
            <Field label="Icon" value={draft.icon} onChange={(value) => setDraft((current) => ({ ...current, icon: value }))} />
            <Field label="Order" value={draft.order} onChange={(value) => setDraft((current) => ({ ...current, order: value }))} type="number" />
            <Field label="Description" value={draft.description} onChange={(value) => setDraft((current) => ({ ...current, description: value }))} as="textarea" />
          </div>
          <button className="btn primary" onClick={addLink}>
            <Icon name="Plus" />
            <span>Add Link</span>
          </button>
        </div>
      )}

      <div className="quick-link-sections">
        {groupedLinks.map(([category, links]) => (
          <section className="quick-link-section" key={category}>
            <div className="quick-link-section-head">
              <h3>{category}</h3>
              <span>{links.length} link{links.length === 1 ? "" : "s"}</span>
            </div>
            <div className="quick-links-grid">
              {links.map((link) => {
                const url = String(link.url || "");
                const linkType = link.id === "nexu" || link.title === "NexU" ? "Internal" : url.startsWith("app:") ? "Internal" : url === "#" || !url ? "Needs URL" : "External";
                return (
                  <article className={classNames("quick-link-card", `type-${slugify(linkType)}`)} key={link.id}>
                    <button onClick={() => openQuickLink(link)}>
                      <span className="quick-link-icon"><Icon name={link.icon || "ExternalLink"} /></span>
                      <span className="quick-link-copy">
                        <strong>{link.title}</strong>
                        <span>{link.description}</span>
                      </span>
                      <em>{linkType}</em>
                    </button>
                    {canManage && (
                      <details className="link-admin-controls">
                        <summary>Manage link</summary>
                        <div>
                          <input value={link.title} onChange={(event) => updateLink(link.id, { title: event.target.value })} aria-label="Quick link title" />
                          <input value={link.url} onChange={(event) => updateLink(link.id, { url: event.target.value })} aria-label="Quick link URL" />
                          <input type="number" value={link.order || 99} onChange={(event) => updateLink(link.id, { order: event.target.value })} aria-label="Quick link order" />
                          <button className="icon-btn small" onClick={() => deleteLink(link.id)} title="Remove link">
                            <Icon name="Trash2" size={15} />
                          </button>
                        </div>
                      </details>
                    )}
                  </article>
                );
              })}
            </div>
          </section>
        ))}
      </div>
    </section>
  );
}

function chatChannelKey(channelId) {
  return `channel:${String(channelId || "general").replace(/^#/, "")}`;
}

function chatDmKeyForUsers(emailA, emailB) {
  return `dm:${[emailA, emailB].map((email) => String(email || "").toLowerCase()).sort().join("|")}`;
}

function chatInitials(nameOrEmail) {
  const text = String(nameOrEmail || "NX").trim();
  const parts = text.includes("@") ? [text[0], text.split("@")[1]?.[0]] : text.split(/\s+/);
  return parts.filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase() || "NX";
}

function chatMessageTime(value) {
  if (!value) return "";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return "";
  return date.toLocaleString([], { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
}

function chatFileExtension(name) {
  const parts = String(name || "").toLowerCase().split(".");
  return parts.length > 1 ? parts.pop() : "";
}

function chatAttachmentAllowed(file) {
  return CHAT_ALLOWED_EXTENSIONS.includes(chatFileExtension(file.name)) && Number(file.size || 0) <= CHAT_UPLOAD_LIMIT_BYTES;
}

function chatFileSize(size) {
  const number = Number(size || 0);
  if (number > 1024 * 1024) return `${(number / (1024 * 1024)).toFixed(1)} MB`;
  if (number > 1024) return `${Math.round(number / 1024)} KB`;
  return `${number || 0} B`;
}

function NexChat({ currentUser, role, setToast, onUnreadTotalChange, onLatestMessageChange }) {
  const [channels, setChannels] = useState([]);
  const [users, setUsers] = useState([]);
  const [messages, setMessages] = useState([]);
  const [unreadCounts, setUnreadCounts] = useState({});
  const [activeRoom, setActiveRoom] = useState({ type: "channel", id: "general", key: "channel:general", label: "#general" });
  const [draft, setDraft] = useState("");
  const [pendingFiles, setPendingFiles] = useState([]);
  const [loading, setLoading] = useState(true);
  const [sending, setSending] = useState(false);
  const [channelDraft, setChannelDraft] = useState("");
  const activeRoomRef = useRef(activeRoom);
  const messageEndRef = useRef(null);
  const canManageChannels = isAdminRole(role);

  function updateUnread(nextCounts) {
    const counts = nextCounts || {};
    setUnreadCounts(counts);
    onUnreadTotalChange?.(Object.values(counts).reduce((sum, value) => sum + Number(value || 0), 0));
  }

  async function markRead(roomKey = activeRoomRef.current.key) {
    if (!roomKey) return;
    try {
      const response = await API.request("/api/chat/read", {
        method: "POST",
        body: JSON.stringify({ roomKey })
      });
      updateUnread(response.unreadCounts || {});
    } catch (error) {
      // Read state is helpful, but chat should continue even if this request misses.
    }
  }

  useEffect(() => {
    activeRoomRef.current = activeRoom;
  }, [activeRoom]);

  useEffect(() => {
    let mounted = true;
    let stream;

    async function loadChat() {
      try {
        const data = await API.request("/api/chat/bootstrap");
        if (!mounted) return;
        setChannels(data.channels || []);
        setUsers(data.users || []);
        setMessages(data.messages || []);
        onLatestMessageChange?.((data.messages || []).slice(-1)[0] || null);
        updateUnread(data.unreadCounts || {});
      } catch (error) {
        setToast(error.message || "Could not load Nex Chat.");
      } finally {
        if (mounted) setLoading(false);
      }
    }

    loadChat();

    if (window.EventSource) {
      stream = new EventSource("/api/chat/stream");
      stream.addEventListener("chat-message", (event) => {
        const payload = JSON.parse(event.data || "{}");
        if (!payload.message) return;
        const roomKey = payload.message.roomType === "dm" ? payload.message.dmKey : chatChannelKey(payload.message.channelId);
        onLatestMessageChange?.(payload.message);
        setMessages((current) => {
          if (current.some((message) => message.id === payload.message.id)) return current;
          return [...current, payload.message].sort((a, b) => new Date(a.createdAt || 0) - new Date(b.createdAt || 0));
        });
        if (roomKey === activeRoomRef.current.key) {
          markRead(roomKey);
        } else if (payload.message.senderEmail !== currentUser?.email) {
          setUnreadCounts((current) => {
            const next = { ...current, [roomKey]: Number(current[roomKey] || 0) + 1 };
            onUnreadTotalChange?.(Object.values(next).reduce((sum, value) => sum + Number(value || 0), 0));
            return next;
          });
        }
      });
      stream.addEventListener("chat-channel", (event) => {
        const payload = JSON.parse(event.data || "{}");
        if (!payload.channel) return;
        setChannels((current) => current.some((channel) => channel.id === payload.channel.id) ? current : [...current, payload.channel].sort((a, b) => Number(a.order || 999) - Number(b.order || 999)));
      });
    }

    return () => {
      mounted = false;
      if (stream) stream.close();
    };
  }, []);

  useEffect(() => {
    markRead(activeRoom.key);
  }, [activeRoom.key]);

  useEffect(() => {
    messageEndRef.current?.scrollIntoView({ behavior: "smooth", block: "end" });
  }, [messages.length, activeRoom.key]);

  const roomMessages = messages.filter((message) => {
    const key = message.roomType === "dm" ? message.dmKey : chatChannelKey(message.channelId);
    return key === activeRoom.key;
  });
  const dmUsers = users.filter((user) => user.email && user.email !== currentUser?.email);
  const activeDmUser = dmUsers.find((user) => chatDmKeyForUsers(currentUser?.email, user.email) === activeRoom.key);

  function openChannel(channel) {
    setActiveRoom({ type: "channel", id: channel.id, key: chatChannelKey(channel.id), label: `#${channel.name || channel.id}` });
  }

  function openDm(user) {
    setActiveRoom({ type: "dm", id: user.email, key: chatDmKeyForUsers(currentUser?.email, user.email), label: user.name || user.email, recipientEmail: user.email });
  }

  async function addFiles(event) {
    const files = Array.from(event.target.files || []);
    const valid = [];
    for (const file of files) {
      if (!chatAttachmentAllowed(file)) {
        setToast(`${file.name} is not an allowed chat upload or is over 5MB.`);
        continue;
      }
      valid.push(await fileToDataUrl(file));
    }
    setPendingFiles((current) => [...current, ...valid].slice(0, 6));
    event.target.value = "";
  }

  async function sendMessage(event) {
    event.preventDefault();
    if (!draft.trim() && !pendingFiles.length) return;
    setSending(true);
    try {
      const payload = {
        roomType: activeRoom.type,
        channelId: activeRoom.type === "channel" ? activeRoom.id : "",
        recipientEmail: activeRoom.type === "dm" ? activeRoom.recipientEmail || activeDmUser?.email : "",
        text: draft,
        attachments: pendingFiles
      };
      const response = await API.request("/api/chat/messages", {
        method: "POST",
        body: JSON.stringify(payload)
      });
      if (response.message) {
        onLatestMessageChange?.(response.message);
        setMessages((current) => current.some((message) => message.id === response.message.id) ? current : [...current, response.message].sort((a, b) => new Date(a.createdAt || 0) - new Date(b.createdAt || 0)));
      }
      updateUnread(response.unreadCounts || {});
      setDraft("");
      setPendingFiles([]);
    } catch (error) {
      setToast(error.message || "Could not send chat message.");
    } finally {
      setSending(false);
    }
  }

  async function createChannel() {
    if (!channelDraft.trim()) return;
    try {
      const channel = await API.request("/api/chat/channels", {
        method: "POST",
        body: JSON.stringify({ name: channelDraft })
      });
      setChannels((current) => [...current, channel].sort((a, b) => Number(a.order || 999) - Number(b.order || 999)));
      setChannelDraft("");
      setToast("Chat channel created.");
    } catch (error) {
      setToast(error.message || "Could not create chat channel.");
    }
  }

  if (loading) return <EmptyState icon="MessagesSquare" title="Loading Nex Chat" body="Opening company channels, direct messages, and live updates." />;

  return (
    <section className="nex-chat-page">
      <aside className="nex-chat-sidebar">
        <div className="nex-chat-section-heading">
          <span>Channels</span>
          <em>{channels.length}</em>
        </div>
        <div className="nex-chat-room-list">
          {channels.map((channel) => {
            const key = chatChannelKey(channel.id);
            return (
              <button key={channel.id} className={classNames(activeRoom.key === key && "active")} onClick={() => openChannel(channel)}>
                <span>#</span>
                <strong>{channel.name}</strong>
                {unreadCounts[key] > 0 && <em>{unreadCounts[key]}</em>}
              </button>
            );
          })}
        </div>

        {canManageChannels && (
          <div className="nex-channel-create">
            <input value={channelDraft} onChange={(event) => setChannelDraft(event.target.value)} placeholder="new-channel" />
            <button onClick={createChannel} title="Create channel"><Icon name="Plus" size={15} /></button>
          </div>
        )}

        <div className="nex-chat-section-heading dm-heading">
          <span>Direct Messages</span>
          <em>{dmUsers.length}</em>
        </div>
        <div className="nex-chat-room-list">
          {dmUsers.map((user) => {
            const key = chatDmKeyForUsers(currentUser?.email, user.email);
            return (
              <button key={user.email} className={classNames(activeRoom.key === key && "active")} onClick={() => openDm(user)}>
                <span className="mini-avatar">{chatInitials(user.name || user.email)}</span>
                <strong>{user.name || user.email}</strong>
                {unreadCounts[key] > 0 && <em>{unreadCounts[key]}</em>}
              </button>
            );
          })}
        </div>
      </aside>

      <main className="nex-chat-main">
        <header className="nex-chat-header">
          <div>
            <p className="eyebrow">{activeRoom.type === "dm" ? "Direct message" : "Shared channel"}</p>
            <h3>{activeRoom.label}</h3>
            <span>{activeRoom.type === "dm" ? "Private conversation between participants only." : "Visible to logged-in Nex Central users."}</span>
          </div>
          <button className="btn secondary small-btn" onClick={() => markRead(activeRoom.key)}>
            <Icon name="CheckCheck" />
            <span>Mark Read</span>
          </button>
        </header>

        <div className="nex-chat-stream">
          {roomMessages.length ? (
            roomMessages.map((message) => (
              <NexChatMessage key={message.id} message={message} currentUser={currentUser} />
            ))
          ) : (
            <EmptyState icon="MessageCircle" title="No messages yet" body="Start the conversation with a quick update, question, file, or photo." />
          )}
          <div ref={messageEndRef} />
        </div>

        <form className="nex-chat-composer" onSubmit={sendMessage}>
          <div className="emoji-row">
            {chatEmojiOptions.map((emoji) => (
              <button key={emoji} type="button" onClick={() => setDraft((current) => `${current}${emoji}`)}>{emoji}</button>
            ))}
          </div>
          {pendingFiles.length > 0 && (
            <div className="pending-attachments">
              {pendingFiles.map((file, index) => (
                <span key={`${file.name}-${index}`}>
                  {file.name}
                  <button type="button" onClick={() => setPendingFiles((current) => current.filter((_, itemIndex) => itemIndex !== index))}>x</button>
                </span>
              ))}
            </div>
          )}
          <div className="composer-row">
            <label className="chat-attach-button" title="Attach file">
              <Icon name="Paperclip" />
              <input type="file" multiple accept=".jpg,.jpeg,.png,.gif,.webp,.pdf,.doc,.docx,.xls,.xlsx,.csv" onChange={addFiles} />
            </label>
            <textarea value={draft} onChange={(event) => setDraft(event.target.value)} placeholder={`Message ${activeRoom.label}`} rows={2} />
            <button className="btn primary" type="submit" disabled={sending}>
              <Icon name={sending ? "LoaderCircle" : "Send"} className={sending ? "spin" : ""} />
              <span>Send</span>
            </button>
          </div>
        </form>
      </main>
    </section>
  );
}

function NexChatMessage({ message, currentUser }) {
  const mine = message.senderEmail === currentUser?.email;
  return (
    <article className={classNames("nex-chat-message", mine && "mine")}>
      <div className="chat-avatar">{chatInitials(message.senderName || message.senderEmail)}</div>
      <div className="chat-message-body">
        <div className="chat-message-meta">
          <strong>{message.senderName || message.senderEmail}</strong>
          <span>{chatMessageTime(message.createdAt)}</span>
        </div>
        {message.text && <p>{message.text}</p>}
        {Array.isArray(message.attachments) && message.attachments.length > 0 && (
          <div className="chat-attachments">
            {message.attachments.map((file) => (
              <a key={file.id || file.name} href={file.dataUrl} download={file.name} target="_blank" rel="noreferrer" className={classNames(file.isImage && "image-attachment")}>
                {file.isImage ? <img src={file.dataUrl} alt={file.name} /> : <Icon name="FileDown" />}
                <span>{file.name}<small>{chatFileSize(file.size)}</small></span>
              </a>
            ))}
          </div>
        )}
      </div>
    </article>
  );
}

function NexAgentAssist({ setToast }) {
  const [prompt, setPrompt] = useState("");
  const [messages, setMessages] = useState([
    { id: "assist-welcome", type: "assistant", text: "Ask me about brokerage procedures, Brokermint, transaction workflows, marketing copy, and agent support." }
  ]);
  const [isAsking, setIsAsking] = useState(false);

  async function ask(question) {
    const nextPrompt = question || prompt;
    if (!nextPrompt.trim()) return;
    setIsAsking(true);
    setMessages((current) => [...current, { id: `ask-${Date.now()}`, type: "user", text: nextPrompt }]);
    try {
      const response = await API.request("/api/agent-assist", { method: "POST", body: JSON.stringify({ prompt: nextPrompt }) });
      setMessages((current) => [...current, { id: response.id, type: "assistant", text: response.answer }]);
      setPrompt("");
    } catch (error) {
      setToast("Nex Agent Assist could not answer right now.");
    } finally {
      setIsAsking(false);
    }
  }

  return (
    <section className="chat-layout agent-assist-layout">
      <div className="chat-card">
        <ComplianceNotice text={agentAssistDisclaimer} />
        <div className="prompt-starters">
          {agentAssistStarters.map((starter) => (
            <button key={starter} type="button" onClick={() => ask(starter)}>
              <Icon name="Sparkles" size={15} />
              <span>{starter}</span>
            </button>
          ))}
        </div>
        <div className="chat-stream">
          {messages.map((message) => (
            <div className={classNames("message", message.type)} key={message.id}>
              <div className="message-bubble">
                <p>{message.text}</p>
              </div>
            </div>
          ))}
        </div>
        <form className="chat-composer" onSubmit={(event) => { event.preventDefault(); ask(); }}>
          <textarea value={prompt} onChange={(event) => setPrompt(event.target.value)} placeholder="Ask Nex Agent Assist..." rows={3} />
          <button className="btn primary" disabled={isAsking}>
            <Icon name={isAsking ? "LoaderCircle" : "Send"} className={isAsking ? "spin" : ""} />
            <span>{isAsking ? "Thinking" : "Ask"}</span>
          </button>
        </form>
      </div>
      <div className="panel">
        <p className="eyebrow">Agent support</p>
        <h3>What I can help with</h3>
        <div className="support-topic-list">
          {["Brokerage procedures", "Brokermint steps", "Transaction workflows", "Listing questions", "Buyer process", "Rental process", "Marketing copy", "Lead follow-up scripts"].map((item) => (
            <span key={item}>{item}</span>
          ))}
        </div>
      </div>
    </section>
  );
}

function NexLiteCrm({ currentUser, role, setToast }) {
  const [activeCrmPage, setActiveCrmPage] = useState("Dashboard");
  const [crm, setCrm] = useState({
    contacts: [],
    notes: [],
    tasks: [],
    activity: [],
    stages: crmDefaultStages,
    pipelines: [],
    deals: [],
    teams: [],
    permissions: {}
  });
  const [loading, setLoading] = useState(true);
  const [query, setQuery] = useState("");
  const [leadTypeFilter, setLeadTypeFilter] = useState("All");
  const [stageFilter, setStageFilter] = useState("All");
  const [sourceFilter, setSourceFilter] = useState("All");
  const [temperatureFilter, setTemperatureFilter] = useState("All");
  const [assignedFilter, setAssignedFilter] = useState("All");
  const [smartListKey, setSmartListKey] = useState("hot");
  const [taskView, setTaskView] = useState("Today");
  const [pipelineView, setPipelineView] = useState("Buyer");
  const [selectedContactId, setSelectedContactId] = useState("");
  const [draft, setDraft] = useState(emptyCrmContact);
  const [contactEdit, setContactEdit] = useState(emptyCrmContact);
  const [noteDraft, setNoteDraft] = useState("");
  const [taskDraft, setTaskDraft] = useState({ title: "", taskType: "Call", dueDate: "", dueTime: "", priority: "Normal", notes: "" });
  const [communicationDraft, setCommunicationDraft] = useState({ type: "Call", outcome: "Connected", notes: "", nextStep: "", nextFollowUpDate: "" });
  const [selectedSmartPlanId, setSelectedSmartPlanId] = useState("new-buyer-lead");
  const [aiOutput, setAiOutput] = useState(null);
  const [aiBusy, setAiBusy] = useState("");
  const [csvText, setCsvText] = useState("");
  const [stagesText, setStagesText] = useState(crmDefaultStages.join("\n"));

  const stages = crm.stages?.length ? crm.stages : crmDefaultStages;
  const tasks = crm.tasks || [];
  const notes = crm.notes || [];
  const activity = crm.activity || [];
  const permissions = crm.permissions || {};
  const contacts = useMemo(
    () =>
      (crm.contacts || []).map((contact) => {
        const contactActivity = crmContactActivityItems(contact, notes, activity);
        const leadScore = typeof contact.leadScore === "number" ? contact.leadScore : crmLeadScore(contact, contactActivity);
        return {
          ...contact,
          leadScore,
          temperature: contact.temperature || crmTemperature({ ...contact, leadScore }, contactActivity)
        };
      }),
    [crm.contacts, notes, activity]
  );
  const agentOptions = useMemo(
    () =>
      ["All", "Unassigned", ...Array.from(new Set(contacts.map((contact) => contact.assignedAgentEmail).filter(Boolean))).sort((a, b) => a.localeCompare(b))],
    [contacts]
  );
  const sourceOptions = useMemo(
    () => ["All", ...Array.from(new Set([...crmLeadSources, ...contacts.map((contact) => contact.leadSource).filter(Boolean)])).sort((a, b) => a.localeCompare(b))],
    [contacts]
  );
  const smartLists = useMemo(() => crmSmartListDefinitions(contacts, tasks, notes, activity, permissions), [contacts, tasks, notes, activity, permissions.canViewAll]);
  const selectedSmartList = smartLists.find((list) => list.key === smartListKey) || smartLists[0];
  const smartListContacts = selectedSmartList ? contacts.filter(selectedSmartList.predicate) : contacts;
  const lowerQuery = query.toLowerCase();
  const filteredContacts = contacts.filter((contact) => {
    const haystack = [
      crmContactName(contact),
      contact.email,
      contact.phone,
      contact.secondaryPhone,
      contact.secondaryEmail,
      contact.leadType,
      contact.leadSource,
      contact.stage,
      contact.status,
      contact.temperature,
      contact.budgetRange,
      contact.desiredArea,
      contact.timeline,
      contact.propertyAddress,
      contact.campaignName,
      contact.formName,
      contact.assignedAgentEmail,
      crmTagsText(contact.tags)
    ]
      .filter(Boolean)
      .join(" ")
      .toLowerCase();
    const matchesSearch = !lowerQuery || haystack.includes(lowerQuery);
    const matchesLeadType = leadTypeFilter === "All" || contact.leadType === leadTypeFilter;
    const matchesStage = stageFilter === "All" || contact.stage === stageFilter;
    const matchesSource = sourceFilter === "All" || contact.leadSource === sourceFilter;
    const matchesTemperature = temperatureFilter === "All" || crmTemperature(contact, crmContactActivityItems(contact, notes, activity)) === temperatureFilter;
    const matchesAssigned =
      assignedFilter === "All" ||
      (assignedFilter === "Unassigned" ? !contact.assignedAgentEmail : normalizeEmail(contact.assignedAgentEmail) === normalizeEmail(assignedFilter));
    return matchesSearch && matchesLeadType && matchesStage && matchesSource && matchesTemperature && matchesAssigned;
  });
  const selectedContact =
    contacts.find((contact) => contact.id === selectedContactId) ||
    filteredContacts[0] ||
    contacts[0] ||
    null;
  const selectedNotes = selectedContact ? notes.filter((note) => note.contactId === selectedContact.id) : [];
  const selectedTasks = selectedContact ? tasks.filter((task) => task.contactId === selectedContact.id) : [];
  const selectedActivity = selectedContact ? activity.filter((item) => item.contactId === selectedContact.id) : [];
  const openTasks = tasks.filter((task) => task.status !== "Completed");
  const dueTodayTasks = openTasks.filter((task) => crmIsDueToday(task.dueDate));
  const dueTasks = openTasks.filter((task) => crmIsDue(task.dueDate));
  const newLeadCount = contacts.filter((contact) => contact.stage === "New Lead").length;
  const hotLeads = contacts.filter((contact) => crmTemperature(contact, crmContactActivityItems(contact, notes, activity)) === "Hot");
  const activeBuyerCount = contacts.filter((contact) => contact.leadType === "Buyer" && !["Closed", "Nurture", "Lost", "Lost/Inactive"].includes(contact.stage)).length;
  const activeSellerCount = contacts.filter((contact) => contact.leadType === "Seller" && !["Closed", "Nurture", "Lost", "Lost/Inactive"].includes(contact.stage)).length;
  const underContractCount = contacts.filter((contact) => contact.stage === "Under Contract").length;
  const closedCount = contacts.filter((contact) => contact.stage === "Closed").length;
  const nurtureCount = contacts.filter((contact) => contact.stage === "Nurture").length;
  const lostCount = contacts.filter((contact) => /lost/i.test(contact.stage || "")).length;
  const unassignedCount = contacts.filter((contact) => !contact.assignedAgentEmail).length;
  const appointmentCount = contacts.filter((contact) => /appointment|consultation|discovery call/i.test(contact.stage || "")).length;
  const conversionRate = contacts.length ? Math.round((closedCount / contacts.length) * 100) : 0;
  const leadContacts = contacts.filter((contact) => !["Vendor", "Past Client", "Sphere", "Referral Partner"].includes(contact.leadType) || /lead|prospect/i.test(contact.stage || ""));

  useEffect(() => {
    refreshCrm();
  }, []);

  useEffect(() => {
    if (!selectedContact) return;
    setContactEdit({
      ...emptyCrmContact,
      ...selectedContact,
      tags: crmTagsText(selectedContact.tags)
    });
  }, [selectedContact?.id, selectedContact?.updatedAt]);

  useEffect(() => {
    setStagesText(stages.join("\n"));
  }, [stages.join("|")]);

  async function refreshCrm() {
    setLoading(true);
    try {
      const payload = await API.request("/api/crm/bootstrap");
      setCrm(payload);
      if (!selectedContactId && payload.contacts?.[0]) setSelectedContactId(payload.contacts[0].id);
    } catch (error) {
      setToast(error.message || "Could not load Nex lite CRM.");
    } finally {
      setLoading(false);
    }
  }

  async function createContact(event) {
    event.preventDefault();
    if (![draft.firstName, draft.lastName, draft.email, draft.phone].some((value) => String(value || "").trim())) {
      setToast("Add a name, email, or phone before saving a contact.");
      return;
    }
    try {
      const saved = await API.request("/api/crm/contacts", { method: "POST", body: JSON.stringify(draft) });
      setCrm((current) => ({ ...current, contacts: [saved, ...(current.contacts || [])] }));
      setDraft({ ...emptyCrmContact, assignedAgentEmail: currentUser?.email || "" });
      setSelectedContactId(saved.id);
      setActiveCrmPage("Contacts");
      setToast("CRM contact added.");
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not add CRM contact.");
    }
  }

  async function updateContact(contactId, patch, toastMessage = "Contact updated.") {
    try {
      const saved = await API.request(`/api/crm/contacts/${encodeURIComponent(contactId)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
      setCrm((current) => ({
        ...current,
        contacts: (current.contacts || []).map((contact) => (contact.id === saved.id ? saved : contact))
      }));
      setToast(toastMessage);
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not update contact.");
    }
  }

  async function deleteContact(contactId) {
    if (!window.confirm("Delete this CRM contact and its notes/tasks?")) return;
    try {
      await API.request(`/api/crm/contacts/${encodeURIComponent(contactId)}`, { method: "DELETE" });
      setCrm((current) => ({
        ...current,
        contacts: (current.contacts || []).filter((contact) => contact.id !== contactId),
        notes: (current.notes || []).filter((note) => note.contactId !== contactId),
        tasks: (current.tasks || []).filter((task) => task.contactId !== contactId),
        activity: (current.activity || []).filter((item) => item.contactId !== contactId)
      }));
      setSelectedContactId("");
      setToast("CRM contact deleted.");
    } catch (error) {
      setToast(error.message || "Could not delete contact.");
    }
  }

  async function saveContactEdit() {
    if (!selectedContact) return;
    await updateContact(selectedContact.id, contactEdit, "Contact detail saved.");
  }

  async function addNote(event) {
    event.preventDefault();
    if (!selectedContact || !noteDraft.trim()) return;
    try {
      const saved = await API.request(`/api/crm/contacts/${encodeURIComponent(selectedContact.id)}/notes`, {
        method: "POST",
        body: JSON.stringify({ body: noteDraft })
      });
      setCrm((current) => ({
        ...current,
        notes: [saved.note, ...(current.notes || [])],
        activity: [saved.activity, ...(current.activity || [])],
        contacts: (current.contacts || []).map((contact) => (contact.id === saved.contact.id ? saved.contact : contact))
      }));
      setNoteDraft("");
      setToast("Note added.");
    } catch (error) {
      setToast(error.message || "Could not add note.");
    }
  }

  async function addTask(event) {
    event.preventDefault();
    if (!selectedContact || !taskDraft.title.trim()) return;
    try {
      const saved = await API.request(`/api/crm/contacts/${encodeURIComponent(selectedContact.id)}/tasks`, {
        method: "POST",
        body: JSON.stringify(taskDraft)
      });
      setCrm((current) => ({
        ...current,
        tasks: [saved.task, ...(current.tasks || [])],
        activity: [saved.activity, ...(current.activity || [])],
        contacts: (current.contacts || []).map((contact) => (contact.id === saved.contact.id ? saved.contact : contact))
      }));
      setTaskDraft({ title: "", taskType: "Call", dueDate: "", dueTime: "", priority: "Normal", notes: "" });
      setToast("Follow-up task added.");
    } catch (error) {
      setToast(error.message || "Could not add task.");
    }
  }

  async function addCommunication(event) {
    event.preventDefault();
    if (!selectedContact || !communicationDraft.notes.trim()) {
      setToast("Add a quick note before logging communication.");
      return;
    }
    try {
      const saved = await API.request(`/api/crm/contacts/${encodeURIComponent(selectedContact.id)}/activity`, {
        method: "POST",
        body: JSON.stringify(communicationDraft)
      });
      setCrm((current) => ({
        ...current,
        activity: [saved.activity, ...(current.activity || [])],
        contacts: (current.contacts || []).map((contact) => (contact.id === saved.contact.id ? saved.contact : contact))
      }));
      setCommunicationDraft({ type: "Call", outcome: "Connected", notes: "", nextStep: "", nextFollowUpDate: "" });
      setToast("Communication logged.");
    } catch (error) {
      setToast(error.message || "Could not log communication.");
    }
  }

  async function applySmartPlan() {
    if (!selectedContact) return;
    const plan = crmSmartPlans.find((item) => item.id === selectedSmartPlanId) || crmSmartPlans[0];
    if (!plan) return;
    try {
      const createdTasks = [];
      for (const [offsetDays, title, taskType, priority] of plan.tasks) {
        const saved = await API.request(`/api/crm/contacts/${encodeURIComponent(selectedContact.id)}/tasks`, {
          method: "POST",
          body: JSON.stringify({
            title,
            taskType,
            priority,
            dueDate: addDays(new Date().toISOString().slice(0, 10), offsetDays),
            notes: `Created from ${plan.name} smart plan.`
          })
        });
        createdTasks.push(saved.task);
      }
      const note = await API.request(`/api/crm/contacts/${encodeURIComponent(selectedContact.id)}/notes`, {
        method: "POST",
        body: JSON.stringify({ body: `${plan.name} smart plan applied. Suggested first text: ${plan.scripts?.text || "Follow up with value and a clear next step."}` })
      });
      setCrm((current) => ({
        ...current,
        tasks: [...createdTasks, ...(current.tasks || [])],
        notes: [note.note, ...(current.notes || [])],
        activity: [note.activity, ...(current.activity || [])]
      }));
      setToast(`${plan.name} smart plan applied.`);
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not apply smart plan.");
    }
  }

  async function updateTask(taskId, patch) {
    try {
      const saved = await API.request(`/api/crm/tasks/${encodeURIComponent(taskId)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
      setCrm((current) => ({
        ...current,
        tasks: (current.tasks || []).map((task) => (task.id === saved.id ? saved : task))
      }));
      setToast("Task updated.");
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not update task.");
    }
  }

  async function deleteTask(taskId) {
    try {
      await API.request(`/api/crm/tasks/${encodeURIComponent(taskId)}`, { method: "DELETE" });
      setCrm((current) => ({
        ...current,
        tasks: (current.tasks || []).filter((task) => task.id !== taskId)
      }));
      setToast("Task deleted.");
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not delete task.");
    }
  }

  async function runAi(action, label) {
    if (!selectedContact) return;
    setAiBusy(action);
    try {
      const response = await API.request("/api/crm/ai", {
        method: "POST",
        body: JSON.stringify({ contactId: selectedContact.id, action })
      });
      setAiOutput({ label, ...response });
      setCrm((current) => ({ ...current, activity: [response.activity, ...(current.activity || [])] }));
      setToast(`${label} generated.`);
    } catch (error) {
      setToast(error.message || "Could not generate CRM AI content.");
    } finally {
      setAiBusy("");
    }
  }

  function exportCsv() {
    const headers = [
      "firstName",
      "lastName",
      "email",
      "phone",
      "secondaryEmail",
      "secondaryPhone",
      "leadType",
      "leadSource",
      "status",
      "stage",
      "assignedAgentEmail",
      "budgetRange",
      "desiredArea",
      "timeline",
      "buyingTimeline",
      "sellingTimeline",
      "propertyAddress",
      "bedrooms",
      "bathrooms",
      "financingStatus",
      "preApproved",
      "workingWithAnotherAgent",
      "consentStatus",
      "doNotContact",
      "campaignName",
      "formName",
      "utmSource",
      "utmMedium",
      "utmCampaign",
      "referringUrl",
      "tags",
      "lastContactedDate",
      "nextFollowUpDate",
      "notes"
    ];
    const body = contacts.map((contact) => headers.map((header) => csvEscape(header === "tags" ? crmTagsText(contact.tags) : contact[header])).join(","));
    const csv = [headers.join(","), ...body].join("\n");
    const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const link = document.createElement("a");
    link.href = url;
    link.download = "nex-lite-crm-contacts.csv";
    link.click();
    URL.revokeObjectURL(url);
    setToast("CRM contacts exported.");
  }

  async function importCsv(event) {
    event.preventDefault();
    const rows = parseCsvRows(csvText);
    if (rows.length < 2) {
      setToast("Paste a CSV with a header row and at least one contact.");
      return;
    }
    const [headers, ...dataRows] = rows;
    const payloads = dataRows.map((row) => crmContactFromCsvRow(headers, row)).filter((contact) => contact.firstName || contact.lastName || contact.email || contact.phone);
    if (!payloads.length) {
      setToast("No importable contacts found in the CSV.");
      return;
    }
    try {
      const created = [];
      for (const payload of payloads) {
        created.push(await API.request("/api/crm/contacts", { method: "POST", body: JSON.stringify(payload) }));
      }
      setCrm((current) => ({ ...current, contacts: [...created, ...(current.contacts || [])] }));
      setCsvText("");
      setToast(`${created.length} CRM contacts imported.`);
      refreshCrm();
    } catch (error) {
      setToast(error.message || "Could not import CRM contacts.");
    }
  }

  function handleCsvFile(event) {
    const file = event.target.files?.[0];
    if (!file) return;
    const reader = new FileReader();
    reader.onload = () => setCsvText(String(reader.result || ""));
    reader.readAsText(file);
  }

  async function saveStages() {
    try {
      const response = await API.request("/api/crm/stages", {
        method: "PUT",
        body: JSON.stringify({ stages: stagesText })
      });
      setCrm((current) => ({ ...current, stages: response.stages, pipelines: response.pipelines }));
      setToast("CRM stages saved.");
    } catch (error) {
      setToast(error.message || "Could not save CRM stages.");
    }
  }

  function renderDashboard() {
    const sourceCounts = Array.from(
      contacts.reduce((map, contact) => {
        const source = contact.leadSource || "No source";
        map.set(source, (map.get(source) || 0) + 1);
        return map;
      }, new Map())
    )
      .sort((a, b) => b[1] - a[1])
      .slice(0, 7);
    const avgSpeedLeads = contacts.filter((contact) => contact.speedToLead?.minutes !== null && typeof contact.speedToLead?.minutes !== "undefined");
    const avgSpeedMinutes = avgSpeedLeads.length ? Math.round(avgSpeedLeads.reduce((sum, contact) => sum + Number(contact.speedToLead.minutes || 0), 0) / avgSpeedLeads.length) : null;
    const accountabilityRows = Array.from(
      contacts.reduce((map, contact) => {
        const agent = contact.assignedAgentEmail || "Unassigned";
        const row = map.get(agent) || { agent, leads: 0, hot: 0, overdue: 0, appointments: 0, closed: 0 };
        const contactTasks = tasks.filter((task) => task.contactId === contact.id && task.status !== "Completed");
        row.leads += 1;
        if (crmTemperature(contact, crmContactActivityItems(contact, notes, activity)) === "Hot") row.hot += 1;
        if (contactTasks.some((task) => crmIsDue(task.dueDate)) || crmIsDue(contact.nextFollowUpDate)) row.overdue += 1;
        if (/appointment|consultation|discovery/i.test(contact.stage || "")) row.appointments += 1;
        if (contact.stage === "Closed") row.closed += 1;
        map.set(agent, row);
        return map;
      }, new Map())
    ).slice(0, 6);

    const cockpitCards = [
      ["New Leads", newLeadCount, "Inbox", "Needs speed-to-lead", "new-not-contacted"],
      ["Hot Leads", hotLeads.length, "Flame", "Highest intent", "hot"],
      ["Due Today", dueTodayTasks.length, "CalendarClock", "Follow up now", "due-today"],
      ["Overdue", dueTasks.length, "AlarmClock", "Action needed", "overdue"],
      ["Appointments", appointmentCount, "CalendarCheck", "Consults booked", ""],
      ["Active Buyers", activeBuyerCount, "Home", "Buyer pipeline", ""],
      ["Active Sellers", activeSellerCount, "MapPinned", "Seller pipeline", ""],
      ["Under Contract", underContractCount, "Handshake", "Client files", ""],
      ["Closed", closedCount, "BadgeDollarSign", `${conversionRate}% conversion`, ""],
      ["Nurture", nurtureCount, "RefreshCcw", "Long-term follow-up", ""],
      ["Lost", lostCount, "ArchiveX", "Review quality", ""],
      ["Unassigned", unassignedCount, "UserRoundX", permissions.canViewAll ? "Admin routing" : "Admin only", "unassigned"]
    ];

    return (
      <div className="crm-cockpit">
        <section className="crm-cockpit-hero">
          <div>
            <p className="eyebrow">Lead conversion cockpit</p>
            <h2>Know who needs action, who is hot, and where every opportunity sits.</h2>
            <p>
              Nex Lite CRM now works like a real estate lead cockpit: score, source, assignment, follow-up, speed-to-lead,
              pipeline, smart lists, and next-best-action prompts in one place.
            </p>
          </div>
          <div className="crm-cockpit-status">
            <span><Icon name="Activity" /> {contacts.length} total records</span>
            <span><Icon name="Gauge" /> Avg speed: {avgSpeedMinutes === null ? "sample pending" : `${avgSpeedMinutes} min`}</span>
            <span><Icon name="ShieldCheck" /> {permissions.canViewAll ? "Admin brokerage view" : "My leads view"}</span>
          </div>
        </section>

        <section className="crm-cockpit-metrics">
          {cockpitCards.map(([label, value, icon, status, smartKey]) => (
            <button
              key={label}
              className={classNames("crm-cockpit-card", label === "Hot Leads" && "hot", label === "Overdue" && "danger")}
              onClick={() => {
                if (smartKey) {
                  setSmartListKey(smartKey);
                  setActiveCrmPage("Smart Lists");
                } else {
                  setActiveCrmPage("Contacts");
                }
              }}
            >
              <Icon name={icon} />
              <strong>{value}</strong>
              <span>{label}</span>
              <em>{status}</em>
            </button>
          ))}
        </section>

        <section className="crm-cockpit-grid">
          <div className="panel crm-action-queue">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Today</p>
                <h3>Action queue</h3>
              </div>
              <button className="btn secondary small-btn" onClick={() => setActiveCrmPage("Follow-Up Tasks")}>Open Tasks</button>
            </div>
            <div className="crm-task-stack">
              {[...dueTodayTasks, ...dueTasks].slice(0, 7).map((task) => {
                const contact = contacts.find((item) => item.id === task.contactId);
                return (
                  <article key={task.id} className={classNames(crmIsDue(task.dueDate) && "overdue")}>
                    <strong>{task.title}</strong>
                    <span>{crmContactName(contact)} - {task.taskType || "Follow-up"} - {crmShortDate(task.dueDate)}</span>
                    <div className="row-actions">
                      <button className="btn secondary small-btn" onClick={() => { setSelectedContactId(task.contactId); setActiveCrmPage("Contacts"); }}>Open Lead</button>
                      <button className="btn secondary small-btn" onClick={() => updateTask(task.id, { status: "Completed" })}>Complete</button>
                    </div>
                  </article>
                );
              })}
              {!openTasks.length && <EmptyState icon="ListTodo" title="No open follow-ups" body="Create tasks from a contact profile or apply a smart plan." />}
            </div>
          </div>

          <div className="panel crm-hot-queue">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Hot leads</p>
                <h3>Highest intent</h3>
              </div>
              <button className="btn secondary small-btn" onClick={() => { setSmartListKey("hot"); setActiveCrmPage("Smart Lists"); }}>View List</button>
            </div>
            <div className="crm-lead-queue">
              {hotLeads.slice(0, 6).map((contact) => (
                <button key={contact.id} onClick={() => { setSelectedContactId(contact.id); setActiveCrmPage("Contacts"); }}>
                  <strong>{crmContactName(contact)}</strong>
                  <span>{contact.leadType} - {contact.leadSource || "No source"} - {contact.stage}</span>
                  <em>{contact.leadScore} score</em>
                </button>
              ))}
              {!hotLeads.length && <p className="muted-text">No hot leads yet. Lead scores rise when phone, timeline, source, budget, area, or contact activity improves.</p>}
            </div>
          </div>

          <div className="panel crm-wide-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Pipeline snapshot</p>
              <h3>Stage conversion view</h3>
            </div>
            <button className="btn secondary" onClick={() => setActiveCrmPage("Pipeline")}>
              <Icon name="Kanban" />
              <span>Open Board</span>
            </button>
          </div>
          <div className="crm-stage-meter">
            {crmPipelineStagesForType(pipelineView, stages).map((stage) => {
              const count = contacts.filter((contact) => contact.stage === stage).length;
              return (
                <button key={stage} onClick={() => { setStageFilter(stage); setActiveCrmPage("Contacts"); }}>
                  <strong>{count}</strong>
                  <span>{stage}</span>
                </button>
              );
            })}
          </div>
          </div>

          <div className="panel crm-smart-list-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Smart lists</p>
              <h3>Conversion filters</h3>
            </div>
          </div>
          <div className="crm-smart-list-mini">
            {smartLists.slice(0, 10).map((list) => (
              <button key={list.key} onClick={() => { setSmartListKey(list.key); setActiveCrmPage("Smart Lists"); }}>
                <Icon name={list.icon} />
                <span>{list.label}</span>
                <strong>{contacts.filter(list.predicate).length}</strong>
              </button>
            ))}
          </div>
          </div>

          <div className="panel crm-source-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Lead source tracking</p>
              <h3>Sources creating activity</h3>
            </div>
          </div>
          <div className="crm-source-list">
            {sourceCounts.map(([source, count]) => (
              <button key={source} onClick={() => { setSourceFilter(source); setActiveCrmPage("Contacts"); }}>
                <span>{source}</span>
                <strong>{count}</strong>
              </button>
            ))}
            {!sourceCounts.length && <p className="muted-text">Sources appear as leads are added or imported.</p>}
          </div>
          </div>

          <div className="panel crm-accountability-panel">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">{permissions.canViewAll ? "Admin accountability" : "My accountability"}</p>
                <h3>Agent activity</h3>
              </div>
            </div>
            <div className="crm-accountability-table">
              {accountabilityRows.map((row) => (
                <article key={row.agent}>
                  <strong>{row.agent}</strong>
                  <span>{row.leads} leads</span>
                  <span>{row.hot} hot</span>
                  <span>{row.overdue} overdue</span>
                  <span>{row.appointments} appts</span>
                  <span>{row.closed} closed</span>
                </article>
              ))}
            </div>
          </div>
        </section>
      </div>
    );
  }

  function renderContactDetail() {
    if (!selectedContact) {
      return <EmptyState icon="UsersRound" title="No contact selected" body="Add or import a CRM contact to open the contact detail page." />;
    }
    const aiActions = [
      ["text", "Write follow-up text", "MessageSquare"],
      ["email", "Write follow-up email", "Mail"],
      ["nurture", "Create nurture plan", "CalendarClock"],
      ["summary", "Summarize contact notes", "ClipboardList"],
      ["next-action", "Suggest next action", "Target"]
    ];
    const contactActivityItems = crmContactActivityItems(selectedContact, notes, activity);
    const leadScore = typeof selectedContact.leadScore === "number" ? selectedContact.leadScore : crmLeadScore(selectedContact, contactActivityItems);
    const temperature = selectedContact.temperature || crmTemperature({ ...selectedContact, leadScore }, contactActivityItems);
    const selectedPlan = crmSmartPlans.find((plan) => plan.id === selectedSmartPlanId) || crmSmartPlans[0];
    const profileStageOptions = Array.from(new Set([...crmPipelineStagesForType(contactEdit.leadType || selectedContact.leadType, stages), contactEdit.stage || selectedContact.stage, ...stages].filter(Boolean)));
    return (
      <div className="crm-detail-stack">
        <div className="crm-contact-head">
          <div>
            <p className="eyebrow">Lead profile</p>
            <h3>{crmContactName(selectedContact)}</h3>
            <span>{selectedContact.leadType} - {selectedContact.stage}</span>
            <div className="crm-profile-badges">
              <em className={classNames("temperature-badge", crmTemperatureClass(temperature))}>{temperature}</em>
              <em>{leadScore} score</em>
              <em>{selectedContact.leadSource || "No source"}</em>
              <em>Speed: {selectedContact.speedToLead?.label || crmSpeedToLead(selectedContact)}</em>
            </div>
          </div>
          <div className="row-actions">
            {selectedContact.phone && <a className="btn secondary small-btn" href={`tel:${selectedContact.phone}`}><Icon name="Phone" /><span>Call</span></a>}
            {selectedContact.phone && <a className="btn secondary small-btn" href={`sms:${selectedContact.phone}`}><Icon name="MessageSquare" /><span>Text</span></a>}
            {selectedContact.email && <a className="btn secondary small-btn" href={`mailto:${selectedContact.email}`}><Icon name="Mail" /><span>Email</span></a>}
            <button className="btn secondary small-btn" onClick={saveContactEdit}>Save Detail</button>
            <button className="icon-btn small" onClick={() => deleteContact(selectedContact.id)} title="Delete contact">
              <Icon name="Trash2" size={15} />
            </button>
          </div>
        </div>

        <div className="crm-next-action-strip">
          <Icon name="Sparkles" />
          <div>
            <strong>Next best action</strong>
            <p>
              {selectedTasks.find((task) => task.status !== "Completed")?.title ||
                (selectedContact.nextFollowUpDate
                  ? `Follow up by ${crmShortDate(selectedContact.nextFollowUpDate)}`
                  : temperature === "Hot"
                    ? "Call or text this lead now and log the result."
                    : "Set a specific next follow-up so this lead does not go cold.")}
            </p>
          </div>
          <button className="btn secondary small-btn" onClick={() => runAi("next-action", "Suggest next action")}>
            Ask AI
          </button>
        </div>

        <div className="crm-form-grid">
          <Field label="First name" value={contactEdit.firstName || ""} onChange={(value) => setContactEdit((current) => ({ ...current, firstName: value }))} />
          <Field label="Last name" value={contactEdit.lastName || ""} onChange={(value) => setContactEdit((current) => ({ ...current, lastName: value }))} />
          <Field label="Email" value={contactEdit.email || ""} onChange={(value) => setContactEdit((current) => ({ ...current, email: value }))} type="email" />
          <Field label="Phone" value={contactEdit.phone || ""} onChange={(value) => setContactEdit((current) => ({ ...current, phone: value }))} />
          <Field label="Secondary email" value={contactEdit.secondaryEmail || ""} onChange={(value) => setContactEdit((current) => ({ ...current, secondaryEmail: value }))} type="email" />
          <Field label="Secondary phone" value={contactEdit.secondaryPhone || ""} onChange={(value) => setContactEdit((current) => ({ ...current, secondaryPhone: value }))} />
          <Field label="Lead type" value={contactEdit.leadType || "Other"} onChange={(value) => setContactEdit((current) => ({ ...current, leadType: value }))} as="select" options={crmLeadTypes} />
          <Field label="Lead status" value={contactEdit.status || "New"} onChange={(value) => setContactEdit((current) => ({ ...current, status: value }))} />
          <Field label="Stage" value={contactEdit.stage || profileStageOptions[0]} onChange={(value) => setContactEdit((current) => ({ ...current, stage: value }))} as="select" options={profileStageOptions} />
          <Field label="Lead source" value={contactEdit.leadSource || ""} onChange={(value) => setContactEdit((current) => ({ ...current, leadSource: value }))} as="select" options={crmLeadSources} />
          <Field label="Assigned agent email" value={contactEdit.assignedAgentEmail || ""} onChange={(value) => setContactEdit((current) => ({ ...current, assignedAgentEmail: value }))} type="email" />
          <Field label="Preferred contact" value={contactEdit.preferredContactMethod || "Any"} onChange={(value) => setContactEdit((current) => ({ ...current, preferredContactMethod: value }))} as="select" options={crmContactMethods} />
          <Field label="Budget / price range" value={contactEdit.budgetRange || ""} onChange={(value) => setContactEdit((current) => ({ ...current, budgetRange: value }))} />
          <Field label="Desired area" value={contactEdit.desiredArea || ""} onChange={(value) => setContactEdit((current) => ({ ...current, desiredArea: value }))} />
          <Field label="Timeline" value={contactEdit.timeline || ""} onChange={(value) => setContactEdit((current) => ({ ...current, timeline: value }))} />
          <Field label="Buying timeline" value={contactEdit.buyingTimeline || ""} onChange={(value) => setContactEdit((current) => ({ ...current, buyingTimeline: value }))} />
          <Field label="Selling timeline" value={contactEdit.sellingTimeline || ""} onChange={(value) => setContactEdit((current) => ({ ...current, sellingTimeline: value }))} />
          <Field label="Seller property address" value={contactEdit.propertyAddress || ""} onChange={(value) => setContactEdit((current) => ({ ...current, propertyAddress: value }))} />
          <Field label="Beds" value={contactEdit.bedrooms || ""} onChange={(value) => setContactEdit((current) => ({ ...current, bedrooms: value }))} />
          <Field label="Baths" value={contactEdit.bathrooms || ""} onChange={(value) => setContactEdit((current) => ({ ...current, bathrooms: value }))} />
          <Field label="Financing status" value={contactEdit.financingStatus || ""} onChange={(value) => setContactEdit((current) => ({ ...current, financingStatus: value }))} />
          <Field label="Pre-approved" value={contactEdit.preApproved || "Unknown"} onChange={(value) => setContactEdit((current) => ({ ...current, preApproved: value }))} as="select" options={crmPreApprovedOptions} />
          <Field label="Working with another agent" value={contactEdit.workingWithAnotherAgent || "Unknown"} onChange={(value) => setContactEdit((current) => ({ ...current, workingWithAnotherAgent: value }))} as="select" options={crmWorkingAgentOptions} />
          <Field label="Consent status" value={contactEdit.consentStatus || "Unknown"} onChange={(value) => setContactEdit((current) => ({ ...current, consentStatus: value, doNotContact: value === "Do Not Contact" || value === "Unsubscribed" }))} as="select" options={crmConsentOptions} />
          <Field label="Opt-in source" value={contactEdit.optInSource || ""} onChange={(value) => setContactEdit((current) => ({ ...current, optInSource: value }))} />
          <Field label="Birthday" value={contactEdit.birthday || ""} onChange={(value) => setContactEdit((current) => ({ ...current, birthday: value }))} type="date" />
          <Field label="Home anniversary" value={contactEdit.homeAnniversary || ""} onChange={(value) => setContactEdit((current) => ({ ...current, homeAnniversary: value }))} type="date" />
          <Field label="Campaign name" value={contactEdit.campaignName || ""} onChange={(value) => setContactEdit((current) => ({ ...current, campaignName: value }))} />
          <Field label="Form name" value={contactEdit.formName || ""} onChange={(value) => setContactEdit((current) => ({ ...current, formName: value }))} />
          <Field label="UTM source" value={contactEdit.utmSource || ""} onChange={(value) => setContactEdit((current) => ({ ...current, utmSource: value }))} />
          <Field label="UTM campaign" value={contactEdit.utmCampaign || ""} onChange={(value) => setContactEdit((current) => ({ ...current, utmCampaign: value }))} />
          <Field label="Referring URL" value={contactEdit.referringUrl || ""} onChange={(value) => setContactEdit((current) => ({ ...current, referringUrl: value }))} />
          <Field label="Tags" value={contactEdit.tags || ""} onChange={(value) => setContactEdit((current) => ({ ...current, tags: value }))} placeholder="open house, luxury, referral" />
          <Field label="Last contacted" value={contactEdit.lastContactedDate || ""} onChange={(value) => setContactEdit((current) => ({ ...current, lastContactedDate: value }))} type="date" />
          <Field label="Next follow-up" value={contactEdit.nextFollowUpDate || ""} onChange={(value) => setContactEdit((current) => ({ ...current, nextFollowUpDate: value }))} type="date" />
          <label className="check-field crm-dnc-toggle">
            <input type="checkbox" checked={Boolean(contactEdit.doNotContact)} onChange={(event) => setContactEdit((current) => ({ ...current, doNotContact: event.target.checked }))} />
            Do not contact
          </label>
          <Field label="Notes" value={contactEdit.notes || ""} onChange={(value) => setContactEdit((current) => ({ ...current, notes: value }))} as="textarea" />
        </div>

        <div className="crm-smart-plan-panel">
          <div>
            <p className="eyebrow">Smart plans</p>
            <h3>Action plan automation</h3>
            <p>Apply a starter plan to create scheduled tasks and a script note for this lead.</p>
          </div>
          <label className="field">
            <span>Plan</span>
            <select value={selectedSmartPlanId} onChange={(event) => setSelectedSmartPlanId(event.target.value)}>
              {crmSmartPlans.map((plan) => <option key={plan.id} value={plan.id}>{plan.name}</option>)}
            </select>
          </label>
          <button className="btn primary" onClick={applySmartPlan}>
            <Icon name="ListPlus" />
            <span>Apply {selectedPlan?.name || "Smart Plan"}</span>
          </button>
          <article>
            <strong>First text script</strong>
            <p>{selectedPlan?.scripts?.text || "Select a smart plan to see suggested scripts."}</p>
          </article>
        </div>

        <div className="crm-ai-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">AI actions</p>
              <h3>Follow-up support</h3>
            </div>
          </div>
          <div className="crm-ai-actions">
            {aiActions.map(([action, label, icon]) => (
              <button className="btn secondary" key={action} onClick={() => runAi(action, label)} disabled={Boolean(aiBusy)}>
                <Icon name={aiBusy === action ? "LoaderCircle" : icon} className={aiBusy === action ? "spin" : ""} />
                <span>{label}</span>
              </button>
            ))}
          </div>
          {aiOutput && (
            <article className="ai-output-card">
              <div className="output-card-head">
                <span>{aiOutput.label}</span>
                <button className="icon-btn small" onClick={() => copyToClipboard(aiOutput.output, setToast)} title="Copy AI output">
                  <Icon name="Copy" size={15} />
                </button>
              </div>
              <p>{aiOutput.output}</p>
            </article>
          )}
        </div>

        <div className="crm-communication-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Communication log</p>
              <h3>Log calls, texts, emails, voicemails, and appointments</h3>
            </div>
          </div>
          <form className="crm-communication-form" onSubmit={addCommunication}>
            <Field label="Type" value={communicationDraft.type} onChange={(value) => setCommunicationDraft((current) => ({ ...current, type: value }))} as="select" options={crmCommunicationTypes} />
            <Field label="Outcome" value={communicationDraft.outcome} onChange={(value) => setCommunicationDraft((current) => ({ ...current, outcome: value }))} as="select" options={crmCommunicationOutcomes} />
            <Field label="Next follow-up" value={communicationDraft.nextFollowUpDate} onChange={(value) => setCommunicationDraft((current) => ({ ...current, nextFollowUpDate: value }))} type="date" />
            <Field label="Next step" value={communicationDraft.nextStep} onChange={(value) => setCommunicationDraft((current) => ({ ...current, nextStep: value }))} />
            <Field label="Notes" value={communicationDraft.notes} onChange={(value) => setCommunicationDraft((current) => ({ ...current, notes: value }))} as="textarea" />
            <button className="btn primary">
              <Icon name="Activity" />
              <span>Log Communication</span>
            </button>
          </form>
        </div>

        <div className="crm-split-panels">
          <div className="crm-inner-panel">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Notes</p>
                <h3>Activity timeline</h3>
              </div>
            </div>
            <form className="crm-inline-form" onSubmit={addNote}>
              <Field label="Add note" value={noteDraft} onChange={setNoteDraft} as="textarea" placeholder="Log a call, meeting, or client preference..." />
              <button className="btn primary">
                <Icon name="Plus" />
                <span>Add Note</span>
              </button>
            </form>
            <div className="crm-timeline-list">
              {[...selectedNotes.map((note) => ({ ...note, type: "note", title: "Note" })), ...selectedActivity].slice(0, 10).map((item) => (
                <article key={`${item.type}-${item.id}`}>
                  <span>{item.title || item.type} - {crmShortDate(item.createdAt)}</span>
                  <p>{item.body}</p>
                </article>
              ))}
              {!selectedNotes.length && !selectedActivity.length && <p className="muted-text">No timeline activity yet.</p>}
            </div>
          </div>

          <div className="crm-inner-panel">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Tasks</p>
                <h3>Follow-up reminders</h3>
              </div>
            </div>
            <form className="crm-inline-form" onSubmit={addTask}>
              <Field label="Task" value={taskDraft.title} onChange={(value) => setTaskDraft((current) => ({ ...current, title: value }))} placeholder="Call client, send homes, request docs..." />
              <Field label="Task type" value={taskDraft.taskType} onChange={(value) => setTaskDraft((current) => ({ ...current, taskType: value }))} as="select" options={crmTaskTypes} />
              <Field label="Due date" value={taskDraft.dueDate} onChange={(value) => setTaskDraft((current) => ({ ...current, dueDate: value }))} type="date" />
              <Field label="Due time" value={taskDraft.dueTime} onChange={(value) => setTaskDraft((current) => ({ ...current, dueTime: value }))} type="time" />
              <Field label="Priority" value={taskDraft.priority} onChange={(value) => setTaskDraft((current) => ({ ...current, priority: value }))} as="select" options={crmTaskPriorities} />
              <Field label="Task notes" value={taskDraft.notes} onChange={(value) => setTaskDraft((current) => ({ ...current, notes: value }))} as="textarea" />
              <button className="btn primary">
                <Icon name="Plus" />
                <span>Add Task</span>
              </button>
            </form>
            <div className="crm-task-stack">
              {selectedTasks.map((task) => (
                <article key={task.id} className={classNames(task.status === "Completed" && "done", crmIsDue(task.dueDate) && "overdue")}>
                  <strong>{task.title}</strong>
                  <span>{task.taskType || "Follow-up"} - {task.priority} - {crmShortDate(task.dueDate)}{task.dueTime ? ` at ${task.dueTime}` : ""} - {task.status}</span>
                  {task.notes && <p>{task.notes}</p>}
                  <div className="row-actions">
                    <button className="btn secondary small-btn" onClick={() => updateTask(task.id, { status: task.status === "Completed" ? "Open" : "Completed" })}>
                      {task.status === "Completed" ? "Reopen" : "Complete"}
                    </button>
                    <button className="icon-btn small" onClick={() => deleteTask(task.id)} title="Delete task">
                      <Icon name="Trash2" size={15} />
                    </button>
                  </div>
                </article>
              ))}
              {!selectedTasks.length && <p className="muted-text">No follow-up tasks yet.</p>}
            </div>
          </div>
        </div>
      </div>
    );
  }

  function renderContacts({ leadOnly = false } = {}) {
    const listContacts = leadOnly
      ? filteredContacts.filter((contact) => leadContacts.some((lead) => lead.id === contact.id))
      : filteredContacts;
    return (
      <div className="crm-layout">
        <div className="panel crm-list-panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">{leadOnly ? "Lead inbox" : "Contact database"}</p>
              <h3>{leadOnly ? "Active leads" : "Contacts and leads"}</h3>
            </div>
          </div>
          <div className="crm-filter-grid">
            <Field label="Search" value={query} onChange={setQuery} placeholder="Name, email, area, tag..." />
            <Field label="Lead type" value={leadTypeFilter} onChange={setLeadTypeFilter} as="select" options={["All", ...crmLeadTypes]} />
            <Field label="Stage" value={stageFilter} onChange={setStageFilter} as="select" options={["All", ...stages]} />
            <Field label="Source" value={sourceFilter} onChange={setSourceFilter} as="select" options={sourceOptions} />
            <Field label="Temperature" value={temperatureFilter} onChange={setTemperatureFilter} as="select" options={crmTemperatureOptions} />
            <Field label="Assigned agent" value={assignedFilter} onChange={setAssignedFilter} as="select" options={agentOptions} />
          </div>
          <div className="crm-contact-list">
            {listContacts.map((contact) => {
              const temperature = crmTemperature(contact, crmContactActivityItems(contact, notes, activity));
              return (
              <button
                key={contact.id}
                className={classNames("crm-contact-card", selectedContact?.id === contact.id && "active")}
                onClick={() => setSelectedContactId(contact.id)}
              >
                <div className="crm-contact-card-head">
                  <strong>{crmContactName(contact)}</strong>
                  <em className={classNames("temperature-badge", crmTemperatureClass(temperature))}>{temperature}</em>
                </div>
                <span>{contact.leadType} - {contact.stage}</span>
                <span>{contact.leadSource || "No source"} - {contact.assignedAgentEmail || "Unassigned"}</span>
                <em>{contact.nextFollowUpDate ? `Next: ${crmShortDate(contact.nextFollowUpDate)}` : contact.desiredArea || "No follow-up set"}</em>
                <small>{contact.leadScore || crmLeadScore(contact)} score</small>
              </button>
            );})}
            {!listContacts.length && <EmptyState icon="Search" title="No CRM contacts found" body="Try a different filter or add a new contact below." />}
          </div>

          <form className="crm-add-form" onSubmit={createContact}>
            <div className="panel-heading compact-heading">
              <div>
                <p className="eyebrow">Add contact</p>
                <h3>New lead</h3>
              </div>
            </div>
            <div className="crm-form-grid compact">
              <Field label="First name" value={draft.firstName} onChange={(value) => setDraft((current) => ({ ...current, firstName: value }))} />
              <Field label="Last name" value={draft.lastName} onChange={(value) => setDraft((current) => ({ ...current, lastName: value }))} />
              <Field label="Email" value={draft.email} onChange={(value) => setDraft((current) => ({ ...current, email: value }))} type="email" />
              <Field label="Phone" value={draft.phone} onChange={(value) => setDraft((current) => ({ ...current, phone: value }))} />
              <Field label="Lead type" value={draft.leadType} onChange={(value) => setDraft((current) => ({ ...current, leadType: value }))} as="select" options={crmLeadTypes} />
              <Field label="Stage" value={draft.stage} onChange={(value) => setDraft((current) => ({ ...current, stage: value }))} as="select" options={crmPipelineStagesForType(draft.leadType, stages)} />
              <Field label="Lead source" value={draft.leadSource} onChange={(value) => setDraft((current) => ({ ...current, leadSource: value }))} as="select" options={crmLeadSources} />
              <Field label="Assigned agent email" value={draft.assignedAgentEmail} onChange={(value) => setDraft((current) => ({ ...current, assignedAgentEmail: value }))} type="email" />
              <Field label="Budget / price range" value={draft.budgetRange} onChange={(value) => setDraft((current) => ({ ...current, budgetRange: value }))} />
              <Field label="Desired area" value={draft.desiredArea} onChange={(value) => setDraft((current) => ({ ...current, desiredArea: value }))} />
              <Field label="Timeline" value={draft.timeline} onChange={(value) => setDraft((current) => ({ ...current, timeline: value }))} />
              <Field label="Next follow-up" value={draft.nextFollowUpDate} onChange={(value) => setDraft((current) => ({ ...current, nextFollowUpDate: value }))} type="date" />
              <Field label="Campaign name" value={draft.campaignName} onChange={(value) => setDraft((current) => ({ ...current, campaignName: value }))} />
              <Field label="Tags" value={draft.tags} onChange={(value) => setDraft((current) => ({ ...current, tags: value }))} />
            </div>
            <Field label="Notes" value={draft.notes} onChange={(value) => setDraft((current) => ({ ...current, notes: value }))} as="textarea" />
            <button className="btn primary">
              <Icon name="Plus" />
              <span>Add Contact</span>
            </button>
          </form>
        </div>

        <div className="panel crm-detail-panel">
          {renderContactDetail()}
        </div>
      </div>
    );
  }

  function renderPipeline() {
    const boardStages = pipelineView === "All" ? stages : crmPipelineStagesForType(pipelineView, stages);
    const boardContacts = pipelineView === "All" ? filteredContacts : filteredContacts.filter((contact) => (contact.leadType === pipelineView || (pipelineView === "Agent Recruit" && contact.leadType === "Recruit")));
    return (
      <div className="panel crm-board-panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Pipeline board</p>
            <h3>Smart real estate pipeline</h3>
          </div>
          <div className="row-actions">
            {["Buyer", "Seller", "Renter", "Agent Recruit", "Investor", "All"].map((view) => (
              <button key={view} className={classNames("btn secondary small-btn", pipelineView === view && "active-soft")} onClick={() => setPipelineView(view)}>
                {view}
              </button>
            ))}
          </div>
        </div>
        <div className="pipeline-board">
          {boardStages.map((stage) => {
            const stageContacts = boardContacts.filter((contact) => contact.stage === stage);
            return (
              <section className="pipeline-column" key={stage}>
                <div className="pipeline-column-head">
                  <strong>{stage}</strong>
                  <span>{stageContacts.length}</span>
                </div>
                {stageContacts.map((contact) => (
                  <article className="pipeline-card" key={contact.id}>
                    <button onClick={() => { setSelectedContactId(contact.id); setActiveCrmPage("Contacts"); }}>
                      <div className="pipeline-card-head">
                        <strong>{crmContactName(contact)}</strong>
                        <em className={classNames("temperature-badge", crmTemperatureClass(crmTemperature(contact, crmContactActivityItems(contact, notes, activity))))}>{contact.leadScore || crmLeadScore(contact)}</em>
                      </div>
                      <span>{contact.leadType} - {contact.desiredArea || contact.leadSource || "No area set"}</span>
                      <small>{contact.nextFollowUpDate ? `Next ${crmShortDate(contact.nextFollowUpDate)}` : "No next follow-up"}</small>
                    </button>
                    <select value={contact.stage} onChange={(event) => updateContact(contact.id, { stage: event.target.value }, "Pipeline stage updated.")}>
                      {Array.from(new Set([...crmPipelineStagesForType(contact.leadType, stages), contact.stage].filter(Boolean))).map((option) => <option key={option} value={option}>{option}</option>)}
                    </select>
                  </article>
                ))}
                {!stageContacts.length && <p className="pipeline-empty">No leads in this stage.</p>}
              </section>
            );
          })}
        </div>
      </div>
    );
  }

  function renderTasks() {
    const visibleTasks = openTasks.filter((task) => {
      if (taskView === "Today") return crmIsDueToday(task.dueDate);
      if (taskView === "Overdue") return crmIsDue(task.dueDate);
      if (taskView === "Upcoming") return !crmIsDue(task.dueDate) && !crmIsDueToday(task.dueDate);
      return true;
    });
    return (
      <div className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Tasks and follow-ups</p>
            <h3>Daily accountability queue</h3>
          </div>
          <div className="row-actions">
            {["Today", "Overdue", "Upcoming", "All"].map((view) => (
              <button key={view} className={classNames("btn secondary small-btn", taskView === view && "active-soft")} onClick={() => setTaskView(view)}>
                {view}
              </button>
            ))}
          </div>
        </div>
        <div className="crm-task-table">
          {visibleTasks.map((task) => {
            const contact = contacts.find((item) => item.id === task.contactId);
            return (
              <article key={task.id} className={classNames(crmIsDue(task.dueDate) && "overdue")}>
                <div>
                  <strong>{task.title}</strong>
                  <span>{crmContactName(contact)} - {task.taskType || "Follow-up"} - {task.priority} - {crmShortDate(task.dueDate)}{task.dueTime ? ` at ${task.dueTime}` : ""}</span>
                  {task.notes && <p>{task.notes}</p>}
                </div>
                <div className="row-actions">
                  <button className="btn secondary small-btn" onClick={() => { setSelectedContactId(task.contactId); setActiveCrmPage("Contacts"); }}>Open Contact</button>
                  <button className="btn secondary small-btn" onClick={() => updateTask(task.id, { status: "Completed" })}>Complete</button>
                </div>
              </article>
            );
          })}
          {!visibleTasks.length && <EmptyState icon="ListTodo" title="No tasks in this view" body="Add follow-up reminders from any contact profile or apply a smart plan." />}
        </div>
      </div>
    );
  }

  function renderSmartLists() {
    return (
      <div className="crm-smart-layout">
        <aside className="panel crm-smart-list-sidebar">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Smart lists</p>
              <h3>Lead intelligence</h3>
            </div>
          </div>
          <div className="crm-smart-list-buttons">
            {smartLists.map((list) => (
              <button key={list.key} className={classNames(smartListKey === list.key && "active")} onClick={() => setSmartListKey(list.key)}>
                <Icon name={list.icon} />
                <span>{list.label}</span>
                <strong>{contacts.filter(list.predicate).length}</strong>
              </button>
            ))}
          </div>
        </aside>

        <section className="panel crm-smart-results">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Selected list</p>
              <h3>{selectedSmartList?.label || "Smart list"}</h3>
            </div>
            <button className="btn secondary small-btn" onClick={() => { setStageFilter("All"); setLeadTypeFilter("All"); setSourceFilter("All"); setTemperatureFilter("All"); }}>
              Clear Filters
            </button>
          </div>
          <div className="crm-lead-table">
            {smartListContacts.map((contact) => {
              const temperature = crmTemperature(contact, crmContactActivityItems(contact, notes, activity));
              return (
                <button key={contact.id} onClick={() => { setSelectedContactId(contact.id); setActiveCrmPage("Contacts"); }}>
                  <div>
                    <strong>{crmContactName(contact)}</strong>
                    <span>{contact.leadType} - {contact.stage} - {contact.leadSource || "No source"}</span>
                  </div>
                  <em className={classNames("temperature-badge", crmTemperatureClass(temperature))}>{temperature}</em>
                  <span>{contact.leadScore || crmLeadScore(contact)} score</span>
                  <span>{contact.nextFollowUpDate ? crmShortDate(contact.nextFollowUpDate) : "No follow-up"}</span>
                  <span>{contact.assignedAgentEmail || "Unassigned"}</span>
                </button>
              );
            })}
            {!smartListContacts.length && <EmptyState icon="Search" title="No leads in this smart list" body="As CRM activity grows, matching leads will appear here automatically." />}
          </div>
        </section>
      </div>
    );
  }

  function renderImportExport() {
    const sample = "firstName,lastName,email,phone,leadType,leadSource,stage,budgetRange,desiredArea,timeline,tags,nextFollowUpDate,campaignName,propertyAddress,preApproved,consentStatus\nTaylor,Smith,taylor@example.com,555-0199,Buyer,Website,New Lead,$500K-$650K,North Dallas,90 days,website lead,2026-06-01,Home Search Form,,Yes,Website Form";
    return (
      <div className="crm-import-grid">
        <div className="panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Import</p>
              <h3>CSV contact upload</h3>
            </div>
          </div>
          <form onSubmit={importCsv} className="crm-inline-form">
            <input className="file-input" type="file" accept=".csv,text/csv" onChange={handleCsvFile} />
            <Field label="Paste CSV" value={csvText} onChange={setCsvText} as="textarea" placeholder={sample} />
            <button className="btn primary">
              <Icon name="Upload" />
              <span>Import Contacts</span>
            </button>
          </form>
        </div>
        <div className="panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Export</p>
              <h3>Download CRM contacts</h3>
            </div>
          </div>
          <p className="crm-helper-text">Exports the contacts visible to your role. Agents export their own contacts; admins export the full brokerage CRM view.</p>
          <button className="btn dark" onClick={exportCsv}>
            <Icon name="Download" />
            <span>Export CSV</span>
          </button>
          <div className="csv-sample">
            <strong>Supported headers</strong>
            <p>firstName, lastName, email, phone, leadType, leadSource, stage, assignedAgentEmail, budgetRange, desiredArea, timeline, tags, nextFollowUpDate, campaignName, propertyAddress, preApproved, consentStatus, notes</p>
          </div>
        </div>
      </div>
    );
  }

  function renderSettings() {
    return (
      <div className="crm-settings-grid">
        <div className="panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">CRM settings</p>
              <h3>Pipeline stages</h3>
            </div>
          </div>
          {permissions.canManageStages ? (
            <>
              <Field label="Stages, one per line" value={stagesText} onChange={setStagesText} as="textarea" />
              <button className="btn primary" onClick={saveStages}>
                <Icon name="Save" />
                <span>Save Stages</span>
              </button>
            </>
          ) : (
            <div className="crm-stage-tags">
              {stages.map((stage) => <span key={stage}>{stage}</span>)}
            </div>
          )}
        </div>
        <div className="panel">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Future-ready</p>
              <h3>Integration path</h3>
            </div>
          </div>
          <div className="native-readiness-list">
            <article><strong>BoldTrail bridge</strong><span>Keep Nex lite CRM simple now, then sync lead/contact records later.</span></article>
            <article><strong>Calendar and Gmail</strong><span>Tasks and contact activity are structured for future calendar and email integrations.</span></article>
            <article><strong>Standalone CRM option</strong><span>The API-first CRM can become its own app and stay connected to Nex Central.</span></article>
          </div>
        </div>
      </div>
    );
  }

  function renderActiveCrmPage() {
    if (loading) return <EmptyState icon="LoaderCircle" title="Loading Nex lite CRM" body="Preparing contacts, tasks, pipeline, and activity." />;
    if (activeCrmPage === "Contacts") return renderContacts();
    if (activeCrmPage === "Leads") return renderContacts({ leadOnly: true });
    if (activeCrmPage === "Pipeline" || activeCrmPage === "Pipeline Board") return renderPipeline();
    if (activeCrmPage === "Smart Lists") return renderSmartLists();
    if (activeCrmPage === "Follow-Up Tasks" || activeCrmPage === "Tasks/Follow-Ups") return renderTasks();
    if (activeCrmPage === "Import/Export") return renderImportExport();
    if (activeCrmPage === "CRM Settings") return renderSettings();
    return renderDashboard();
  }

  return (
    <section className="portal-page crm-module">
      <div className="module-hero crm-hero">
        <div>
          <p className="eyebrow">Nex lite CRM</p>
          <h3>Lead conversion cockpit for Nex agents.</h3>
          <p>
            Manage leads, smart lists, follow-ups, source tracking, pipeline stages, communication history,
            scoring, and action plans now while keeping the structure ready for BoldTrail, Gmail, SMS, calendar,
            landing page, and webhook integrations later.
          </p>
          <div className="hero-actions">
            <button className="btn light" onClick={() => setActiveCrmPage("Leads")}>
              <Icon name="UserPlus" />
              <span>Lead Inbox</span>
            </button>
            <button className="btn light" onClick={() => setActiveCrmPage("Follow-Up Tasks")}>
              <Icon name="ListTodo" />
              <span>Open Follow-Ups</span>
            </button>
            <button className="btn light" onClick={() => setActiveCrmPage("Smart Lists")}>
              <Icon name="Sparkles" />
              <span>Smart Lists</span>
            </button>
            <button className="btn light" onClick={refreshCrm}>
              <Icon name="RefreshCcw" />
              <span>Refresh CRM</span>
            </button>
          </div>
        </div>
      </div>

      <div className="crm-tabs">
        {crmPages.map((page) => (
          <button key={page} className={classNames(activeCrmPage === page && "active")} onClick={() => setActiveCrmPage(page)}>
            {page}
          </button>
        ))}
      </div>

      {renderActiveCrmPage()}
    </section>
  );
}

function DeadlineSummaryCard({ transaction, tasks }) {
  const next = nextBuyerDeadline(tasks);
  const percent = buyerCompletionPercent(tasks);
  const overdue = buyerOverdueCount(tasks);
  const days = next ? daysUntil(next.dueDate) : null;
  return (
    <div className="deadline-summary-card">
      <div>
        <p className="eyebrow">Next deadline</p>
        <h3>{next ? next.taskName : "No open dated tasks"}</h3>
        <span>
          {next ? `${formatDate(next.dueDate)}${days === 0 ? " - due today" : days > 0 ? ` - ${days} day${days === 1 ? "" : "s"} left` : ` - ${Math.abs(days)} day${Math.abs(days) === 1 ? "" : "s"} overdue`}` : "Add due dates to track upcoming items."}
        </span>
      </div>
      <div className="deadline-summary-metrics">
        <strong>{percent}%</strong>
        <span>Complete</span>
        <strong className={overdue ? "danger" : ""}>{overdue}</strong>
        <span>Overdue</span>
      </div>
      <div className="progress-track">
        <div style={{ width: `${percent}%` }} />
      </div>
      {transaction && <em>{buyerDisplayStatus(transaction)}</em>}
    </div>
  );
}

function BuyerTransactionCard({ transaction, tasks, onOpen }) {
  const percent = buyerCompletionPercent(tasks);
  const next = nextBuyerDeadline(tasks);
  const overdue = buyerOverdueCount(tasks);
  return (
    <article className="buyer-transaction-card">
      <button onClick={onOpen}>
        <div className="buyer-card-head">
          <span className={classNames("status-badge", buyerDisplayStatus(transaction).toLowerCase().replace(/\s+/g, "-"))}>
            {buyerDisplayStatus(transaction)}
          </span>
          <strong>{transaction.transactionName}</strong>
        </div>
        <p>{transaction.propertyAddress || "No property address entered"}</p>
        <div className="buyer-card-meta">
          <span>Buyer: {transaction.buyerName || "Not set"}</span>
          <span>Closing: {formatDate(transaction.closingDate)}</span>
          <span>Next: {next ? `${next.taskName} (${formatDate(next.dueDate)})` : "No upcoming deadline"}</span>
          <span>{overdue} overdue</span>
        </div>
        <div className="progress-track">
          <div style={{ width: `${percent}%` }} />
        </div>
        <em>{percent}% complete</em>
      </button>
    </article>
  );
}

function ChecklistTaskItem({ task, onUpdate, onDelete }) {
  const tone = buyerTaskTone(task);
  const days = task.dueDate ? daysUntil(task.dueDate) : null;
  return (
    <article className={classNames("checklist-task-item", `tone-${tone}`, task.completed && "completed")}>
      <label className="task-check">
        <input type="checkbox" checked={Boolean(task.completed)} onChange={(event) => onUpdate(task.id, { completed: event.target.checked })} />
        <span />
      </label>
      <div className="task-main">
        <div className="task-title-row">
          <strong>{task.taskName}</strong>
          <span className={classNames("deadline-badge", `tone-${tone}`)}>
            {task.completed
              ? "Completed"
              : task.dueDate
                ? days < 0
                  ? `Overdue ${Math.abs(days)}d`
                  : days === 0
                    ? "Due today"
                    : days === 1
                      ? "Due tomorrow"
                      : days <= 3
                        ? `Due in ${days}d`
                        : formatDate(task.dueDate)
                : "No due date"}
          </span>
        </div>
        <div className="task-edit-grid">
          <Field label="Due date" value={task.dueDate || ""} onChange={(value) => onUpdate(task.id, { dueDate: value })} type="date" />
          <Field label="Due time" value={task.dueTime || ""} onChange={(value) => onUpdate(task.id, { dueTime: value })} type="time" />
          <Field label="Priority" value={task.priority || "Normal"} onChange={(value) => onUpdate(task.id, { priority: value })} as="select" options={buyerTaskPriorities} />
          <Field label="Assigned person" value={task.assignedTo || ""} onChange={(value) => onUpdate(task.id, { assignedTo: value })} />
          <Field label="Linked document" value={task.linkedDocument || ""} onChange={(value) => onUpdate(task.id, { linkedDocument: value })} />
          <Field label="Notes" value={task.notes || ""} onChange={(value) => onUpdate(task.id, { notes: value })} as="textarea" />
        </div>
      </div>
      {task.isCustom && (
        <button className="icon-btn small" onClick={() => onDelete(task.id)} title="Delete custom task">
          <Icon name="Trash2" size={15} />
        </button>
      )}
    </article>
  );
}

function ChecklistSection({ section, tasks, expanded, onToggle, onUpdateTask, onDeleteTask }) {
  const completedCount = tasks.filter((task) => task.completed).length;
  const openTasks = tasks.filter((task) => !task.completed);
  const previewTasks = openTasks.slice(0, 3);
  const deadlineAlerts = tasks.filter((task) => !task.completed && ["overdue", "today", "tomorrow", "soon"].includes(buyerTaskTone(task))).length;
  const percent = buyerCompletionPercent(tasks);

  return (
    <section className={classNames("checklist-section", !expanded && "collapsed")}>
      <button className="checklist-section-head" type="button" onClick={onToggle} aria-expanded={expanded}>
        <div className="checklist-section-title">
          <Icon name={expanded ? "ChevronDown" : "ChevronRight"} />
          <div>
            <h3>{section}</h3>
            <p>
              {completedCount}/{tasks.length} complete - {openTasks.length} open
              {deadlineAlerts ? ` - ${deadlineAlerts} deadline alert${deadlineAlerts === 1 ? "" : "s"}` : ""}
            </p>
          </div>
        </div>
        <span>{percent}%</span>
      </button>
      {!expanded && (
        <div className="checklist-section-preview">
          {previewTasks.map((task) => (
            <div key={task.id} className={classNames("preview-task", `tone-${buyerTaskTone(task)}`)}>
              <strong>{task.taskName}</strong>
              <small>{task.dueDate ? formatDate(task.dueDate) : "No due date"}</small>
            </div>
          ))}
          {!previewTasks.length && <span>All tasks complete in this section.</span>}
          {openTasks.length > previewTasks.length && <em>+{openTasks.length - previewTasks.length} more open task{openTasks.length - previewTasks.length === 1 ? "" : "s"}</em>}
        </div>
      )}
      {expanded && (
        <div className="checklist-task-list">
          {tasks.map((task) => (
            <ChecklistTaskItem key={task.id} task={task} onUpdate={onUpdateTask} onDelete={onDeleteTask} />
          ))}
        </div>
      )}
    </section>
  );
}

function ReminderSettingsPanel({ settings, onSave }) {
  const [draft, setDraft] = useState(settings || {});

  useEffect(() => {
    setDraft(settings || {});
  }, [settings?.id, settings?.updatedAt]);

  if (!settings) return null;

  const options = [
    ["remind3DaysBefore", "Notify me 3 days before"],
    ["remind1DayBefore", "Notify me 1 day before"],
    ["remindSameDay", "Notify me same day"],
    ["remindOverdue", "Notify me when overdue"],
    ["inAppEnabled", "In-app reminders enabled"],
    ["emailEnabled", "Email reminders enabled"]
  ];

  return (
    <div className="reminder-settings-panel">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Reminders</p>
          <h3>Notification settings</h3>
        </div>
      </div>
      <div className="check-grid">
        {options.map(([key, label]) => (
          <label className="check-field" key={key}>
            <input type="checkbox" checked={Boolean(draft[key])} onChange={(event) => setDraft((current) => ({ ...current, [key]: event.target.checked }))} />
            {label}
          </label>
        ))}
      </div>
      <p className="muted-text">Email reminder delivery is prepared in the data model, but no email provider is configured yet.</p>
      <button className="btn secondary" onClick={() => onSave(settings.id, draft)}>
        <Icon name="Save" />
        <span>Save Reminders</span>
      </button>
    </div>
  );
}

function NotificationCenter({ notifications, transactions, tasks, onMarkRead, onOpenTransaction }) {
  return (
    <div className="panel notification-center">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Notifications</p>
          <h3>Buyer contract reminders</h3>
        </div>
      </div>
      <div className="notification-list">
        {notifications.map((notification) => {
          const transaction = transactions.find((item) => item.id === notification.transactionId);
          const task = tasks.find((item) => item.id === notification.taskId);
          return (
            <article className={classNames(!notification.read && "unread")} key={notification.id}>
              <div>
                <span>{notification.notificationType}</span>
                <strong>{notification.title}</strong>
                <p>{notification.message}</p>
                <em>{transaction?.transactionName || "Buyer transaction"}{task ? ` - ${task.taskName}` : ""}</em>
              </div>
              <div className="row-actions">
                {transaction && <button className="btn secondary small-btn" onClick={() => onOpenTransaction(transaction.id)}>Open</button>}
                {!notification.read && <button className="btn secondary small-btn" onClick={() => onMarkRead(notification.id)}>Mark Read</button>}
              </div>
            </article>
          );
        })}
        {!notifications.length && <EmptyState icon="Bell" title="No buyer tracker notifications" body="Upcoming and overdue deadline reminders will appear here." />}
      </div>
    </div>
  );
}

function CreateBuyerTransactionForm({ currentUser, onCreate }) {
  const [draft, setDraft] = useState({
    ...emptyBuyerTransaction,
    agentName: currentUser?.name || "",
    agentEmail: currentUser?.email || ""
  });

  function update(field, value) {
    setDraft((current) => ({ ...current, [field]: value }));
  }

  function submit(event) {
    event.preventDefault();
    onCreate(draft);
  }

  return (
    <form className="panel buyer-create-form" onSubmit={submit}>
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Create New Transaction</p>
          <h3>Buyer under-contract checklist</h3>
        </div>
      </div>
      <div className="buyer-form-grid">
        <Field label="Transaction name" value={draft.transactionName} onChange={(value) => update("transactionName", value)} placeholder="Smith purchase - 123 Main St" />
        <Field label="Buyer name" value={draft.buyerName} onChange={(value) => update("buyerName", value)} />
        <Field label="Property address" value={draft.propertyAddress} onChange={(value) => update("propertyAddress", value)} />
        <Field label="MLS number optional" value={draft.mlsNumber} onChange={(value) => update("mlsNumber", value)} />
        <Field label="Contract effective date" value={draft.effectiveDate} onChange={(value) => update("effectiveDate", value)} type="date" />
        <Field label="Closing date" value={draft.closingDate} onChange={(value) => update("closingDate", value)} type="date" />
        <Field label="Inspection period end date" value={draft.inspectionPeriodEndDate} onChange={(value) => update("inspectionPeriodEndDate", value)} type="date" />
        <Field label="Financing period end date" value={draft.financingPeriodEndDate} onChange={(value) => update("financingPeriodEndDate", value)} type="date" />
        <Field label="Appraisal deadline optional" value={draft.appraisalDeadline} onChange={(value) => update("appraisalDeadline", value)} type="date" />
        <Field label="HOA application deadline optional" value={draft.hoaDeadline} onChange={(value) => update("hoaDeadline", value)} type="date" />
        <Field label="EMD due date" value={draft.emdDueDate} onChange={(value) => update("emdDueDate", value)} type="date" />
        <Field label="Final walkthrough date" value={draft.finalWalkthroughDate} onChange={(value) => update("finalWalkthroughDate", value)} type="date" />
        <Field label="Agent name" value={draft.agentName} onChange={(value) => update("agentName", value)} />
        <Field label="Agent email" value={draft.agentEmail} onChange={(value) => update("agentEmail", value)} type="email" />
        <Field label="Agent phone" value={draft.agentPhone} onChange={(value) => update("agentPhone", value)} />
        <Field label="Notes" value={draft.notes} onChange={(value) => update("notes", value)} as="textarea" />
      </div>
      <button className="btn primary">
        <Icon name="ClipboardCheck" />
        <span>Generate Checklist</span>
      </button>
    </form>
  );
}

function BuyerTransactionList({ transactions, tasks, filters, setFilters, permissions, onOpen }) {
  const agentOptions = ["All", ...Array.from(new Set(transactions.map((transaction) => transaction.userEmail).filter(Boolean)))];
  const filtered = transactions.filter((transaction) => {
    const transactionTasks = tasks.filter((task) => task.transactionId === transaction.id);
    const matchesStatus = filters.status === "All" || buyerDisplayStatus(transaction) === filters.status || transaction.status === filters.status;
    const matchesAgent = filters.agent === "All" || transaction.userEmail === filters.agent;
    const matchesOverdue = !filters.overdueOnly || buyerOverdueCount(transactionTasks) > 0;
    const matchesClosing = !filters.closingBefore || (transaction.closingDate && transaction.closingDate <= filters.closingBefore);
    return matchesStatus && matchesAgent && matchesOverdue && matchesClosing;
  });

  return (
    <div className="buyer-list-page">
      <div className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Transaction list</p>
            <h3>Buyer contract checklists</h3>
          </div>
        </div>
        <div className="buyer-filter-grid">
          <Field label="Status" value={filters.status} onChange={(value) => setFilters((current) => ({ ...current, status: value }))} as="select" options={["All", ...buyerStatuses]} />
          {permissions.canViewAll && <Field label="Agent" value={filters.agent} onChange={(value) => setFilters((current) => ({ ...current, agent: value }))} as="select" options={agentOptions} />}
          <Field label="Closing before" value={filters.closingBefore} onChange={(value) => setFilters((current) => ({ ...current, closingBefore: value }))} type="date" />
          <label className="check-field buyer-overdue-filter">
            <input type="checkbox" checked={filters.overdueOnly} onChange={(event) => setFilters((current) => ({ ...current, overdueOnly: event.target.checked }))} />
            Overdue only
          </label>
        </div>
      </div>
      <div className="buyer-transaction-grid">
        {filtered.map((transaction) => (
          <BuyerTransactionCard
            key={transaction.id}
            transaction={transaction}
            tasks={tasks.filter((task) => task.transactionId === transaction.id)}
            onOpen={() => onOpen(transaction.id)}
          />
        ))}
        {!filtered.length && <EmptyState icon="ClipboardCheck" title="No buyer contract checklists yet" body="Create a transaction to generate the default checklist." />}
      </div>
    </div>
  );
}

function BuyerTransactionDetail({ transaction, tasks, reminderSettings, onBack, onUpdateTransaction, onUpdateTask, onDeleteTask, onAddTask, onSaveReminders }) {
  const [customTask, setCustomTask] = useState({ section: "Custom Tasks", taskName: "", dueDate: "", dueTime: "", priority: "Normal", assignedTo: "", linkedDocument: "", notes: "" });
  const [expandedSections, setExpandedSections] = useState({});
  const sectionGroups = useMemo(() => {
    const sectionNames = Array.from(new Set(tasks.map((task) => task.section || "General")));
    return sectionNames.map((section) => ({
      section,
      tasks: tasks
        .filter((task) => (task.section || "General") === section)
        .sort((a, b) => Number(a.order || 0) - Number(b.order || 0))
    }));
  }, [tasks]);
  const sectionNamesKey = sectionGroups.map((group) => group.section).join("|");

  useEffect(() => {
    setExpandedSections((current) => {
      const next = {};
      sectionGroups.forEach((group, index) => {
        const hasUrgentOpenTask = group.tasks.some((task) => !task.completed && ["overdue", "today", "tomorrow"].includes(buyerTaskTone(task)));
        next[group.section] = Object.prototype.hasOwnProperty.call(current, group.section)
          ? current[group.section]
          : index === 0 || hasUrgentOpenTask;
      });
      return next;
    });
  }, [transaction.id, sectionNamesKey]);

  function addCustom(event) {
    event.preventDefault();
    if (!customTask.taskName.trim()) return;
    onAddTask(transaction.id, customTask);
    setCustomTask({ section: "Custom Tasks", taskName: "", dueDate: "", dueTime: "", priority: "Normal", assignedTo: "", linkedDocument: "", notes: "" });
  }

  function toggleSection(section) {
    setExpandedSections((current) => ({ ...current, [section]: !current[section] }));
  }

  function expandAllSections() {
    setExpandedSections(sectionGroups.reduce((next, group) => ({ ...next, [group.section]: true }), {}));
  }

  function showPrioritySections() {
    setExpandedSections(
      sectionGroups.reduce((next, group, index) => {
        const hasDeadlineAlert = group.tasks.some((task) => !task.completed && ["overdue", "today", "tomorrow", "soon"].includes(buyerTaskTone(task)));
        const hasOpenTask = group.tasks.some((task) => !task.completed);
        next[group.section] = hasDeadlineAlert || (index === 0 && hasOpenTask);
        return next;
      }, {})
    );
  }

  return (
    <section className="buyer-detail-page">
      <button className="btn secondary" onClick={onBack}>
        <Icon name="ArrowLeft" />
        <span>Back to Transactions</span>
      </button>
      <div className="buyer-detail-hero">
        <div>
          <p className="eyebrow">Buyer Contract Tracker</p>
          <h3>{transaction.transactionName}</h3>
          <p>{transaction.propertyAddress || "No property address entered"} - Buyer: {transaction.buyerName || "Not set"}</p>
        </div>
        <div className="row-actions">
          <button className="btn secondary" onClick={() => onUpdateTransaction(transaction.id, { status: "Closed" })}>Mark Closed</button>
          <button className="btn secondary" onClick={() => onUpdateTransaction(transaction.id, { status: "Cancelled" })}>Cancel</button>
        </div>
      </div>
      <DeadlineSummaryCard transaction={transaction} tasks={tasks} />
      <div className="buyer-detail-grid">
        <div className="buyer-checklist-stack">
          <div className="panel buyer-checklist-toolbar">
            <div>
              <p className="eyebrow">Checklist view</p>
              <h3>Work by section</h3>
              <span>Open the section you need, or focus only on deadline-sensitive work.</span>
            </div>
            <div className="row-actions">
              <button className="btn secondary small-btn" type="button" onClick={showPrioritySections}>
                <Icon name="AlertTriangle" />
                <span>Priority View</span>
              </button>
              <button className="btn secondary small-btn" type="button" onClick={expandAllSections}>
                <Icon name="ListChecks" />
                <span>Expand All</span>
              </button>
            </div>
          </div>
          {sectionGroups.map((group) => (
            <ChecklistSection
              key={group.section}
              section={group.section}
              tasks={group.tasks}
              expanded={Boolean(expandedSections[group.section])}
              onToggle={() => toggleSection(group.section)}
              onUpdateTask={onUpdateTask}
              onDeleteTask={onDeleteTask}
            />
          ))}
          <form className="panel custom-task-form" onSubmit={addCustom}>
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Custom task</p>
                <h3>Add an extra checklist item</h3>
              </div>
            </div>
            <div className="buyer-form-grid compact">
              <Field label="Section/category" value={customTask.section} onChange={(value) => setCustomTask((current) => ({ ...current, section: value }))} />
              <Field label="Task name" value={customTask.taskName} onChange={(value) => setCustomTask((current) => ({ ...current, taskName: value }))} />
              <Field label="Due date" value={customTask.dueDate} onChange={(value) => setCustomTask((current) => ({ ...current, dueDate: value }))} type="date" />
              <Field label="Due time" value={customTask.dueTime} onChange={(value) => setCustomTask((current) => ({ ...current, dueTime: value }))} type="time" />
              <Field label="Priority" value={customTask.priority} onChange={(value) => setCustomTask((current) => ({ ...current, priority: value }))} as="select" options={buyerTaskPriorities} />
              <Field label="Assigned person optional" value={customTask.assignedTo} onChange={(value) => setCustomTask((current) => ({ ...current, assignedTo: value }))} />
              <Field label="Linked document optional" value={customTask.linkedDocument} onChange={(value) => setCustomTask((current) => ({ ...current, linkedDocument: value }))} />
              <Field label="Notes" value={customTask.notes} onChange={(value) => setCustomTask((current) => ({ ...current, notes: value }))} as="textarea" />
            </div>
            <button className="btn primary">
              <Icon name="Plus" />
              <span>Add Custom Task</span>
            </button>
          </form>
        </div>
        <aside className="buyer-side-panel">
          <div className="panel">
            <div className="panel-heading">
              <div>
                <p className="eyebrow">Key dates</p>
                <h3>Contract timeline</h3>
              </div>
            </div>
            <div className="buyer-key-date-list">
              {[
                ["Effective", transaction.effectiveDate],
                ["EMD due", transaction.emdDueDate],
                ["Inspection ends", transaction.inspectionPeriodEndDate],
                ["Financing ends", transaction.financingPeriodEndDate],
                ["Appraisal", transaction.appraisalDeadline],
                ["HOA", transaction.hoaDeadline],
                ["Walkthrough", transaction.finalWalkthroughDate],
                ["Closing", transaction.closingDate]
              ].map(([label, value]) => (
                <span key={label}><strong>{label}</strong>{formatDate(value)}</span>
              ))}
            </div>
            <Field label="Status" value={transaction.status || "Active"} onChange={(value) => onUpdateTransaction(transaction.id, { status: value })} as="select" options={buyerStatuses} />
          </div>
          <ReminderSettingsPanel settings={reminderSettings} onSave={onSaveReminders} />
          <ComplianceNotice text={buyerTrackerDisclaimer} />
        </aside>
      </div>
    </section>
  );
}

function BuyerContractTrackerPage({ currentUser, role, setToast, notifications, setNotifications, initialView, onInitialViewConsumed }) {
  const [transactions, setTransactions] = useState([]);
  const [tasks, setTasks] = useState([]);
  const [settings, setSettings] = useState([]);
  const [permissions, setPermissions] = useState({ canViewAll: isAdminRole(role), role });
  const [activeTab, setActiveTab] = useState(initialView || "Transactions");
  const [selectedTransactionId, setSelectedTransactionId] = useState("");
  const [filters, setFilters] = useState({ status: "All", agent: "All", overdueOnly: false, closingBefore: "" });
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    refreshTracker();
  }, []);

  useEffect(() => {
    if (initialView) {
      setActiveTab(initialView);
      onInitialViewConsumed?.();
    }
  }, [initialView]);

  async function refreshTracker() {
    setLoading(true);
    try {
      const payload = await API.request("/api/buyer-tracker/bootstrap");
      setTransactions(payload.transactions || []);
      setTasks(payload.tasks || []);
      setSettings(payload.reminderSettings || []);
      setNotifications(payload.notifications || []);
      setPermissions(payload.permissions || { canViewAll: isAdminRole(role), role });
    } catch (error) {
      setToast(error.message || "Could not load buyer contract tracker.");
    } finally {
      setLoading(false);
    }
  }

  async function createTransaction(draft) {
    try {
      const saved = await API.request("/api/buyer-transactions", { method: "POST", body: JSON.stringify(draft) });
      setTransactions((current) => [saved.transaction, ...current]);
      setTasks((current) => [...saved.tasks, ...current]);
      setSettings((current) => [saved.reminderSettings, ...current]);
      setSelectedTransactionId(saved.transaction.id);
      setActiveTab("Detail");
      setToast("Buyer contract checklist created.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not create buyer checklist.");
    }
  }

  async function updateTransaction(transactionId, patch) {
    try {
      const saved = await API.request(`/api/buyer-transactions/${encodeURIComponent(transactionId)}`, { method: "PUT", body: JSON.stringify(patch) });
      setTransactions((current) => current.map((transaction) => (transaction.id === saved.id ? saved : transaction)));
      setToast("Buyer transaction updated.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not update buyer transaction.");
    }
  }

  async function updateTask(taskId, patch) {
    try {
      const saved = await API.request(`/api/buyer-transaction-tasks/${encodeURIComponent(taskId)}`, { method: "PUT", body: JSON.stringify(patch) });
      setTasks((current) => current.map((task) => (task.id === saved.id ? saved : task)));
      setToast(saved.completed ? "Task completed." : "Task updated.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not update task.");
    }
  }

  async function addTask(transactionId, task) {
    try {
      const saved = await API.request(`/api/buyer-transactions/${encodeURIComponent(transactionId)}/tasks`, { method: "POST", body: JSON.stringify(task) });
      setTasks((current) => [saved, ...current]);
      setToast("Custom task added.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not add custom task.");
    }
  }

  async function deleteTask(taskId) {
    try {
      await API.request(`/api/buyer-transaction-tasks/${encodeURIComponent(taskId)}`, { method: "DELETE" });
      setTasks((current) => current.filter((task) => task.id !== taskId));
      setToast("Custom task deleted.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not delete task.");
    }
  }

  async function saveReminders(settingsId, patch) {
    try {
      const saved = await API.request(`/api/reminder-settings/${encodeURIComponent(settingsId)}`, { method: "PUT", body: JSON.stringify(patch) });
      setSettings((current) => current.map((item) => (item.id === saved.id ? saved : item)));
      setToast("Reminder settings saved.");
      refreshTracker();
    } catch (error) {
      setToast(error.message || "Could not save reminder settings.");
    }
  }

  async function markNotificationRead(notificationId) {
    try {
      const saved = await API.request(`/api/notifications/${encodeURIComponent(notificationId)}`, { method: "PUT", body: JSON.stringify({ read: true }) });
      setNotifications((current) => current.map((notification) => (notification.id === saved.id ? saved : notification)));
    } catch (error) {
      setToast(error.message || "Could not update notification.");
    }
  }

  function openTransaction(transactionId) {
    setSelectedTransactionId(transactionId);
    setActiveTab("Detail");
  }

  const selectedTransaction = transactions.find((transaction) => transaction.id === selectedTransactionId);
  const selectedTasks = selectedTransaction ? tasks.filter((task) => task.transactionId === selectedTransaction.id) : [];
  const selectedSettings = selectedTransaction ? settings.find((item) => item.transactionId === selectedTransaction.id) : null;

  return (
    <section className="portal-page buyer-tracker-page">
      <div className="module-hero buyer-hero">
        <div>
          <p className="eyebrow">Buyer Contract Tracker</p>
          <h3>A simple under-contract deadline checklist for Nex buyer files.</h3>
          <p>Use this as an internal guide alongside Brokermint, contract deadlines, lender/title communication, and brokerage compliance review.</p>
          <div className="hero-actions">
            <button className="btn light" onClick={() => setActiveTab("Create New Transaction")}>
              <Icon name="ClipboardCheck" />
              <span>Create New Transaction</span>
            </button>
            <button className="btn light" onClick={() => setActiveTab("Notifications")}>
              <Icon name="Bell" />
              <span>View Notifications</span>
            </button>
            <button className="btn light" onClick={refreshTracker}>
              <Icon name="RefreshCcw" />
              <span>Refresh</span>
            </button>
          </div>
        </div>
      </div>

      <div className="crm-tabs buyer-tabs">
        {buyerTrackerTabs.map((tab) => (
          <button key={tab} className={classNames(activeTab === tab && "active")} onClick={() => setActiveTab(tab)}>
            {tab}
          </button>
        ))}
      </div>

      {loading ? (
        <EmptyState icon="LoaderCircle" title="Loading buyer tracker" body="Preparing transactions, checklist tasks, and reminders." />
      ) : activeTab === "Create New Transaction" ? (
        <CreateBuyerTransactionForm currentUser={currentUser} onCreate={createTransaction} />
      ) : activeTab === "Notifications" ? (
        <NotificationCenter notifications={notifications} transactions={transactions} tasks={tasks} onMarkRead={markNotificationRead} onOpenTransaction={openTransaction} />
      ) : activeTab === "Detail" && selectedTransaction ? (
        <BuyerTransactionDetail
          transaction={selectedTransaction}
          tasks={selectedTasks}
          reminderSettings={selectedSettings}
          onBack={() => setActiveTab("Transactions")}
          onUpdateTransaction={updateTransaction}
          onUpdateTask={updateTask}
          onDeleteTask={deleteTask}
          onAddTask={addTask}
          onSaveReminders={saveReminders}
        />
      ) : (
        <BuyerTransactionList transactions={transactions} tasks={tasks} filters={filters} setFilters={setFilters} permissions={permissions} onOpen={openTransaction} />
      )}
    </section>
  );
}

function ProductionNumberField({ label, value, onChange, min = 0, max, step = 1, prefix = "", suffix = "", helper = "" }) {
  return (
    <label className="production-field">
      <span>{label}</span>
      <div className="production-input-wrap">
        {prefix && <em>{prefix}</em>}
        <input
          type="number"
          aria-label={label}
          min={min}
          max={max}
          step={step}
          value={value}
          onChange={(event) => onChange(event.target.value)}
        />
        {suffix && <em>{suffix}</em>}
      </div>
      {helper && <small>{helper}</small>}
    </label>
  );
}

function ProductionMetricCard({ label, value, icon = "Target", featured = false }) {
  return (
    <article className={classNames("production-metric-card", featured && "featured")}>
      <Icon name={icon} />
      <span>{label}</span>
      <strong>{value}</strong>
    </article>
  );
}

function ProductionActivityRow({ label, actual, goal, icon }) {
  const ratio = progressRatio(actual, goal);
  const percent = Math.round(ratio * 100);
  const pace = getProductionPace(actual, goal);
  const goalLabel = Number(goal) < 10 ? Number(goal).toFixed(1) : roundUp(goal);
  return (
    <article className="production-activity-row">
      <div>
        <Icon name={icon} />
        <strong>{label}</strong>
        <span>{actual || 0} actual / {goalLabel} goal</span>
      </div>
      <div className="production-progress">
        <div style={{ width: `${percent}%` }} />
      </div>
      <em className={classNames("production-pace-badge", pace.toLowerCase().replace(/\s+/g, "-"))}>
        {pace} - {percent}%
      </em>
    </article>
  );
}

function AgentProductionCalculator({ currentUser, setToast }) {
  const storageKey = `${productionStorageKey}:${currentUser?.email || "local"}`;
  const [agentName, setAgentName] = useState(currentUser?.name || "");
  const [assumptions, setAssumptions] = useState(DEFAULT_PRODUCTION_ASSUMPTIONS);
  const [activity, setActivity] = useState(DEFAULT_ACTIVITY);
  const [trackerPeriod, setTrackerPeriod] = useState("Daily");
  const [lastUpdated, setLastUpdated] = useState("");
  const [productionLoaded, setProductionLoaded] = useState(false);

  useEffect(() => {
    const saved = localStorage.getItem(storageKey);
    if (!saved) {
      setProductionLoaded(true);
      return;
    }
    try {
      const parsed = JSON.parse(saved);
      setAgentName(parsed.agentName || currentUser?.name || "");
      setAssumptions({ ...DEFAULT_PRODUCTION_ASSUMPTIONS, ...(parsed.assumptions || {}) });
      setActivity({ ...DEFAULT_ACTIVITY, ...(parsed.activity || {}) });
      setTrackerPeriod(productionTrackerPeriods.includes(parsed.trackerPeriod) ? parsed.trackerPeriod : "Daily");
      setLastUpdated(parsed.lastUpdated || "");
    } catch (error) {
      setToast?.("Could not load saved production tracker data.");
    }
    setProductionLoaded(true);
  }, [storageKey]);

  useEffect(() => {
    if (!productionLoaded) return;
    const nextUpdated = new Date().toISOString();
    setLastUpdated(nextUpdated);
    try {
      localStorage.setItem(
        storageKey,
        JSON.stringify({
          agentName,
          assumptions,
          activity,
          trackerPeriod,
          lastUpdated: nextUpdated
        })
      );
    } catch (error) {
      setToast?.("Could not save production tracker data in this browser.");
    }
  }, [agentName, assumptions, activity, trackerPeriod, storageKey, productionLoaded]);

  const plan = useMemo(() => calculateProductionPlan(assumptions), [assumptions]);
  const activityGoals = useMemo(() => calculateProductionActivityGoals(plan, assumptions, trackerPeriod), [plan, assumptions, trackerPeriod]);
  const score = useMemo(() => calculateAccountabilityScore(activity, plan, assumptions, trackerPeriod), [activity, plan, assumptions, trackerPeriod]);
  const status = getAccountabilityStatus(score);
  const signedClientsActual = Number(activity.buyerAgreementsSigned || 0) + Number(activity.listingAgreementsSigned || 0);

  function updateAssumption(field, value, options = {}) {
    setAssumptions((current) => {
      if (value === "") return { ...current, [field]: "" };
      const number = Number(value);
      let nextValue = Number.isFinite(number) ? number : options.min || 0;
      if (options.percent) nextValue = clampRate(nextValue / 100);
      else {
        nextValue = Math.max(options.min ?? 0, nextValue);
        if (typeof options.max !== "undefined") nextValue = Math.min(options.max, nextValue);
      }
      return { ...current, [field]: nextValue };
    });
  }

  function updateActivity(field, value) {
    setActivity((current) => ({ ...current, [field]: value === "" ? "" : Math.max(0, Number(value) || 0) }));
  }

  function resetActivity() {
    setActivity(DEFAULT_ACTIVITY);
    setToast?.("Production activity reset.");
  }

  function resetAssumptions() {
    setAssumptions(DEFAULT_PRODUCTION_ASSUMPTIONS);
    setToast?.("Production assumptions reset.");
  }

  const activityRows = [
    ["Calls", activity.callsMade, activityGoals.callsMade, "Phone"],
    ["Conversations", activity.conversationsHad, activityGoals.conversationsHad, "MessagesSquare"],
    ["Leads", activity.leadsGenerated, activityGoals.leadsGenerated, "UserPlus"],
    ["Appointments", activity.appointmentsSet, activityGoals.appointmentsSet, "CalendarCheck"],
    ["Signed Clients", signedClientsActual, activityGoals.signedClients, "FileSignature"],
    ["Contracts Accepted", activity.contractsAccepted, activityGoals.contractsAccepted, "Handshake"],
    ["Closings", activity.closings, activityGoals.closings, "BadgeDollarSign"]
  ];

  return (
    <section className="portal-page production-calculator-page">
      <div className="module-hero production-hero">
        <div>
          <p className="eyebrow">Agent Production Calculator</p>
          <h3>Reverse-engineer your closings into daily action.</h3>
          <p>Turn a monthly transaction goal into calls, conversations, leads, appointments, signed clients, contracts, and closings.</p>
          <div className="hero-actions">
            <button className="btn light" onClick={resetActivity}>
              <Icon name="RotateCcw" />
              <span>Reset Activity</span>
            </button>
            <button className="btn light" onClick={resetAssumptions}>
              <Icon name="SlidersHorizontal" />
              <span>Reset Assumptions</span>
            </button>
          </div>
        </div>
        <div className="production-score-card">
          <span>Accountability Score</span>
          <strong>{score}</strong>
          <em className={classNames("production-status-badge", `status-${slugify(status)}`)}>{status}</em>
        </div>
      </div>

      <section className="production-daily-card">
        <div>
          <p>Your Daily Success Number</p>
          <h3>{plan.dailyCallsNeeded} Calls / Day</h3>
          <span>About {plan.dailyHoursNeeded} prospecting hours per day at {Math.max(1, Number(assumptions.callsPerHour) || 25)} calls/hour.</span>
        </div>
        <Icon name="Target" size={54} />
      </section>

      <div className="production-grid two-column">
        <section className="panel production-card">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Goal math</p>
              <h3>Reverse Engineer Your Goal</h3>
            </div>
          </div>
          <label className="production-field">
            <span>Agent name</span>
            <input value={agentName} onChange={(event) => setAgentName(event.target.value)} placeholder="Agent name" />
          </label>
          <div className="production-form-grid">
            <ProductionNumberField label="Monthly closing goal" value={assumptions.monthlyClosingGoal} min={1} onChange={(value) => updateAssumption("monthlyClosingGoal", value, { min: 1 })} />
            <ProductionNumberField label="Average sales price" value={assumptions.averageSalesPrice} min={0} step={10000} prefix="$" onChange={(value) => updateAssumption("averageSalesPrice", value, { min: 0 })} />
            <ProductionNumberField label="Average commission rate" value={productionPercentInputValue(assumptions.averageCommissionRate, 2)} min={1} max={100} step={0.1} suffix="%" onChange={(value) => updateAssumption("averageCommissionRate", value, { percent: true })} />
            <ProductionNumberField label="Prospecting days / month" value={assumptions.prospectingDaysPerMonth} min={1} onChange={(value) => updateAssumption("prospectingDaysPerMonth", value, { min: 1 })} />
            <ProductionNumberField label="Prospecting days / week" value={assumptions.prospectingDaysPerWeek} min={1} max={7} onChange={(value) => updateAssumption("prospectingDaysPerWeek", value, { min: 1, max: 7 })} />
            <ProductionNumberField label="Calls per hour" value={assumptions.callsPerHour} min={1} onChange={(value) => updateAssumption("callsPerHour", value, { min: 1 })} />
          </div>
        </section>

        <section className="panel production-card">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">Conversion math</p>
              <h3>Funnel Assumptions</h3>
            </div>
          </div>
          <p className="production-muted">These are editable planning assumptions. Actual conversion rates vary by market, lead source, skill level, follow-up speed, and consistency.</p>
          <div className="production-assumption-list">
            {productionAssumptionFields.map(([field, label]) => (
              <ProductionNumberField
                key={field}
                label={label}
                value={productionPercentInputValue(assumptions[field])}
                min={1}
                max={100}
                suffix="%"
                helper={`Current: ${formatPercent(assumptions[field])}`}
                onChange={(value) => updateAssumption(field, value, { percent: true })}
              />
            ))}
          </div>
        </section>
      </div>

      <section className="panel production-card">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Production plan</p>
            <h3>Your Daily Success Number</h3>
          </div>
          <span className="production-updated">Saved {lastUpdated ? new Date(lastUpdated).toLocaleString() : "in this browser"}</span>
        </div>
        <div className="production-metrics-grid">
          <ProductionMetricCard label="Daily Calls Needed" value={plan.dailyCallsNeeded} icon="PhoneCall" featured />
          <ProductionMetricCard label="Weekly Calls Needed" value={plan.weeklyCallsNeeded} icon="CalendarDays" />
          <ProductionMetricCard label="Monthly Calls Needed" value={plan.callsNeeded} icon="ListChecks" />
          <ProductionMetricCard label="Daily Prospecting Hours" value={plan.dailyHoursNeeded} icon="Clock" />
          <ProductionMetricCard label="Monthly Conversations" value={plan.conversationsNeeded} icon="MessagesSquare" />
          <ProductionMetricCard label="Monthly Leads" value={plan.leadsNeeded} icon="UserPlus" />
          <ProductionMetricCard label="Monthly Appointments" value={plan.appointmentsNeeded} icon="CalendarCheck" />
          <ProductionMetricCard label="Monthly Signed Clients" value={plan.signedClientsNeeded} icon="FileSignature" />
          <ProductionMetricCard label="Monthly Contracts" value={plan.contractsNeeded} icon="Handshake" />
          <ProductionMetricCard label="Estimated Monthly GCI" value={formatCurrency(plan.estimatedGci)} icon="BadgeDollarSign" />
        </div>
      </section>

      <section className="panel production-card">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Accountability</p>
            <h3>{trackerPeriod} Activity Tracker</h3>
          </div>
          <div className="production-period-toggle">
            {productionTrackerPeriods.map((period) => (
              <button key={period} className={classNames(trackerPeriod === period && "active")} onClick={() => setTrackerPeriod(period)}>
                {period}
              </button>
            ))}
          </div>
        </div>
        <div className="production-activity-layout">
          <div className="production-activity-form">
            {productionActivityFields.map(([field, label, icon]) => (
              <ProductionNumberField
                key={field}
                label={label}
                value={activity[field]}
                min={0}
                icon={icon}
                onChange={(value) => updateActivity(field, value)}
              />
            ))}
          </div>
          <div className="production-accountability-panel">
            <div className="production-score-ring">
              <strong>{score}</strong>
              <span>{status}</span>
            </div>
            <p>You are at {score}% of the {trackerPeriod.toLowerCase()} activity target based on your monthly production goal.</p>
            <div className="production-activity-rows">
              {activityRows.map(([label, actual, goal, icon]) => (
                <ProductionActivityRow key={label} label={label} actual={actual} goal={goal} icon={icon} />
              ))}
            </div>
          </div>
        </div>
      </section>
    </section>
  );
}

function TrainingCenter({ resources, role, setTrainingResources, setToast }) {
  const [category, setCategory] = useState("All");
  const [draft, setDraft] = useState({ title: "", category: "Nex Level Learning", type: "Video", url: "", description: "", featured: false, isNew: true });
  const [bulkVideoUrls, setBulkVideoUrls] = useState("");
  const [activeResourceId, setActiveResourceId] = useState("");
  const canManage = isAdminRole(role);
  const filtered = category === "All" ? resources : resources.filter((item) => normalizeTrainingCategory(item.category) === category);
  const embeddableResources = filtered.filter(isEmbeddableTrainingResource);
  const activeResource =
    resources.find((item) => item.id === activeResourceId) ||
    embeddableResources[0] ||
    resources.find(isEmbeddableTrainingResource);
  const activeEmbedUrl = activeResource ? getTrainingEmbedUrl(trainingResourceUrl(activeResource)) : "";

  useEffect(() => {
    if (!activeResourceId && embeddableResources[0]?.id) {
      setActiveResourceId(embeddableResources[0].id);
    }
  }, [activeResourceId, embeddableResources]);

  async function addResource() {
    if (!draft.title.trim()) return;
    try {
      const saved = await API.request("/api/training-resources", { method: "POST", body: JSON.stringify(draft) });
      setTrainingResources((current) => [saved, ...current]);
      if (getTrainingEmbedUrl(trainingResourceUrl(saved))) setActiveResourceId(saved.id);
      setDraft({ title: "", category: "Nex Level Learning", type: "Video", url: "", description: "", featured: false, isNew: true });
      setToast("Training added.");
    } catch (error) {
      setToast(error.message || "Could not add training.");
    }
  }

  async function importBulkVideos() {
    const parsed = bulkVideoUrls
      .split("\n")
      .map((line, index) => parseBulkTrainingVideoLine(line, index + 1))
      .filter((item) => item?.url);
    if (!parsed.length) return;

    const created = [];
    for (const item of parsed) {
      try {
        const saved = await API.request("/api/training-resources", {
          method: "POST",
          body: JSON.stringify({
            title: item.title,
            category: draft.category || "Nex Level Learning",
            type: "Video",
            url: item.url,
            description: "NexU video training resource.",
            featured: false,
            isNew: true
          })
        });
        created.push(saved);
      } catch (error) {
        setToast(error.message || "Could not import one of the NexU videos.");
      }
    }

    if (created.length) {
      setTrainingResources((current) => [...created, ...current]);
      setActiveResourceId(created.find(isEmbeddableTrainingResource)?.id || created[0].id);
      setBulkVideoUrls("");
      setToast(`${created.length} NexU video${created.length === 1 ? "" : "s"} imported.`);
    }
  }

  async function deleteResource(id) {
    try {
      await API.request(`/api/training-resources/${encodeURIComponent(id)}`, { method: "DELETE" });
      setTrainingResources((current) => current.filter((item) => item.id !== id));
      setToast("Training removed.");
    } catch (error) {
      setToast(error.message || "Could not remove training.");
    }
  }

  return (
    <section className="portal-page">
      <div className="nexu-internal-card">
        <div>
          <p className="eyebrow">NexU</p>
          <h3>NexU Training Center</h3>
          <p>Watch Nex Realty training directly inside Nex Central. Choose a category, open a lesson, and keep learning without leaving the portal.</p>
        </div>
        <span>Internal Library</span>
      </div>

      <div className="nexu-player-layout">
        <section className="nexu-player-panel">
          {activeEmbedUrl ? (
            <iframe
              title={activeResource?.title || "NexU training video"}
              src={activeEmbedUrl}
              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
              allowFullScreen
            />
          ) : (
            <EmptyState icon="PlayCircle" title="No embedded NexU videos yet" body="Add a YouTube, Vimeo, Loom, or Google Drive video URL to play it inside Nex Central." />
          )}
        </section>
        <aside className="nexu-now-playing">
          <p className="eyebrow">Now playing</p>
          <h3>{activeResource?.title || "Add your first NexU video"}</h3>
          <p>{activeResource?.description || "Paste video links below as admin, or send me the URLs and I can load them into the training library."}</p>
          <div className="nexu-player-meta">
            <span>{activeResource?.category || "NexU"}</span>
            <span>{activeResource?.type || "Video"}</span>
          </div>
          {activeResource && (
            <a className="btn secondary small-btn" href={trainingResourceUrl(activeResource)} target="_blank" rel="noreferrer">
              <Icon name="ExternalLink" />
              <span>Open original</span>
            </a>
          )}
        </aside>
      </div>

      <div className="category-pills">
        {["All", ...trainingCategories].map((item) => (
          <button key={item} className={category === item ? "selected" : ""} onClick={() => setCategory(item)}>{item}</button>
        ))}
      </div>
      {canManage && (
        <div className="panel admin-compose">
          <div className="panel-heading"><div><p className="eyebrow">Admin</p><h3>Add NexU resource</h3></div></div>
          <div className="admin-form-grid">
            <Field label="Title" value={draft.title} onChange={(value) => setDraft((current) => ({ ...current, title: value }))} />
            <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} as="select" options={trainingCategories} />
            <Field label="Type" value={draft.type} onChange={(value) => setDraft((current) => ({ ...current, type: value }))} as="select" options={["Video", "Google Site", "PDF", "Link"]} />
            <Field label="URL" value={draft.url} onChange={(value) => setDraft((current) => ({ ...current, url: value }))} />
            <label className="check-field"><input type="checkbox" checked={draft.featured} onChange={(event) => setDraft((current) => ({ ...current, featured: event.target.checked }))} />Featured</label>
            <label className="check-field"><input type="checkbox" checked={draft.isNew} onChange={(event) => setDraft((current) => ({ ...current, isNew: event.target.checked }))} />New</label>
            <Field label="Description" value={draft.description} onChange={(value) => setDraft((current) => ({ ...current, description: value }))} as="textarea" />
          </div>
          <button className="btn primary" onClick={addResource}><Icon name="Plus" /><span>Add Training</span></button>
          <div className="bulk-video-import">
            <Field
              label="Bulk NexU video URLs"
              value={bulkVideoUrls}
              onChange={setBulkVideoUrls}
              as="textarea"
              placeholder="One per line, or Title | URL"
            />
            <button className="btn secondary" onClick={importBulkVideos}>
              <Icon name="ListPlus" />
              <span>Import Video URLs</span>
            </button>
          </div>
        </div>
      )}
      <div className="resource-grid">
        {filtered.map((item) => {
          const embedUrl = getTrainingEmbedUrl(trainingResourceUrl(item));
          return (
            <article className={classNames("resource-card", activeResource?.id === item.id && "selected")} key={item.id}>
              <Icon name={embedUrl ? "PlayCircle" : item.type === "PDF" ? "FileText" : "Link"} />
              <span>{normalizeTrainingCategory(item.category)}</span>
              <h3>{item.title}</h3>
              <p>{item.description}</p>
              <div>{embedUrl && <em>Embedded</em>}{item.featured && <em>Featured</em>}{item.isNew && <em>New</em>}</div>
              <div className="row-actions">
                {embedUrl ? (
                  <button className="btn secondary small-btn" onClick={() => setActiveResourceId(item.id)}>
                    <Icon name="Play" />
                    <span>Watch Inside</span>
                  </button>
                ) : (
                  <a className="btn secondary small-btn" href={trainingResourceUrl(item)} target="_blank" rel="noreferrer">Open Resource</a>
                )}
                {canManage && (
                  <button className="icon-btn small" onClick={() => deleteResource(item.id)} title="Remove training">
                    <Icon name="Trash2" size={15} />
                  </button>
                )}
              </div>
            </article>
          );
        })}
      </div>
    </section>
  );
}

function TransactionHelpCenter({ guides, role, setTransactionGuides, setActivePage, setToast }) {
  const [selectedId, setSelectedId] = useState(guides[0]?.id || "");
  const [draft, setDraft] = useState({
    title: "",
    category: "Transaction",
    checklist: "",
    requiredForms: "",
    brokermintReminder: "",
    contact: "Broker/admin team",
    commonMistakes: "",
    supportLabel: "Ask support"
  });
  const guide = guides.find((item) => item.id === selectedId) || guides[0];
  const guideEnhancement = guide ? transactionGuideEnhancements[guide.id] || {
    overview: "Use this guide as a practical internal starting point for the workflow. Confirm the exact documents, deadlines, and compliance requirements with the broker/admin team before acting on transaction-specific issues.",
    reminders: [
      "Verify dates, signatures, required documents, and client instructions before moving to the next step.",
      "Keep Brokermint, client communication, and your own notes aligned so the file is easy to review.",
      "Ask for broker/admin guidance early when money, contract deadlines, compliance, or cancellation language is involved."
    ]
  } : null;
  const canManage = isAdminRole(role);

  useEffect(() => {
    if (!selectedId && guides[0]?.id) setSelectedId(guides[0].id);
  }, [guides, selectedId]);

  async function addGuide() {
    if (!draft.title.trim()) return;
    try {
      const saved = await API.request("/api/transaction-guides", {
        method: "POST",
        body: JSON.stringify(draft)
      });
      setTransactionGuides((current) => [saved, ...current]);
      setSelectedId(saved.id);
      setDraft({
        title: "",
        category: "Transaction",
        checklist: "",
        requiredForms: "",
        brokermintReminder: "",
        contact: "Broker/admin team",
        commonMistakes: "",
        supportLabel: "Ask support"
      });
      setToast("Transaction guide added.");
    } catch (error) {
      setToast(error.message || "Could not add guide.");
    }
  }

  async function deleteGuide(id) {
    try {
      await API.request(`/api/transaction-guides/${encodeURIComponent(id)}`, { method: "DELETE" });
      setTransactionGuides((current) => current.filter((item) => item.id !== id));
      setSelectedId("");
      setToast("Transaction guide removed.");
    } catch (error) {
      setToast(error.message || "Could not remove guide.");
    }
  }

  return (
    <section className="transaction-layout">
      <div className="panel guide-list">
        <div className="panel-heading"><div><p className="eyebrow">Transaction help</p><h3>Guides</h3></div></div>
        {guides.map((item) => (
          <button key={item.id} className={selectedId === item.id ? "selected" : ""} onClick={() => setSelectedId(item.id)}>
            <span>{item.category}</span>
            <strong>{item.title}</strong>
          </button>
        ))}
        {canManage && (
          <div className="guide-admin-card">
            <p className="eyebrow">Admin</p>
            <h4>Add guide</h4>
            <Field label="Title" value={draft.title} onChange={(value) => setDraft((current) => ({ ...current, title: value }))} />
            <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} />
            <Field label="Checklist" value={draft.checklist} onChange={(value) => setDraft((current) => ({ ...current, checklist: value }))} as="textarea" placeholder="One step per line" />
            <Field label="Required forms" value={draft.requiredForms} onChange={(value) => setDraft((current) => ({ ...current, requiredForms: value }))} as="textarea" placeholder="One form per line" />
            <Field label="Brokermint reminder" value={draft.brokermintReminder} onChange={(value) => setDraft((current) => ({ ...current, brokermintReminder: value }))} as="textarea" />
            <Field label="Who to contact" value={draft.contact} onChange={(value) => setDraft((current) => ({ ...current, contact: value }))} />
            <Field label="Common mistakes" value={draft.commonMistakes} onChange={(value) => setDraft((current) => ({ ...current, commonMistakes: value }))} as="textarea" placeholder="One mistake per line" />
            <Field label="Support button label" value={draft.supportLabel} onChange={(value) => setDraft((current) => ({ ...current, supportLabel: value }))} />
            <button className="btn primary" onClick={addGuide}><Icon name="Plus" /><span>Add Guide</span></button>
          </div>
        )}
      </div>
      {guide && (
        <div className="panel guide-detail">
          <div className="panel-heading">
            <div>
              <p className="eyebrow">{guide.category}</p>
              <h3>{guide.title}</h3>
            </div>
            {canManage && (
              <button className="icon-btn small" onClick={() => deleteGuide(guide.id)} title="Delete guide">
                <Icon name="Trash2" size={15} />
              </button>
            )}
          </div>
          {guideEnhancement && (
            <section className="guide-overview-card">
              <h4>What this guide is for</h4>
              <p>{guideEnhancement.overview}</p>
              <h4>Best practice reminders</h4>
              <ul>{guideEnhancement.reminders.map((item) => <li key={item}>{item}</li>)}</ul>
            </section>
          )}
          <section>
            <h4>Step-by-step checklist</h4>
            <ul>{(guide.checklist || []).map((item) => <li key={item}>{item}</li>)}</ul>
          </section>
          <section>
            <h4>Required forms</h4>
            <ul>{(guide.requiredForms || []).map((item) => <li key={item}>{item}</li>)}</ul>
          </section>
          <div className="guide-callouts">
            <article><strong>Brokermint reminder</strong><p>{guide.brokermintReminder}</p></article>
            <article><strong>Who to contact</strong><p>{guide.contact}</p></article>
            <article><strong>Common mistakes</strong><ul>{(guide.commonMistakes || []).map((item) => <li key={item}>{item}</li>)}</ul></article>
          </div>
          <button className="btn primary" onClick={() => setActivePage("Support Desk")}><Icon name="LifeBuoy" /><span>{guide.supportLabel || "Ask support"}</span></button>
        </div>
      )}
    </section>
  );
}

function SupportDesk({ currentUser, role, tickets, setSupportTickets, setToast }) {
  const [draft, setDraft] = useState({
    agentName: currentUser?.name || "",
    agentEmail: currentUser?.email || "",
    phone: "",
    category: "General question",
    urgency: "Normal",
    subject: "",
    description: "",
    attachmentName: ""
  });
  const [selectedTicketId, setSelectedTicketId] = useState("");
  const canManage = isAdminRole(role);
  const selectedTicket = tickets.find((ticket) => ticket.id === selectedTicketId) || tickets[0];

  useEffect(() => {
    if (!tickets.length) {
      setSelectedTicketId("");
      return;
    }
    if (!selectedTicketId || !tickets.some((ticket) => ticket.id === selectedTicketId)) {
      setSelectedTicketId(tickets[0].id);
    }
  }, [tickets, selectedTicketId]);

  async function submitTicket() {
    if (!draft.subject.trim()) return;
    try {
      const saved = await API.request("/api/support-tickets", { method: "POST", body: JSON.stringify(draft) });
      setSupportTickets((current) => [saved, ...current]);
      setSelectedTicketId(saved.id);
      setDraft((current) => ({ ...current, subject: "", description: "", attachmentName: "" }));
      setToast("Support ticket submitted.");
    } catch (error) {
      setToast(error.message || "Could not submit ticket.");
    }
  }

  async function updateTicket(id, status) {
    try {
      const saved = await API.request(`/api/support-tickets/${encodeURIComponent(id)}`, { method: "PUT", body: JSON.stringify({ status }) });
      setSupportTickets((current) => current.map((ticket) => (ticket.id === id ? saved : ticket)));
      setToast("Ticket updated.");
    } catch (error) {
      setToast(error.message || "Could not update ticket.");
    }
  }

  return (
    <section className="support-layout">
      <div className="panel">
        <div className="panel-heading"><div><p className="eyebrow">Support Desk</p><h3>Submit a request</h3></div></div>
        <div className="admin-form-grid">
          <Field label="Agent name" value={draft.agentName} onChange={(value) => setDraft((current) => ({ ...current, agentName: value }))} />
          <Field label="Agent email" value={draft.agentEmail} onChange={(value) => setDraft((current) => ({ ...current, agentEmail: value }))} type="email" />
          <Field label="Phone" value={draft.phone} onChange={(value) => setDraft((current) => ({ ...current, phone: value }))} />
          <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} as="select" options={supportCategories} />
          <Field label="Urgency" value={draft.urgency} onChange={(value) => setDraft((current) => ({ ...current, urgency: value }))} as="select" options={supportUrgencies} />
          <Field label="Subject" value={draft.subject} onChange={(value) => setDraft((current) => ({ ...current, subject: value }))} />
          <Field label="Description" value={draft.description} onChange={(value) => setDraft((current) => ({ ...current, description: value }))} as="textarea" />
          <label className="field">
            <span>Optional file</span>
            <input type="file" onChange={(event) => setDraft((current) => ({ ...current, attachmentName: event.target.files?.[0]?.name || "" }))} />
          </label>
        </div>
        <button className="btn primary" onClick={submitTicket}><Icon name="Send" /><span>Submit Ticket</span></button>
      </div>
      <div className="panel">
        <div className="panel-heading"><div><p className="eyebrow">{canManage ? "All tickets" : "My tickets"}</p><h3>Ticket status</h3></div></div>
        <div className="ticket-list">
          {tickets.map((ticket) => (
            <article key={ticket.id} className={classNames(selectedTicket?.id === ticket.id && "selected")}>
              <button className="ticket-summary-button" onClick={() => setSelectedTicketId(ticket.id)}>
                <span>{ticket.category} | {ticket.urgency}</span>
                <strong>{ticket.subject}</strong>
                <p>{ticket.description}</p>
                <em>{ticket.status}</em>
              </button>
              {canManage && <Field label="Status" value={ticket.status} onChange={(value) => updateTicket(ticket.id, value)} as="select" options={ticketStatuses} />}
            </article>
          ))}
          {!tickets.length && <EmptyState icon="LifeBuoy" title="No support tickets yet" body="Submit a request and it will appear here." />}
        </div>
        {selectedTicket && (
          <div className="ticket-detail-card">
            <div className="ticket-detail-head">
              <div>
                <p className="eyebrow">Ticket detail</p>
                <h3>{selectedTicket.subject}</h3>
              </div>
              <span>{selectedTicket.status}</span>
            </div>
            <dl>
              <div><dt>Agent</dt><dd>{selectedTicket.agentName || "Not listed"}</dd></div>
              <div><dt>Email</dt><dd>{selectedTicket.agentEmail || "Not listed"}</dd></div>
              <div><dt>Phone</dt><dd>{selectedTicket.phone || "Not listed"}</dd></div>
              <div><dt>Category</dt><dd>{selectedTicket.category}</dd></div>
              <div><dt>Urgency</dt><dd>{selectedTicket.urgency}</dd></div>
              <div><dt>Submitted</dt><dd>{selectedTicket.createdAt ? new Date(selectedTicket.createdAt).toLocaleString() : "Not listed"}</dd></div>
            </dl>
            <div className="ticket-detail-description">
              <strong>Full description</strong>
              <p>{selectedTicket.description || "No description provided."}</p>
            </div>
            {selectedTicket.attachmentName && (
              <div className="ticket-attachment">
                <Icon name="Paperclip" />
                <span>{selectedTicket.attachmentName}</span>
              </div>
            )}
            {canManage && (
              <Field label="Update status" value={selectedTicket.status} onChange={(value) => updateTicket(selectedTicket.id, value)} as="select" options={ticketStatuses} />
            )}
          </div>
        )}
      </div>
    </section>
  );
}

function AdminPanel({ role, announcements, quickLinks, trainingResources, transactionGuides, supportTickets, allowedUsers, setActivePage }) {
  if (!isAdminRole(role)) {
    return <EmptyState icon="ShieldCheck" title="Admin access required" body="This area is for Nex Realty leadership and admin users." />;
  }

  const adminTiles = [
    ["Announcements", announcements.length, "Bulletin Board"],
    ["Quick links", quickLinks.length, "Quick Links"],
    ["Training resources", trainingResources.length, "NexU / Training Center"],
    ["Transaction guides", transactionGuides.length, "Transaction Help Center"],
    ["Support tickets", supportTickets.length, "Support Desk"],
    ["Users/agents", allowedUsers.length, "Brand Settings / Admin"],
    ["Marketing templates", "Open", "Template Library"],
    ["Brand settings", "Open", "Brand Settings / Admin"]
  ];

  return (
    <section className="portal-page">
      <div className="module-hero">
        <div>
          <p className="eyebrow">Admin</p>
          <h3>Nex Central controls</h3>
          <p>Manage brokerage content, links, training, guides, support tickets, templates, users, and brand settings.</p>
        </div>
      </div>
      <div className="module-card-grid">
        {adminTiles.map(([label, value, page]) => (
          <button className="module-card" key={label} onClick={() => setActivePage(page)}>
            <Icon name="Settings" />
            <strong>{label}</strong>
            <span>{value}</span>
          </button>
        ))}
      </div>
    </section>
  );
}

function ChatMessage({ message, setToast }) {
  if (message.type === "assistant" && message.output) {
    return (
      <div className="message assistant">
        <div className="message-bubble rich">
          <ComplianceNotice text={message.output.complianceReminder} />
          <MarketingOutput output={message.output} setToast={setToast} />
        </div>
      </div>
    );
  }

  return (
    <div className={classNames("message", message.type)}>
      <div className="message-bubble">
        <p>{message.text}</p>
      </div>
    </div>
  );
}

function ChatWorkspace({ messages, prompt, setPrompt, onSubmit, isGenerating, output, setToast, onQuickAction = null }) {
  return (
    <section className="chat-layout">
      <div className="chat-card">
        <div className="prompt-starters">
          {promptStarters.map((starter) => (
            <button key={starter.label} type="button" onClick={() => setPrompt(starter.prompt)}>
              <Icon name="Sparkles" size={15} />
              <span>{starter.label}</span>
            </button>
          ))}
        </div>
        <div className="copy-action-row">
          {copyQuickActions.map(([label, action]) => (
            <button key={label} type="button" onClick={() => (onQuickAction ? onQuickAction(`${action}\n\nCurrent copy:\n${output?.primary || prompt || "Use current Nex Realty campaign details."}`) : setPrompt(action))}>
              {label}
            </button>
          ))}
        </div>
        <div className="chat-stream">
          {messages.map((message) => (
            <ChatMessage key={message.id} message={message} setToast={setToast} />
          ))}
        </div>
        <form className="chat-composer" onSubmit={onSubmit}>
          <textarea
            value={prompt}
            onChange={(event) => setPrompt(event.target.value)}
            placeholder='Try "Create a Facebook post, flyer copy, email blast, and text message for this listing."'
            rows={3}
          />
          <button className="btn primary" type="submit" disabled={isGenerating}>
            <Icon name={isGenerating ? "LoaderCircle" : "Send"} className={isGenerating ? "spin" : ""} />
            <span>{isGenerating ? "Working" : "Send"}</span>
          </button>
        </form>
      </div>

      <div className="panel output-panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Structured output</p>
            <h3>Marketing set</h3>
          </div>
        </div>
        {output ? (
          <MarketingOutput output={output} setToast={setToast} compact />
        ) : (
          <EmptyState icon="FileText" title="Generated content appears here" body="Every request returns copy, headline, CTA, hashtags, email, SMS, and flyer-ready text." />
        )}
      </div>
    </section>
  );
}

function MarketingOutput({ output, setToast = () => {}, compact = false }) {
  const sections = [
    ["Primary marketing copy", output.primary],
    ["Short version", output.shortVersion],
    ["Long version", output.longVersion],
    ["Flyer-ready copy", output.flyerReadyCopy],
    ["Email version", output.emailVersion],
    ["SMS version", output.smsVersion]
  ];

  return (
    <div className={classNames("marketing-output", compact && "compact")}>
      <div className="output-header">
        <div>
          <p className="eyebrow">{output.marketingType}</p>
          <h3>{output.suggestedHeadline}</h3>
        </div>
        <button
          className="icon-btn"
          onClick={() =>
            copyToClipboard(
              sections.map(([label, value]) => `${label}\n${value}`).join("\n\n"),
              setToast
            )
          }
          title="Copy all output"
        >
          <Icon name="Copy" />
        </button>
      </div>

      {!compact && output.missingSuggestions && output.missingSuggestions.length > 0 && (
        <div className="suggestion-strip">
          Add later for sharper output: {output.missingSuggestions.join(", ")}.
        </div>
      )}

      <div className="output-grid">
        {sections.map(([label, value]) => (
          <article className="output-card" key={label}>
            <div className="output-card-head">
              <span>{label}</span>
              <button className="icon-btn small" onClick={() => copyToClipboard(value, setToast)} title={`Copy ${label}`}>
                <Icon name="Copy" size={15} />
              </button>
            </div>
            <p>{value}</p>
          </article>
        ))}
      </div>

      {output.hashtags && output.hashtags.length > 0 && (
        <div className="hashtag-row">
          {output.hashtags.map((tag) => (
            <span key={tag}>{tag}</span>
          ))}
        </div>
      )}
    </div>
  );
}

function ComplianceNotice({ text }) {
  return (
    <div className="compliance-notice">
      <Icon name="ShieldCheck" size={18} />
      <p>{text}</p>
    </div>
  );
}

function EmptyState({ icon, title, body }) {
  return (
    <div className="empty-state">
      <Icon name={icon} size={28} />
      <h3>{title}</h3>
      <p>{body}</p>
    </div>
  );
}

function Field({ label, value, onChange, placeholder = "", type = "text", as = "input", options = [] }) {
  return (
    <label className="field">
      <span>{label}</span>
      {as === "select" ? (
        <select value={value} onChange={(event) => onChange(event.target.value)}>
          {options.map((option) => (
            <option key={option} value={option}>
              {option}
            </option>
          ))}
        </select>
      ) : as === "textarea" ? (
        <textarea value={value} onChange={(event) => onChange(event.target.value)} placeholder={placeholder} rows={4} />
      ) : (
        <input value={value} onChange={(event) => onChange(event.target.value)} placeholder={placeholder} type={type} />
      )}
    </label>
  );
}

function RangeField({ label, value, onChange, min, max, step = 1, suffix = "" }) {
  return (
    <label className="field range-field">
      <span>
        {label}
        <strong>{value}{suffix}</strong>
      </span>
      <input
        type="range"
        min={min}
        max={max}
        step={step}
        value={value}
        onChange={(event) => onChange(Number(event.target.value))}
      />
    </label>
  );
}

function InputPanel({ fields, setFields, agentHeadshot, setAgentHeadshot, propertyPhotos, setPropertyPhotos }) {
  function update(name, value) {
    setFields((current) => ({ ...current, [name]: value }));
  }

  async function onHeadshotChange(event) {
    const files = Array.from((event.target && event.target.files) || event.files || []);
    const file = files[0];
    if (!file) return;
    setAgentHeadshot(await fileToDataUrl(file));
  }

  async function onPhotoChange(event) {
    const files = Array.from((event.target && event.target.files) || event.files || []);
    const uploads = await Promise.all(files.map(fileToDataUrl));
    setPropertyPhotos((current) => [...current, ...uploads].slice(0, 8));
  }

  return (
    <aside className="input-panel">
      <div className="panel-heading sticky-heading">
        <div>
          <p className="eyebrow">Optional details</p>
          <h3>Agent and property inputs</h3>
        </div>
      </div>

      <div className="form-stack">
        <section className="form-section">
          <h4>Marketing setup</h4>
          <Field label="Marketing type" value={fields.marketingType} onChange={(value) => update("marketingType", value)} as="select" options={marketingTypes} />
          <Field label="Tone" value={fields.tone} onChange={(value) => update("tone", value)} as="select" options={toneOptions} />
          <Field label="Platform" value={fields.platform} onChange={(value) => update("platform", value)} as="select" options={platformOptions} />
        </section>

        <section className="form-section">
          <h4>Agent</h4>
          <Field label="Agent name" value={fields.agentName} onChange={(value) => update("agentName", value)} placeholder="Jordan Taylor" />
          <Field label="Agent phone" value={fields.agentPhone} onChange={(value) => update("agentPhone", value)} placeholder="555-010-2026" />
          <Field label="Agent email" value={fields.agentEmail} onChange={(value) => update("agentEmail", value)} placeholder="agent@nexrealty.com" />
          <FileDrop label="Agent headshot" onChange={onHeadshotChange} preview={agentHeadshot && agentHeadshot.dataUrl} />
        </section>

        <section className="form-section">
          <h4>Property</h4>
          <Field label="Property address" value={fields.propertyAddress} onChange={(value) => update("propertyAddress", value)} placeholder="123 Main Street" />
          <div className="split-fields">
            <Field label="City" value={fields.city} onChange={(value) => update("city", value)} />
            <Field label="State" value={fields.state} onChange={(value) => update("state", value)} />
            <Field label="Zip" value={fields.zip} onChange={(value) => update("zip", value)} />
          </div>
          <Field label="Price" value={fields.price} onChange={(value) => update("price", value)} placeholder="$725,000" />
          <div className="split-fields">
            <Field label="Beds" value={fields.bedrooms} onChange={(value) => update("bedrooms", value)} />
            <Field label="Baths" value={fields.bathrooms} onChange={(value) => update("bathrooms", value)} />
            <Field label="Garage" value={fields.garageSpaces} onChange={(value) => update("garageSpaces", value)} />
          </div>
          <div className="split-fields">
            <Field label="Sq ft" value={fields.squareFootage} onChange={(value) => update("squareFootage", value)} />
            <Field label="Lot size" value={fields.lotSize} onChange={(value) => update("lotSize", value)} />
          </div>
          <Field label="Property type" value={fields.propertyType} onChange={(value) => update("propertyType", value)} placeholder="Single family, condo, townhome" />
          <Field
            label="Status"
            value={fields.status}
            onChange={(value) => update("status", value)}
            as="select"
            options={["For sale", "For rent", "Sold", "Open house", "Price drop"]}
          />
          <Field label="Open house date and time" type="datetime-local" value={fields.openHouseDateTime} onChange={(value) => update("openHouseDateTime", value)} />
          <Field label="Available date for rentals" type="date" value={fields.rentalAvailableDate} onChange={(value) => update("rentalAvailableDate", value)} />
          <Field label="Property highlights" value={fields.propertyHighlights} onChange={(value) => update("propertyHighlights", value)} as="textarea" placeholder="Renovated kitchen, private yard, smart home features..." />
          <Field label="MLS remarks" value={fields.mlsRemarks} onChange={(value) => update("mlsRemarks", value)} as="textarea" placeholder="Paste MLS remarks or rough notes." />
          <Field label="Call-to-action" value={fields.cta} onChange={(value) => update("cta", value)} />
          <FileDrop label="Property photos" multiple onChange={onPhotoChange} count={propertyPhotos.length} />
          {propertyPhotos.length > 0 && (
            <div className="photo-thumb-grid">
              {propertyPhotos.map((photo, index) => (
                <button key={`${photo.name}-${index}`} onClick={() => setPropertyPhotos((current) => current.filter((_, itemIndex) => itemIndex !== index))} title="Remove photo">
                  <img src={photo.dataUrl} alt={photo.name} />
                </button>
              ))}
            </div>
          )}
        </section>
      </div>
    </aside>
  );
}

function FileDrop({ label, onChange, preview, count, multiple = false }) {
  function handleDrop(event) {
    event.preventDefault();
    const files = Array.from(event.dataTransfer.files || []).filter((file) => file.type.startsWith("image/"));
    if (files.length) onChange({ files: multiple ? files : files.slice(0, 1) });
  }

  return (
    <label className="file-drop" onDragOver={(event) => event.preventDefault()} onDrop={handleDrop}>
      {preview ? <img src={preview} alt="" /> : <Icon name="UploadCloud" size={19} />}
      <span>{label}</span>
      {typeof count === "number" && <em>{count} selected</em>}
      <input type="file" accept="image/*" onChange={onChange} multiple={multiple} />
    </label>
  );
}

function ChoiceGrid({ label, value, options, onChange }) {
  return (
    <div className="choice-group">
      <p>{label}</p>
      <div className="choice-grid">
        {options.map((option) => (
          <button
            key={option.id}
            className={classNames("choice-chip", value === option.id && "selected")}
            onClick={() => onChange(option.id)}
            type="button"
          >
            {option.icon && <Icon name={option.icon} size={16} />}
            <span>{option.name}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

function EditableCanvasText({ as = "span", value, placeholder = "Click to edit", onCommit, onSelect, className = "", multiline = false, style = {} }) {
  const Tag = as;
  const displayValue = value || placeholder;

  function commit(event) {
    const nextValue = event.currentTarget.textContent.trim();
    onCommit(nextValue);
  }

  function handleKeyDown(event) {
    if (!multiline && event.key === "Enter") {
      event.preventDefault();
      event.currentTarget.blur();
    }
    if (event.key === "Escape") {
      event.currentTarget.textContent = displayValue;
      event.currentTarget.blur();
    }
  }

  return (
    <Tag
      className={classNames("canvas-editable", className)}
      contentEditable
      suppressContentEditableWarning
      tabIndex={0}
      onClick={(event) => {
        event.stopPropagation();
        if (onSelect) onSelect();
      }}
      onFocus={onSelect}
      onBlur={commit}
      onKeyDown={handleKeyDown}
      title="Click to edit directly"
      style={style}
    >
      {displayValue}
    </Tag>
  );
}

function ResizeHandles({ active, elementId, onStart }) {
  if (!active) return null;
  return (
    <span className="resize-handles" aria-hidden="true">
      {["nw", "ne", "sw", "se"].map((corner) => (
        <i key={corner} className={`handle-${corner}`} onPointerDown={(event) => onStart(event, elementId)} />
      ))}
    </span>
  );
}

function TemplateSelector({ templates, selectedTemplateId, onSelect, brand }) {
  return (
    <div className="template-picker">
      {templates.map((template) => (
        <button
          key={template.id}
          className={classNames("template-preset", selectedTemplateId === template.id && "selected")}
          onClick={() => onSelect(template.id)}
          type="button"
          style={{ "--accent": template.accent || "#e30613" }}
        >
          <Wordmark brand={brand} compact variant="dark" className="template-card-logo" />
          <span>{template.category}</span>
          <strong>{template.headline}</strong>
          <em>{template.badge}</em>
        </button>
      ))}
    </div>
  );
}

function StockPhotoTray({ onAddStockPhoto }) {
  const [activeCategory, setActiveCategory] = useState("All");
  const categories = ["All", ...Array.from(new Set(stockPhotoPresets.map((photo) => photo.category)))];
  const filtered = activeCategory === "All" ? stockPhotoPresets : stockPhotoPresets.filter((photo) => photo.category === activeCategory);
  const stockQuery = activeCategory === "All" ? "luxury real estate" : `${activeCategory} real estate`;

  return (
    <div className="stock-tray">
      <div className="stock-tabs">
        {categories.map((category) => (
          <button
            key={category}
            className={activeCategory === category ? "selected" : ""}
            onClick={() => setActiveCategory(category)}
            type="button"
          >
            {category}
          </button>
        ))}
      </div>
      <button
        className="stock-search-button"
        type="button"
        onClick={() => window.open(`https://www.pexels.com/search/${encodeURIComponent(stockQuery)}/`, "_blank", "noopener,noreferrer")}
      >
        <Icon name="Search" size={15} />
        <span>Pexels stock search</span>
      </button>
      <div className="stock-grid">
        {filtered.map((photo) => (
          <button key={photo.id} className="stock-card" onClick={() => onAddStockPhoto(photo)} type="button">
            <img src={photo.dataUrl} alt={photo.title} />
            <span>{photo.title}</span>
          </button>
        ))}
      </div>
    </div>
  );
}

function FlyerBuilder({
  brand,
  fields,
  setFields,
  templates,
  selectedTemplateId,
  setSelectedTemplateId,
  flyerCopy,
  setFlyerCopy,
  propertyPhotos,
  agentHeadshot,
  flyerRef,
  onDownloadPdf,
  onDownloadPng,
  onDownloadVideo,
  onGenerate,
  onAddStockPhoto
}) {
  const selectedTemplate = templates.find((template) => template.id === selectedTemplateId);
  const selectedFormat = getCreativeFormat(flyerCopy.canvasFormat);

  function updateFlyerCopy(name, value) {
    setFlyerCopy((current) => ({ ...current, [name]: value }));
  }

  function updateField(name, value) {
    if (!setFields) return;
    setFields((current) => ({ ...current, [name]: value }));
  }

  function selectTemplate(templateId) {
    const template = templates.find((item) => item.id === templateId);
    setSelectedTemplateId(templateId);
    if (template) {
      setFlyerCopy((current) => ({
        ...current,
        headline: template.headline || current.headline,
        badge: template.badge || current.badge,
        layout: template.style || current.layout
      }));
    }
  }

  return (
    <section className="flyer-builder-grid">
      <div className="panel builder-controls studio-controls">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Design Studio</p>
            <h3>Templates, visuals, and copy</h3>
          </div>
        </div>

        <section className="studio-block">
          <p className="studio-label">Template</p>
          <TemplateSelector templates={templates} selectedTemplateId={selectedTemplateId} onSelect={selectTemplate} brand={brand} />
        </section>

        <section className="studio-block">
          <p className="studio-label">Design options</p>
          <ChoiceGrid label="Format" value={flyerCopy.canvasFormat || "letter-flyer"} options={creativeFormatOptions} onChange={(value) => updateFlyerCopy("canvasFormat", value)} />
          <ChoiceGrid label="Layout" value={flyerCopy.layout || selectedTemplate?.style || "classic"} options={flyerLayoutOptions} onChange={(value) => updateFlyerCopy("layout", value)} />
          <ChoiceGrid label="Finish" value={flyerCopy.finish || "red-signature"} options={flyerFinishOptions} onChange={(value) => updateFlyerCopy("finish", value)} />
          <ChoiceGrid label="Photo" value={flyerCopy.photoTreatment || "dramatic-overlay"} options={photoTreatmentOptions} onChange={(value) => updateFlyerCopy("photoTreatment", value)} />
          <div className="format-note">
            <Icon name="Ruler" size={15} />
            <span>{selectedFormat.name}: {selectedFormat.exportLabel}</span>
          </div>
        </section>

        <section className="studio-block">
          <p className="studio-label">Stock visuals</p>
          <StockPhotoTray onAddStockPhoto={onAddStockPhoto} />
        </section>

        <section className="studio-block copy-studio-block">
          <p className="studio-label">Editable copy</p>
          <Field label="Headline" value={flyerCopy.headline} onChange={(value) => updateFlyerCopy("headline", value)} />
          <Field label="Badge" value={flyerCopy.badge} onChange={(value) => updateFlyerCopy("badge", value)} />
          <Field label="Flyer body" value={flyerCopy.body} onChange={(value) => updateFlyerCopy("body", value)} as="textarea" />
          <Field label="CTA" value={flyerCopy.cta} onChange={(value) => updateFlyerCopy("cta", value)} />
        </section>

        <div className="button-grid">
          <button className="btn secondary" onClick={onGenerate}>
            <Icon name="RefreshCw" />
            <span>Regenerate Copy</span>
          </button>
          <button className="btn primary" onClick={onDownloadPdf}>
            <Icon name="Download" />
            <span>PDF</span>
          </button>
          <button className="btn dark" onClick={onDownloadPng}>
            <Icon name="ImageDown" />
            <span>PNG</span>
          </button>
          <button className="btn secondary" onClick={() => onDownloadVideo(flyerCopy.videoOrientation || "vertical")}>
            <Icon name="Clapperboard" />
            <span>Quick Video</span>
          </button>
        </div>
      </div>

      <FlyerPreview
        brand={brand}
        fields={fields}
        templates={templates}
        selectedTemplateId={selectedTemplateId}
        flyerCopy={flyerCopy}
        propertyPhotos={propertyPhotos}
        agentHeadshot={agentHeadshot}
        flyerRef={flyerRef}
        onUpdateField={updateField}
        onUpdateFlyer={updateFlyerCopy}
      />
    </section>
  );
}

function FlyerPreview({
  brand,
  fields,
  templates,
  selectedTemplateId,
  flyerCopy,
  propertyPhotos,
  agentHeadshot,
  flyerRef,
  onSelectElement = () => {},
  selectedElement = "",
  selectedElements = [],
  onUpdateField = () => {},
  onUpdateFlyer = () => {},
  onUpdateFlyerLive = onUpdateFlyer,
  onReplacePhoto = () => {},
  onRemovePhoto = () => {},
  onHeadshotUpload = () => {}
}) {
  const template = templates.find((item) => item.id === selectedTemplateId) || templates[0] || {};
  const accent = flyerCopy.accentColor || template.accent || brand.primaryColor;
  const layout = flyerCopy.layout || template.style || "classic";
  const finish = flyerCopy.finish || "red-signature";
  const photoTreatment = flyerCopy.photoTreatment || "dramatic-overlay";
  const format = getCreativeFormat(flyerCopy.canvasFormat);
  const flyerLogoVariant = finish === "black-luxury" ? "dark" : "light";
  const headline = flyerCopy.headline || template.headline || fields.marketingType.toUpperCase();
  const badge = flyerCopy.badge || template.badge || fields.status || "Featured";
  const heroPhoto = propertyPhotos[0];
  const supporting = propertyPhotos.slice(1, 4);
  const address = fields.propertyAddress || "123 Nex Realty Drive";
  const cityLine = [fields.city || "Your City", fields.state || "ST", fields.zip].filter(Boolean).join(", ");
  const price = money(fields.price) || "Price Upon Request";
  const openHouse = titleCaseDate(fields.openHouseDateTime);
  const headlineDrag = useRef(null);
  const resizeDrag = useRef(null);
  const selectedSet = new Set(selectedElements.length ? selectedElements : [selectedElement]);
  const qrCodeUrl = getQrCodeUrl(flyerCopy.qrUrl || flyerCopy.agentWebsite || "https://mynexrealty.com");

  function isSelected(elementId) {
    return selectedSet.has(elementId);
  }

  function startHeadlineDrag(event) {
    if (event.target.closest("[contenteditable='true']")) return;
    onSelectElement("headline", event);
    event.currentTarget.setPointerCapture?.(event.pointerId);
    headlineDrag.current = {
      pointerId: event.pointerId,
      startX: event.clientX,
      startY: event.clientY,
      offsetX: Number(flyerCopy.headlineOffsetX) || 0,
      offsetY: Number(flyerCopy.headlineOffsetY) || 0
    };
  }

  function moveHeadlineDrag(event) {
    if (!headlineDrag.current || headlineDrag.current.pointerId !== event.pointerId) return;
    const nextX = Math.round(headlineDrag.current.offsetX + event.clientX - headlineDrag.current.startX);
    const nextY = Math.round(headlineDrag.current.offsetY + event.clientY - headlineDrag.current.startY);
    onUpdateFlyerLive("headlineOffsetX", nextX);
    onUpdateFlyerLive("headlineOffsetY", nextY);
  }

  function endHeadlineDrag(event) {
    if (headlineDrag.current?.pointerId === event.pointerId) {
      headlineDrag.current = null;
    }
  }

  function startElementResize(event, elementId) {
    event.preventDefault();
    event.stopPropagation();
    onSelectElement(elementId, event);
    event.currentTarget.setPointerCapture?.(event.pointerId);
    resizeDrag.current = {
      pointerId: event.pointerId,
      elementId,
      startX: event.clientX,
      startY: event.clientY,
      headlineWidth: Number(flyerCopy.headlineWidth) || 360,
      fontScale: Number(flyerCopy.fontScale) || 100,
      heroHeight: Number(flyerCopy.heroHeight) || 274,
      layerScale: Number(flyerCopy[`${elementId}Scale`]) || 100,
      qrScale: Number(flyerCopy.qrScale) || 100
    };
  }

  function moveElementResize(event) {
    const drag = resizeDrag.current;
    if (!drag || drag.pointerId !== event.pointerId) return;
    const deltaX = event.clientX - drag.startX;
    const deltaY = event.clientY - drag.startY;
    if (drag.elementId === "headline") {
      onUpdateFlyerLive("headlineWidth", Math.round(clampStudioNumber(drag.headlineWidth + deltaX, 160, 720)));
      onUpdateFlyerLive("fontScale", Math.round(clampStudioNumber(drag.fontScale + deltaY / 2, 70, 170)));
      return;
    }
    if (drag.elementId === "media") {
      onUpdateFlyerLive("heroHeight", Math.round(clampStudioNumber(drag.heroHeight + deltaY, 140, 560)));
      return;
    }
    if (drag.elementId === "qr") {
      onUpdateFlyerLive("qrScale", Math.round(clampStudioNumber(drag.qrScale + (deltaX + deltaY) / 4, 70, 180)));
      return;
    }
    onUpdateFlyerLive(`${drag.elementId}Scale`, Math.round(clampStudioNumber(drag.layerScale + (deltaX + deltaY) / 4, 70, 180)));
  }

  function endElementResize(event) {
    if (resizeDrag.current?.pointerId === event.pointerId) {
      resizeDrag.current = null;
    }
  }

  if (format.id === "business-card") {
    return (
      <div className="preview-shell business-card-preview-shell">
        <div className="canvas-edit-hint">
          <Icon name="MousePointerClick" size={14} />
          <span>Click text to edit. Switch sides to design the front and back.</span>
        </div>
        <div className="studio-card-side-tabs">
          {["front", "back"].map((side) => (
            <button key={side} className={(flyerCopy.businessCardSide || "front") === side ? "selected" : ""} onClick={() => onUpdateFlyer("businessCardSide", side)} type="button">
              {side === "front" ? "Front Side" : "Back Side"}
            </button>
          ))}
        </div>
        <StudioBusinessCardCanvas
          brand={brand}
          fields={fields}
          flyerCopy={flyerCopy}
          agentHeadshot={agentHeadshot}
          flyerRef={flyerRef}
          selectedElement={selectedElement}
          selectedElements={selectedElements}
          onSelectElement={onSelectElement}
          onUpdateField={onUpdateField}
          onUpdateFlyer={onUpdateFlyer}
          onHeadshotUpload={onHeadshotUpload}
        />
      </div>
    );
  }

  return (
    <div className="preview-shell">
      <div className="canvas-edit-hint">
        <Icon name="MousePointerClick" size={14} />
        <span>Click text to edit directly. Drag the headline block to reposition it.</span>
      </div>
      <div
        className={classNames("flyer-page studio-flyer", `layout-${slugify(layout)}`, `finish-${slugify(finish)}`, `photo-${slugify(photoTreatment)}`, `format-${slugify(format.id)}`)}
        ref={flyerRef}
        style={{
          "--accent": accent,
          "--nex-red": brand.primaryColor,
          "--brand-gold": brand.gold,
          "--canvas-aspect": `${format.width} / ${format.height}`,
          "--headline-scale": `${(flyerCopy.fontScale || 100) / 100}`,
          "--headline-weight": flyerCopy.fontWeight || 900,
          "--headline-width": `${flyerCopy.headlineWidth || 360}px`,
          "--hero-height": `${flyerCopy.heroHeight || 274}px`,
          "--text-align": flyerCopy.textAlign || "left",
          "--studio-bg-color": flyerCopy.backgroundColor || "#ffffff",
          "--studio-bg-image": flyerCopy.backgroundImage ? `url("${flyerCopy.backgroundImage}")` : "none",
          "--studio-bg-opacity": `${(flyerCopy.backgroundOpacity || 0) / 100}`
        }}
      >
        <header className="flyer-top">
          <Wordmark brand={brand} compact variant={flyerLogoVariant} />
          <div className="flyer-template-label">
            <EditableCanvasText value={fields.marketingType || template.name || "Nex Realty Flyer"} onCommit={(value) => onUpdateField("marketingType", value)} onSelect={() => onSelectElement("headline")} />
          </div>
        </header>

        <section
          className={classNames("flyer-hero", isSelected("media") && "selected-element")}
          onClick={(event) => onSelectElement("media", event)}
          onPointerMove={moveElementResize}
          onPointerUp={endElementResize}
          onPointerCancel={endElementResize}
        >
          <div
            className={classNames("flyer-headline draggable-headline", isSelected("headline") && "selected-element")}
            style={{ transform: `translate(${Number(flyerCopy.headlineOffsetX) || 0}px, ${Number(flyerCopy.headlineOffsetY) || 0}px)` }}
            onPointerDown={startHeadlineDrag}
            onPointerMove={moveHeadlineDrag}
            onPointerUp={endHeadlineDrag}
            onPointerCancel={endHeadlineDrag}
          >
            <EditableCanvasText value={badge} onCommit={(value) => onUpdateFlyer("badge", value)} onSelect={() => onSelectElement("headline")} />
            <EditableCanvasText as="h2" value={headline} onCommit={(value) => onUpdateFlyer("headline", value)} onSelect={() => onSelectElement("headline")} />
            <ResizeHandles active={isSelected("headline")} elementId="headline" onStart={startElementResize} />
          </div>
          {heroPhoto ? (
            <img src={heroPhoto.dataUrl} alt={heroPhoto.name} style={{ objectFit: (flyerCopy.heroImageFit || "Cover").toLowerCase(), objectPosition: flyerCopy.heroImagePosition || "center" }} />
          ) : (
            <div className="photo-placeholder">
              <Icon name="Image" size={38} />
              <span>Upload a property photo</span>
            </div>
          )}
          <div className="canvas-image-toolbar">
            <label>
              <Icon name="ImagePlus" size={13} />
              <span>{heroPhoto ? "Replace photo" : "Add photo"}</span>
              <input type="file" accept="image/*" onChange={(event) => onReplacePhoto(event, 0)} />
            </label>
            {heroPhoto && (
              <button type="button" onClick={(event) => { event.stopPropagation(); onRemovePhoto(0); }}>
                <Icon name="Trash2" size={13} />
                <span>Remove</span>
              </button>
            )}
          </div>
          <ResizeHandles active={isSelected("media")} elementId="media" onStart={startElementResize} />
        </section>

        <section className="flyer-photo-strip">
          {[0, 1, 2].map((slot) =>
            supporting[slot] ? (
              <img key={slot} src={supporting[slot].dataUrl} alt={supporting[slot].name} />
            ) : (
              <div key={slot} className="mini-placeholder">
                <Icon name="Image" size={18} />
              </div>
            )
          )}
        </section>

        <section className="flyer-details">
          <div
            className={classNames("address-block canvas-scalable-layer", isSelected("address") && "selected-element")}
            style={layerScaleStyle(flyerCopy, "address")}
            onClick={(event) => onSelectElement("address", event)}
            onPointerMove={moveElementResize}
            onPointerUp={endElementResize}
            onPointerCancel={endElementResize}
          >
            <Icon name="MapPin" size={21} />
            <div>
              <EditableCanvasText as="strong" value={address} onCommit={(value) => onUpdateField("propertyAddress", value)} onSelect={() => onSelectElement("address")} />
              <EditableCanvasText value={cityLine} onCommit={(value) => onUpdateField("city", value)} onSelect={() => onSelectElement("address")} />
            </div>
            <ResizeHandles active={isSelected("address")} elementId="address" onStart={startElementResize} />
          </div>

          {flyerCopy.showPrice !== false && (
            <div
              className={classNames("price-block canvas-scalable-layer", isSelected("price") && "selected-element")}
              style={layerScaleStyle(flyerCopy, "price")}
              onClick={(event) => onSelectElement("price", event)}
              onPointerMove={moveElementResize}
              onPointerUp={endElementResize}
              onPointerCancel={endElementResize}
            >
              <EditableCanvasText value={fields.status || "For sale"} onCommit={(value) => onUpdateField("status", value)} onSelect={() => onSelectElement("price")} />
              <EditableCanvasText as="strong" value={price} onCommit={(value) => onUpdateField("price", value)} onSelect={() => onSelectElement("price")} />
              <ResizeHandles active={isSelected("price")} elementId="price" onStart={startElementResize} />
            </div>
          )}
        </section>

        {flyerCopy.showStats !== false && (
          <section
            className={classNames("stats-strip canvas-scalable-layer", isSelected("stats") && "selected-element")}
            style={layerScaleStyle(flyerCopy, "stats")}
            onClick={(event) => onSelectElement("stats", event)}
            onPointerMove={moveElementResize}
            onPointerUp={endElementResize}
            onPointerCancel={endElementResize}
          >
            <FeatureStat icon="BedDouble" label="Beds" value={fields.bedrooms || "-"} onCommit={(value) => onUpdateField("bedrooms", value)} />
            <FeatureStat icon="Bath" label="Baths" value={fields.bathrooms || "-"} onCommit={(value) => onUpdateField("bathrooms", value)} />
            <FeatureStat icon="Car" label="Garage" value={fields.garageSpaces || "-"} onCommit={(value) => onUpdateField("garageSpaces", value)} />
            <FeatureStat icon="Ruler" label="Sq Ft" value={fields.squareFootage || "-"} onCommit={(value) => onUpdateField("squareFootage", value)} />
            <ResizeHandles active={isSelected("stats")} elementId="stats" onStart={startElementResize} />
          </section>
        )}

        {(openHouse || fields.rentalAvailableDate) && (
          <section className="event-band">
            <Icon name="CalendarDays" size={18} />
            <span>
              {openHouse ? `Open House: ${openHouse}` : `Available: ${fields.rentalAvailableDate}`}
            </span>
          </section>
        )}

        <section
          className={classNames("flyer-copy-block canvas-scalable-layer", isSelected("body") && "selected-element")}
          style={layerScaleStyle(flyerCopy, "body")}
          onClick={(event) => onSelectElement("body", event)}
          onPointerMove={moveElementResize}
          onPointerUp={endElementResize}
          onPointerCancel={endElementResize}
        >
          <EditableCanvasText as="p" value={flyerCopy.body || "Premium Nex Realty marketing copy will appear here after generation."} multiline onCommit={(value) => onUpdateFlyer("body", value)} onSelect={() => onSelectElement("body")} />
          <EditableCanvasText as="strong" value={flyerCopy.cta || fields.cta || "Schedule Your Tour Today"} onCommit={(value) => onUpdateFlyer("cta", value)} onSelect={() => onSelectElement("body")} />
          <ResizeHandles active={isSelected("body")} elementId="body" onStart={startElementResize} />
        </section>

        {flyerCopy.showQr && (
          <section
            className={classNames("qr-design-block canvas-scalable-layer", isSelected("qr") && "selected-element")}
            style={layerScaleStyle(flyerCopy, "qr")}
            onClick={(event) => onSelectElement("qr", event)}
            onPointerMove={moveElementResize}
            onPointerUp={endElementResize}
            onPointerCancel={endElementResize}
          >
            <div>
              <img className="qr-generated-image" src={qrCodeUrl} alt="Generated QR code" crossOrigin="anonymous" />
            </div>
            <span>{flyerCopy.qrUrl || flyerCopy.agentWebsite || "Scan for more info"}</span>
            <ResizeHandles active={isSelected("qr")} elementId="qr" onStart={startElementResize} />
          </section>
        )}

        <footer className="flyer-footer">
          {flyerCopy.showAgent !== false && (
            <div
              className={classNames("agent-block canvas-scalable-layer", isSelected("agent") && "selected-element")}
              style={layerScaleStyle(flyerCopy, "agent")}
              onClick={(event) => onSelectElement("agent", event)}
              onPointerMove={moveElementResize}
              onPointerUp={endElementResize}
              onPointerCancel={endElementResize}
            >
              {agentHeadshot ? (
                <img src={agentHeadshot.dataUrl} alt={fields.agentName || "Agent headshot"} />
              ) : (
                <div className="agent-placeholder">
                  <Icon name="User" size={22} />
                </div>
              )}
              <label className="agent-headshot-replace" title="Replace headshot">
                <Icon name="ImagePlus" size={12} />
                <input type="file" accept="image/*" onChange={onHeadshotUpload} />
              </label>
              <div>
                <EditableCanvasText as="strong" value={fields.agentName || "Nex Realty Agent"} onCommit={(value) => onUpdateField("agentName", value)} onSelect={() => onSelectElement("agent")} />
                <EditableCanvasText value={[fields.agentPhone, fields.agentEmail].filter(Boolean).join(" | ") || "agent@nexrealty.com"} onCommit={(value) => onUpdateField("agentPhone", value)} onSelect={() => onSelectElement("agent")} />
                {(flyerCopy.agentWebsite || flyerCopy.agentLicense) && <EditableCanvasText as="em" value={[flyerCopy.agentWebsite, flyerCopy.agentLicense].filter(Boolean).join(" | ")} onCommit={(value) => onUpdateFlyer("agentWebsite", value)} onSelect={() => onSelectElement("agent")} />}
              </div>
              <ResizeHandles active={isSelected("agent")} elementId="agent" onStart={startElementResize} />
            </div>
          )}
          {flyerCopy.showDisclaimer !== false && (
            <EditableCanvasText
              as="p"
              className={classNames("canvas-scalable-layer", isSelected("disclaimer") && "selected-element")}
              value={flyerCopy.footer || brand.defaultDisclaimer}
              multiline
              onCommit={(value) => onUpdateFlyer("footer", value)}
              onSelect={() => onSelectElement("disclaimer")}
              style={layerScaleStyle(flyerCopy, "disclaimer")}
            />
          )}
        </footer>
      </div>
    </div>
  );
}

function StudioBusinessCardCanvas({ brand, fields, flyerCopy, agentHeadshot, flyerRef, selectedElement, selectedElements = [], onSelectElement, onUpdateField = () => {}, onUpdateFlyer = () => {}, onHeadshotUpload = () => {} }) {
  const agentName = fields.agentName || "Nex Realty Agent";
  const title = flyerCopy.agentTitle || "Real Estate Professional";
  const phone = fields.agentPhone || "555-010-2026";
  const email = fields.agentEmail || "agent@nexrealty.com";
  const website = flyerCopy.agentWebsite || "nexrealty.com";
  const social = flyerCopy.agentSocial || "@nexrealty";
  const license = flyerCopy.agentLicense || "Advertised by Nex Realty";
  const initials = agentName
    .split(" ")
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0])
    .join("")
    .toUpperCase();
  const side = flyerCopy.businessCardSide || "front";
  const selectedSet = new Set(selectedElements.length ? selectedElements : [selectedElement]);
  const qrCodeUrl = getQrCodeUrl(flyerCopy.qrUrl || website);
  const isSelected = (elementId) => selectedSet.has(elementId);

  if (side === "back") {
    return (
      <article
        className="studio-business-card-canvas studio-business-card-back"
        ref={flyerRef}
        style={{
          "--accent": flyerCopy.accentColor || brand.primaryColor,
          "--card-gold": brand.gold,
          "--headline-scale": `${(flyerCopy.fontScale || 100) / 100}`,
          "--studio-bg-color": flyerCopy.backgroundColor || "#050505",
          "--studio-bg-image": flyerCopy.backgroundImage ? `url("${flyerCopy.backgroundImage}")` : "none",
          "--studio-bg-opacity": `${(flyerCopy.backgroundOpacity || 0) / 100}`
        }}
      >
        <div className="studio-card-back-brand">
          <Wordmark brand={brand} compact variant="dark" />
          <EditableCanvasText
            as="strong"
            value={flyerCopy.cta || "Modern brokerage. Bold marketing. Smarter moves."}
            onCommit={(value) => onUpdateFlyer("cta", value)}
            onSelect={() => onSelectElement("body")}
          />
        </div>
        <section className="studio-card-back-grid">
          <div className={classNames("studio-card-qr-block", isSelected("qr") && "selected-element")} onClick={(event) => onSelectElement("qr", event)}>
            <img className="qr-generated-image" src={qrCodeUrl} alt="Generated QR code" crossOrigin="anonymous" />
            <span>QR / Lead Link</span>
          </div>
          <div className="studio-card-back-copy">
            <EditableCanvasText as="h2" value={website} onCommit={(value) => onUpdateFlyer("agentWebsite", value)} onSelect={() => onSelectElement("body")} />
            <EditableCanvasText value={social} onCommit={(value) => onUpdateFlyer("agentSocial", value)} onSelect={() => onSelectElement("body")} />
            <EditableCanvasText as="p" value={license} multiline onCommit={(value) => onUpdateFlyer("agentLicense", value)} onSelect={() => onSelectElement("disclaimer")} />
          </div>
        </section>
        <EditableCanvasText
          className="studio-card-back-footer"
          value={flyerCopy.footer || brand.defaultDisclaimer}
          multiline
          onCommit={(value) => onUpdateFlyer("footer", value)}
          onSelect={() => onSelectElement("disclaimer")}
        />
      </article>
    );
  }

  return (
    <article
      className="studio-business-card-canvas"
      ref={flyerRef}
      style={{
        "--accent": flyerCopy.accentColor || brand.primaryColor,
        "--card-gold": brand.gold,
        "--headline-scale": `${(flyerCopy.fontScale || 100) / 100}`,
        "--studio-bg-color": flyerCopy.backgroundColor || "#ffffff",
        "--studio-bg-image": flyerCopy.backgroundImage ? `url("${flyerCopy.backgroundImage}")` : "none",
        "--studio-bg-opacity": `${(flyerCopy.backgroundOpacity || 0) / 100}`
      }}
    >
      <div className="studio-card-brand">
        <Wordmark brand={brand} compact variant="light" />
        <EditableCanvasText value={license} multiline onCommit={(value) => onUpdateFlyer("agentLicense", value)} onSelect={() => onSelectElement("disclaimer")} />
      </div>

      <section className={classNames("studio-card-main", isSelected("agent") && "selected-element")} onClick={(event) => onSelectElement("agent", event)}>
        <div className="studio-card-agent-copy">
          <EditableCanvasText as="p" value={title} onCommit={(value) => onUpdateFlyer("agentTitle", value)} onSelect={() => onSelectElement("agent")} />
          <EditableCanvasText as="h2" value={agentName} onCommit={(value) => onUpdateField("agentName", value)} onSelect={() => onSelectElement("agent")} />
          <EditableCanvasText as="em" value={flyerCopy.cta || "Modern brokerage. Bold marketing. Smarter moves."} multiline onCommit={(value) => onUpdateFlyer("cta", value)} onSelect={() => onSelectElement("body")} />
        </div>
        <div className="studio-card-headshot">
          {agentHeadshot ? <img src={agentHeadshot.dataUrl} alt={agentName} /> : <strong>{initials || "NX"}</strong>}
          <label className="canvas-headshot-button" title="Replace headshot">
            <Icon name="ImagePlus" size={13} />
            <input type="file" accept="image/*" onChange={onHeadshotUpload} />
          </label>
        </div>
      </section>

      <section className={classNames("studio-card-contact", isSelected("body") && "selected-element")} onClick={(event) => onSelectElement("body", event)}>
        <span><Icon name="Phone" size={13} /><EditableCanvasText value={phone} onCommit={(value) => onUpdateField("agentPhone", value)} onSelect={() => onSelectElement("body")} /></span>
        <span><Icon name="Mail" size={13} /><EditableCanvasText value={email} onCommit={(value) => onUpdateField("agentEmail", value)} onSelect={() => onSelectElement("body")} /></span>
        <span><Icon name="Globe" size={13} /><EditableCanvasText value={website} onCommit={(value) => onUpdateFlyer("agentWebsite", value)} onSelect={() => onSelectElement("body")} /></span>
        <span><Icon name="AtSign" size={13} /><EditableCanvasText value={social} onCommit={(value) => onUpdateFlyer("agentSocial", value)} onSelect={() => onSelectElement("body")} /></span>
      </section>
    </article>
  );
}

function QuickVideoBuilder({ brand, fields, flyerCopy, setFlyerCopy, propertyPhotos, onAddStockPhoto, onGenerate, onDownloadVideo }) {
  const orientation = getVideoOrientation(flyerCopy.videoOrientation || "vertical");
  const heroPhoto = propertyPhotos[0];

  function update(name, value) {
    setFlyerCopy((current) => ({ ...current, [name]: value }));
  }

  return (
    <section className="video-builder-grid">
      <div className="panel builder-controls studio-controls">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Quick Video</p>
            <h3>Reels and horizontal videos</h3>
          </div>
        </div>

        <section className="studio-block">
          <p className="studio-label">Video format</p>
          <ChoiceGrid label="Orientation" value={flyerCopy.videoOrientation || "vertical"} options={videoOrientationOptions} onChange={(value) => update("videoOrientation", value)} />
          <div className="format-note">
            <Icon name="Clapperboard" size={15} />
            <span>{orientation.name}: {orientation.label}</span>
          </div>
        </section>

        <section className="studio-block">
          <p className="studio-label">Video copy</p>
          <Field label="Headline" value={flyerCopy.headline} onChange={(value) => update("headline", value)} />
          <Field label="Badge" value={flyerCopy.badge} onChange={(value) => update("badge", value)} />
          <Field label="CTA" value={flyerCopy.cta} onChange={(value) => update("cta", value)} />
          <Field label="Short script / caption" value={flyerCopy.body} onChange={(value) => update("body", value)} as="textarea" />
        </section>

        <section className="studio-block">
          <p className="studio-label">Stock visuals</p>
          <StockPhotoTray onAddStockPhoto={onAddStockPhoto} />
        </section>

        <div className="button-grid video-buttons">
          <button className="btn secondary" onClick={onGenerate}>
            <Icon name="RefreshCw" />
            <span>Regenerate Copy</span>
          </button>
          <button className="btn primary" onClick={() => onDownloadVideo("vertical")}>
            <Icon name="Smartphone" />
            <span>Vertical Reel</span>
          </button>
          <button className="btn dark" onClick={() => onDownloadVideo("horizontal")}>
            <Icon name="MonitorPlay" />
            <span>Horizontal Video</span>
          </button>
        </div>
      </div>

      <div className="video-preview-shell">
        <article
          className={classNames("video-preview-frame", `orientation-${orientation.id}`)}
          style={{
            "--video-bg": heroPhoto ? `url("${heroPhoto.dataUrl}")` : `url("${stockPhotoPresets[0]?.dataUrl}")`,
            "--card-red": brand.primaryColor,
            "--card-gold": brand.gold
          }}
        >
          <div className="video-preview-overlay">
            <Wordmark brand={brand} compact variant="dark" />
            <span>{flyerCopy.badge || fields.status || "Featured"}</span>
            <h3>{flyerCopy.headline || fields.marketingType || "Nex Realty"}</h3>
            <p>{fields.propertyAddress || flyerCopy.body || "Quick branded Nex video preview"}</p>
            <strong>{flyerCopy.cta || fields.cta || "Schedule Your Tour Today"}</strong>
          </div>
        </article>
      </div>
    </section>
  );
}

function BusinessCardBuilder({
  brand,
  fields,
  agentHeadshot,
  setAgentHeadshot,
  businessCardCopy,
  setBusinessCardCopy,
  businessCardFrontRef,
  businessCardBackRef,
  onDownloadFrontPdf,
  onDownloadBackPdf,
  onDownloadFrontPng,
  onDownloadBackPng,
  onPrint
}) {
  async function onBusinessHeadshotChange(event) {
    const file = event.target.files && event.target.files[0];
    if (!file) return;
    setAgentHeadshot(await fileToDataUrl(file));
  }

  function useAgentInfo() {
    setBusinessCardCopy((current) => ({
      ...current,
      displayName: current.displayName || fields.agentName,
      email: current.email || fields.agentEmail,
      phone: current.phone || fields.agentPhone
    }));
  }

  return (
    <section className="business-card-layout">
      <div className="panel builder-controls business-card-controls">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Business Card</p>
            <h3>Agent print design</h3>
          </div>
          <button className="btn secondary small-btn" onClick={useAgentInfo}>
            <Icon name="UserRoundCheck" />
            <span>Use Agent Info</span>
          </button>
        </div>

        <Field label="Card style" value={businessCardCopy.style} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, style: value }))} as="select" options={businessCardStyles} />
        <Field label="Display name" value={businessCardCopy.displayName} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, displayName: value }))} placeholder={fields.agentName || "Avery Morgan"} />
        <Field label="Card email" value={businessCardCopy.email} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, email: value }))} type="email" placeholder={fields.agentEmail || "agent@nexrealty.com"} />
        <Field label="Card phone" value={businessCardCopy.phone} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, phone: value }))} placeholder={fields.agentPhone || "555-010-2026"} />
        <Field label="Website" value={businessCardCopy.website} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, website: value }))} />
        <Field label="Social account" value={businessCardCopy.social} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, social: value }))} placeholder="@nexrealty" />
        <Field label="Agent title" value={businessCardCopy.title} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, title: value }))} />
        <Field label="License / office line" value={businessCardCopy.licenseLine} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, licenseLine: value }))} />
        <Field label="Card tagline" value={businessCardCopy.tagline} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, tagline: value }))} />

        <label className="business-headshot-upload">
          {agentHeadshot ? (
            <img src={agentHeadshot.dataUrl} alt="Selected headshot" />
          ) : (
            <span>
              <Icon name="ImagePlus" size={24} />
            </span>
          )}
          <strong>Upload card headshot</strong>
          <em>Use a clean square or portrait photo. Then adjust crop below.</em>
          <input type="file" accept="image/*" onChange={onBusinessHeadshotChange} />
        </label>

        <div className="card-control-group">
          <p>Headshot</p>
          <Field label="Shape" value={businessCardCopy.headshotShape} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, headshotShape: value }))} as="select" options={headshotShapes} />
          <Field label="Fit" value={businessCardCopy.headshotFit} onChange={(value) => setBusinessCardCopy((current) => ({ ...current, headshotFit: value }))} as="select" options={headshotFitOptions} />
          <RangeField label="Zoom" value={businessCardCopy.headshotZoom} min={80} max={180} suffix="%" onChange={(value) => setBusinessCardCopy((current) => ({ ...current, headshotZoom: value }))} />
          <RangeField label="Horizontal" value={businessCardCopy.headshotX} min={-40} max={40} suffix="%" onChange={(value) => setBusinessCardCopy((current) => ({ ...current, headshotX: value }))} />
          <RangeField label="Vertical" value={businessCardCopy.headshotY} min={-40} max={40} suffix="%" onChange={(value) => setBusinessCardCopy((current) => ({ ...current, headshotY: value }))} />
        </div>

        <div className="print-note">
          Export the front and back as separate files, then upload both sides when ordering cards.
        </div>

        <div className="business-card-buttons">
          <button className="btn primary" onClick={onDownloadFrontPdf}>
            <Icon name="Download" />
            <span>Front PDF</span>
          </button>
          <button className="btn primary" onClick={onDownloadBackPdf}>
            <Icon name="Download" />
            <span>Back PDF</span>
          </button>
          <button className="btn dark" onClick={onDownloadFrontPng}>
            <Icon name="ImageDown" />
            <span>Front PNG</span>
          </button>
          <button className="btn dark" onClick={onDownloadBackPng}>
            <Icon name="ImageDown" />
            <span>Back PNG</span>
          </button>
          <button className="btn secondary" onClick={onPrint}>
            <Icon name="ExternalLink" />
            <span>VistaPrint</span>
          </button>
        </div>
      </div>

      <BusinessCardPreview
        brand={brand}
        fields={fields}
        agentHeadshot={agentHeadshot}
        businessCardCopy={businessCardCopy}
        businessCardFrontRef={businessCardFrontRef}
        businessCardBackRef={businessCardBackRef}
      />
    </section>
  );
}

function BusinessCardPreview({ brand, fields, agentHeadshot, businessCardCopy, businessCardFrontRef, businessCardBackRef }) {
  const agentName = businessCardCopy.displayName || fields.agentName || "Nex Realty Agent";
  const phone = businessCardCopy.phone || fields.agentPhone || "555-010-2026";
  const email = businessCardCopy.email || fields.agentEmail || "agent@nexrealty.com";
  const website = businessCardCopy.website || "nexrealty.com";
  const social = businessCardCopy.social || "@nexrealty";
  const styleClass = `style-${(businessCardCopy.style || "Signature Red").toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
  const frontLogoVariant = businessCardCopy.style === "Executive Black" ? "dark" : "light";
  const backLogoVariant = businessCardCopy.style === "Clean White" ? "light" : "dark";
  const headshotStyle = {
    objectFit: (businessCardCopy.headshotFit || "Cover").toLowerCase(),
    transform: `translate(${businessCardCopy.headshotX || 0}%, ${businessCardCopy.headshotY || 0}%) scale(${(businessCardCopy.headshotZoom || 100) / 100})`
  };
  const initials = agentName
    .split(" ")
    .filter(Boolean)
    .slice(0, 2)
    .map((part) => part[0])
    .join("")
    .toUpperCase();

  return (
    <div className="business-preview-shell">
      <div className="business-card-proof">
        <div>
          <p className="proof-label">Front</p>
          <article className={classNames("print-card card-front immersive-card", styleClass)} ref={businessCardFrontRef} style={{ "--card-red": brand.primaryColor, "--card-gold": brand.gold }}>
            <div className="print-safe-area">
              <header className="card-header-line">
                <Wordmark brand={brand} compact variant={frontLogoVariant} />
                <span>{businessCardCopy.licenseLine || "Advertised by Nex Realty"}</span>
              </header>

              <section className="card-identity-row">
                <div className="card-name-block">
                  <p>{businessCardCopy.title || "Real Estate Professional"}</p>
                  <h3>{agentName}</h3>
                  <em>{businessCardCopy.tagline || "Modern brokerage. Bold marketing. Smarter moves."}</em>
                </div>
                <div className={classNames("card-headshot", `shape-${(businessCardCopy.headshotShape || "Square").toLowerCase()}`)}>
                  {agentHeadshot ? <img src={agentHeadshot.dataUrl} alt={agentName} style={headshotStyle} /> : <strong>{initials || "NX"}</strong>}
                </div>
              </section>

              <section className="card-contact-grid">
                <BusinessContact icon="Phone" label="Call" value={phone} />
                <BusinessContact icon="Mail" label="Email" value={email} />
                <BusinessContact icon="Globe" label="Web" value={website} />
                <BusinessContact icon="AtSign" label="Social" value={social} />
              </section>
            </div>
          </article>
        </div>

        <div>
          <p className="proof-label">Back</p>
          <article className={classNames("print-card card-back immersive-card", styleClass)} ref={businessCardBackRef} style={{ "--card-red": brand.primaryColor, "--card-gold": brand.gold }}>
            <div className="print-safe-area back-safe-area">
              <Wordmark brand={brand} compact variant={backLogoVariant} />
              <div className="back-message">
                <h3>{businessCardCopy.tagline || "Modern brokerage. Bold marketing. Smarter moves."}</h3>
                <p>{businessCardCopy.title || "Real Estate Professional"} | {agentName}</p>
              </div>
              <div className="back-footer">
                <div className="qr-card">
                  <Icon name="QrCode" size={38} />
                  <span>{website}<small>{social}</small></span>
                </div>
                <p>Equal Housing Opportunity. Information deemed reliable but not guaranteed.</p>
              </div>
            </div>
          </article>
        </div>
      </div>
    </div>
  );
}

function BusinessContact({ icon, label, value }) {
  return (
    <div>
      <Icon name={icon} size={15} />
      <p>{label}</p>
      <span>{value}</span>
    </div>
  );
}

function FeatureStat({ icon, label, value, onCommit }) {
  return (
    <div>
      <Icon name={icon} size={21} />
      {onCommit ? (
        <EditableCanvasText as="strong" value={value} onCommit={onCommit} />
      ) : (
        <strong>{value}</strong>
      )}
      <span>{label}</span>
    </div>
  );
}

function SavedProjects({ projects, loadProject, duplicateProject, deleteProject, brand }) {
  return (
    <section className="panel saved-projects-panel">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Saved projects</p>
          <h3>Marketing project library</h3>
        </div>
      </div>
      {projects.length ? (
        <div className="project-grid">
          {projects.map((project) => (
            <article className="project-card" key={project.id}>
              <ProjectThumbnail project={project} brand={brand} />
              <p className="eyebrow">{project.fields && project.fields.marketingType}</p>
              <h4>{project.title}</h4>
              <p>{project.output ? project.output.primary : "Saved project"}</p>
              <div className="project-card-foot">
                <span>Edited {new Date(project.updatedAt || project.createdAt).toLocaleDateString()}</span>
                <button className="btn secondary small-btn" onClick={() => loadProject(project)}>
                  <Icon name="FolderOpen" />
                  <span>Open</span>
                </button>
                <button className="btn secondary small-btn" onClick={() => duplicateProject(project)}>
                  <Icon name="CopyPlus" />
                  <span>Duplicate</span>
                </button>
                <button className="btn danger-soft small-btn" onClick={() => deleteProject(project.id)}>
                  <Icon name="Trash2" />
                  <span>Delete</span>
                </button>
              </div>
            </article>
          ))}
        </div>
      ) : (
        <EmptyState icon="FolderOpen" title="No saved projects yet" body="Save a generated campaign or flyer to see it here." />
      )}
    </section>
  );
}

function BrandSettings({ brand, setBrand, saveBrand, role, allowedUsers, setAllowedUsers, setToast }) {
  async function updateLogo(event, key) {
    const file = event.target.files && event.target.files[0];
    if (!file) return;
    const upload = await fileToDataUrl(file);
    setBrand((current) => ({ ...current, [key]: upload.dataUrl }));
  }

  const disabled = !isAdminRole(role);

  return (
    <section className="settings-grid">
      <div className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Admin</p>
            <h3>Brand settings</h3>
          </div>
        </div>
        {disabled && <div className="suggestion-strip">Admin access is required to edit brokerage-wide defaults.</div>}

        <div className={classNames("settings-form", disabled && "muted")}>
          <Field label="Company name" value={brand.companyName} onChange={(value) => setBrand((current) => ({ ...current, companyName: value }))} />
          <div className="brand-logo-manager">
            <div className="logo-swatch light">
              <Wordmark brand={brand} variant="light" />
              <label className="field">
                <span>Black-letter logo for light backgrounds</span>
                <input type="file" accept="image/*" onChange={(event) => updateLogo(event, "logoBlackDataUrl")} disabled={disabled} />
              </label>
            </div>
            <div className="logo-swatch dark">
              <Wordmark brand={brand} variant="dark" />
              <label className="field">
                <span>White-letter logo for dark backgrounds</span>
                <input type="file" accept="image/*" onChange={(event) => updateLogo(event, "logoWhiteDataUrl")} disabled={disabled} />
              </label>
            </div>
          </div>
          <div className="color-grid">
            {[
              ["Primary red", "primaryColor"],
              ["Black", "black"],
              ["Charcoal", "charcoal"],
              ["White", "white"],
              ["Gold accent", "gold"]
            ].map(([label, key]) => (
              <label className="field color-field" key={key}>
                <span>{label}</span>
                <input type="color" value={brand[key]} onChange={(event) => setBrand((current) => ({ ...current, [key]: event.target.value }))} disabled={disabled} />
              </label>
            ))}
          </div>
          <Field label="Default disclaimer" value={brand.defaultDisclaimer} onChange={(value) => setBrand((current) => ({ ...current, defaultDisclaimer: value }))} as="textarea" />
          <Field label="Recruiting disclaimer" value={brand.recruitingDisclaimer} onChange={(value) => setBrand((current) => ({ ...current, recruitingDisclaimer: value }))} as="textarea" />
          <button className="btn primary" onClick={saveBrand} disabled={disabled}>
            <Icon name="Save" />
            <span>Save Brand Settings</span>
          </button>
        </div>
      </div>

      <div className="settings-side">
        <div className="panel brand-preview-panel">
          <p className="eyebrow">Preview</p>
          <div className="brand-preview" style={{ "--brand-red": brand.primaryColor, "--brand-gold": brand.gold }}>
            <Wordmark brand={brand} variant="dark" />
            <h3>Modern, bold, high-performance real estate marketing.</h3>
            <p>{brand.defaultDisclaimer}</p>
          </div>
        </div>
        <AccessManager allowedUsers={allowedUsers} setAllowedUsers={setAllowedUsers} role={role} setToast={setToast} />
      </div>
    </section>
  );
}

function AccessManager({ allowedUsers, setAllowedUsers, role, setToast }) {
  const [draft, setDraft] = useState({ name: "", email: "", role: "Agent", password: "", active: true });
  const [editing, setEditing] = useState({});
  const [passwords, setPasswords] = useState({});
  const [resetLink, setResetLink] = useState("");
  const disabled = !isAdminRole(role);

  async function addUser() {
    if (!draft.email.trim()) return;
    try {
      const saved = await API.request("/api/users", {
        method: "POST",
        body: JSON.stringify(draft)
      });
      setAllowedUsers((current) => {
        const withoutDuplicate = current.filter((user) => user.email !== saved.email);
        return [saved, ...withoutDuplicate].sort((a, b) => a.email.localeCompare(b.email));
      });
      setDraft({ name: "", email: "", role: "Agent", password: "", active: true });
      setToast("User account added.");
    } catch (error) {
      setToast(error.message || "Could not add user.");
    }
  }

  function editableUser(user) {
    return editing[user.email] || {
      name: user.name || "",
      email: user.email || "",
      role: user.role || "Agent",
      active: user.active !== false
    };
  }

  async function saveUser(user) {
    const patch = editableUser(user);
    try {
      const saved = await API.request(`/api/users/${encodeURIComponent(user.id || user.email)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
      setAllowedUsers((current) => current.map((item) => (item.id === user.id || item.email === user.email ? saved : item)).sort((a, b) => a.email.localeCompare(b.email)));
      setEditing((current) => {
        const next = { ...current };
        delete next[user.email];
        return next;
      });
      setToast("User updated.");
    } catch (error) {
      setToast(error.message || "Could not update user.");
    }
  }

  async function toggleUser(user) {
    const nextActive = user.active === false;
    setEditing((current) => ({
      ...current,
      [user.email]: { ...editableUser(user), active: nextActive }
    }));
    try {
      const saved = await API.request(`/api/users/${encodeURIComponent(user.id || user.email)}`, {
        method: "PUT",
        body: JSON.stringify({ ...editableUser(user), active: nextActive })
      });
      setAllowedUsers((current) => current.map((item) => (item.id === user.id || item.email === user.email ? saved : item)));
      setToast(nextActive ? "User reactivated." : "User deactivated.");
    } catch (error) {
      setToast(error.message || "Could not update user status.");
    }
  }

  async function updatePassword(user) {
    const password = passwords[user.email] || "";
    if (!password) return;
    try {
      const saved = await API.request(`/api/users/${encodeURIComponent(user.id || user.email)}/password`, {
        method: "POST",
        body: JSON.stringify({ password })
      });
      setAllowedUsers((current) => current.map((item) => (item.id === user.id || item.email === user.email ? saved : item)));
      setPasswords((current) => ({ ...current, [user.email]: "" }));
      setToast("Password updated.");
    } catch (error) {
      setToast(error.message || "Could not update password.");
    }
  }

  async function generateResetLink(user) {
    try {
      const response = await API.request(`/api/users/${encodeURIComponent(user.id || user.email)}/reset-link`, { method: "POST" });
      setResetLink(response.resetLink || "");
      setToast(response.message || "Password reset link generated.");
    } catch (error) {
      setToast(error.message || "Could not generate reset link.");
    }
  }

  return (
    <div className="panel access-panel">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">User management</p>
          <h3>Agents and admins</h3>
        </div>
      </div>
      {disabled && <div className="suggestion-strip">Only admins can manage Nex Central users.</div>}
      <div className={classNames("access-form", disabled && "muted")}>
        <Field label="Name" value={draft.name} onChange={(value) => setDraft((current) => ({ ...current, name: value }))} placeholder="Avery Morgan" />
        <Field label="Email" value={draft.email} onChange={(value) => setDraft((current) => ({ ...current, email: value }))} type="email" placeholder="agent@nexrealty.com" />
        <Field label="Role" value={draft.role} onChange={(value) => setDraft((current) => ({ ...current, role: value }))} as="select" options={["Agent", "Admin", "Super Admin"]} />
        <Field label="Temporary password" value={draft.password} onChange={(value) => setDraft((current) => ({ ...current, password: value }))} type="password" placeholder="Optional, at least 8 characters" />
        <button className="btn primary" onClick={addUser} disabled={disabled}>
          <Icon name="UserPlus" />
          <span>Add User</span>
        </button>
      </div>

      {resetLink && (
        <div className="reset-link-box">
          <strong>Password reset link</strong>
          <p>{resetLink}</p>
          <button className="btn secondary" onClick={() => copyToClipboard(resetLink, setToast)}>
            <Icon name="Copy" />
            <span>Copy Link</span>
          </button>
        </div>
      )}

      <div className="allowed-user-list">
        {allowedUsers.length ? (
          allowedUsers.map((user) => (
            <article key={user.email}>
              <div className="user-edit-grid">
                <Field label="Name" value={editableUser(user).name} onChange={(value) => setEditing((current) => ({ ...current, [user.email]: { ...editableUser(user), name: value } }))} />
                <Field label="Email" value={editableUser(user).email} onChange={(value) => setEditing((current) => ({ ...current, [user.email]: { ...editableUser(user), email: value } }))} type="email" />
                <Field label="Role" value={editableUser(user).role} onChange={(value) => setEditing((current) => ({ ...current, [user.email]: { ...editableUser(user), role: value } }))} as="select" options={["Agent", "Admin", "Super Admin"]} />
                <Field label="New password" value={passwords[user.email] || ""} onChange={(value) => setPasswords((current) => ({ ...current, [user.email]: value }))} type="password" placeholder="Set/change password" />
              </div>
              <em className={user.active === false ? "inactive" : ""}>{user.active === false ? "Inactive" : user.role}</em>
              <div className="user-action-row">
                <button className="btn secondary" onClick={() => saveUser(user)} disabled={disabled}>
                  <Icon name="Save" />
                  <span>Save</span>
                </button>
                <button className="btn secondary" onClick={() => updatePassword(user)} disabled={disabled || !passwords[user.email]}>
                  <Icon name="KeyRound" />
                  <span>Set Password</span>
                </button>
                <button className="btn secondary" onClick={() => generateResetLink(user)} disabled={disabled || user.active === false}>
                  <Icon name="Mail" />
                  <span>Reset Link</span>
                </button>
                <button className={classNames("btn", user.active === false ? "primary" : "danger-soft")} onClick={() => toggleUser(user)} disabled={disabled}>
                  <Icon name={user.active === false ? "UserCheck" : "UserX"} />
                  <span>{user.active === false ? "Reactivate" : "Deactivate"}</span>
                </button>
              </div>
            </article>
          ))
        ) : (
          <p className="access-empty">No users have been added yet.</p>
        )}
      </div>
    </div>
  );
}

function TemplateLibrary({ templates, setTemplates, brand, role, setSelectedTemplateId, setFields, setActivePage, setToast }) {
  const [draft, setDraft] = useState({
    name: "",
    category: "Custom",
    headline: "",
    badge: "",
    accent: brand.primaryColor,
    style: "classic"
  });
  const [query, setQuery] = useState("");
  const [category, setCategory] = useState("All");
  const [favorites, setFavorites] = useState(() => JSON.parse(localStorage.getItem("nex-template-favorites") || "[]"));
  const [recent, setRecent] = useState(() => JSON.parse(localStorage.getItem("nex-template-recent") || "[]"));
  const disabled = !isAdminRole(role);
  const categories = ["All", "Favorites", "Recently Used", ...Array.from(new Set(templates.map((template) => template.category || "Custom")))];
  const filteredTemplates = templates.filter((template) => {
    const search = `${template.name} ${template.category} ${template.headline} ${template.badge}`.toLowerCase();
    const matchesSearch = !query || search.includes(query.toLowerCase());
    const matchesCategory =
      category === "All" ||
      (category === "Favorites" && favorites.includes(template.id)) ||
      (category === "Recently Used" && recent.includes(template.id)) ||
      template.category === category;
    return matchesSearch && matchesCategory;
  });

  async function addTemplate() {
    if (!draft.name.trim()) return;
    try {
      const saved = await API.request("/api/templates", {
        method: "POST",
        body: JSON.stringify(draft)
      });
      setTemplates((current) => [saved, ...current]);
      setDraft({ name: "", category: "Custom", headline: "", badge: "", accent: brand.primaryColor, style: "classic" });
      setToast("Template added.");
    } catch (error) {
      setToast(error.message || "Could not add template.");
    }
  }

  async function updateTemplate(id, patch) {
    const currentTemplate = templates.find((template) => template.id === id) || {};
    let saved;
    try {
      saved = await API.request(`/api/templates/${encodeURIComponent(id)}`, {
        method: "PUT",
        body: JSON.stringify(patch)
      });
    } catch (error) {
      saved = await API.request("/api/templates", {
        method: "POST",
        body: JSON.stringify({ ...currentTemplate, ...patch, id })
      });
    }
    setTemplates((current) => current.map((template) => (template.id === id ? saved : template)));
  }

  async function deleteTemplate(id) {
    try {
      await API.request(`/api/templates/${encodeURIComponent(id)}`, { method: "DELETE" });
      setTemplates((current) => current.filter((template) => template.id !== id));
      setToast("Template deleted.");
    } catch (error) {
      setToast(error.message || "Could not delete template.");
    }
  }

  function useTemplate(template) {
    setSelectedTemplateId(template.id);
    setFields((current) => ({ ...current, marketingType: template.name }));
    const nextRecent = [template.id, ...recent.filter((id) => id !== template.id)].slice(0, 8);
    setRecent(nextRecent);
    localStorage.setItem("nex-template-recent", JSON.stringify(nextRecent));
    setActivePage("Marketing Studio");
  }

  function toggleFavorite(id) {
    const next = favorites.includes(id) ? favorites.filter((item) => item !== id) : [id, ...favorites];
    setFavorites(next);
    localStorage.setItem("nex-template-favorites", JSON.stringify(next));
  }

  return (
    <section className="template-layout premium-template-library">
      <div className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Template Library</p>
            <h3>Template library</h3>
          </div>
        </div>
        <input className="studio-search" value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Search campaign, listing, social, recruiting..." />
        <div className="studio-filter-row">
          {categories.map((item) => (
            <button key={item} className={category === item ? "selected" : ""} onClick={() => setCategory(item)}>{item}</button>
          ))}
        </div>
        {disabled && <div className="suggestion-strip">Switch to Admin role to add or edit reusable templates.</div>}
        <div className={classNames("template-editor", disabled && "muted")}>
          <Field label="Template name" value={draft.name} onChange={(value) => setDraft((current) => ({ ...current, name: value }))} placeholder="Buyer Seminar Flyer" />
          <Field label="Category" value={draft.category} onChange={(value) => setDraft((current) => ({ ...current, category: value }))} />
          <Field label="Headline" value={draft.headline} onChange={(value) => setDraft((current) => ({ ...current, headline: value }))} placeholder="BUYER SEMINAR" />
          <Field label="Badge" value={draft.badge} onChange={(value) => setDraft((current) => ({ ...current, badge: value }))} placeholder="Free Event" />
          <Field label="Layout style" value={draft.style} onChange={(value) => setDraft((current) => ({ ...current, style: value }))} as="select" options={flyerLayoutOptions.map((option) => option.id)} />
          <label className="field color-field">
            <span>Accent</span>
            <input type="color" value={draft.accent} onChange={(event) => setDraft((current) => ({ ...current, accent: event.target.value }))} disabled={disabled} />
          </label>
          <button className="btn primary" onClick={addTemplate} disabled={disabled}>
            <Icon name="Plus" />
            <span>Add Template</span>
          </button>
        </div>
      </div>

      <div className="template-grid">
        {filteredTemplates.map((template) => (
          <article className="template-card" key={template.id}>
            <MiniTemplatePreview template={template} brand={brand} />
            <p>{template.category}</p>
            <input value={template.name} onChange={(event) => updateTemplate(template.id, { name: event.target.value })} disabled={disabled} />
            <input value={template.headline} onChange={(event) => updateTemplate(template.id, { headline: event.target.value })} disabled={disabled} />
            <span>{template.badge}</span>
            <span>{template.style || "classic"}</span>
            <div className="template-card-actions">
              <button className="btn secondary small-btn" onClick={() => useTemplate(template)}><Icon name="MousePointerClick" /><span>Use</span></button>
              <button className="icon-btn small" onClick={() => toggleFavorite(template.id)} title="Favorite">
                <Icon name={favorites.includes(template.id) ? "Star" : "StarOff"} size={15} />
              </button>
              {!disabled && <button className="icon-btn small" onClick={() => deleteTemplate(template.id)} title="Delete template"><Icon name="Trash2" size={15} /></button>}
            </div>
          </article>
        ))}
      </div>
    </section>
  );
}

function SocialGenerator({ output, setToast }) {
  return (
    <section className="panel">
      <div className="panel-heading">
        <div>
          <p className="eyebrow">Social media</p>
          <h3>Captions, hashtags, reels, and posts</h3>
        </div>
      </div>
      {output ? (
        <MarketingOutput output={output} setToast={setToast} />
      ) : (
        <EmptyState icon="Megaphone" title="Generate a social campaign" body="Choose Facebook, Instagram, LinkedIn, Story, or Reel Script in the input panel." />
      )}
    </section>
  );
}

function EmailSmsGenerator({ output, setToast }) {
  if (!output) {
    return (
      <section className="panel">
        <EmptyState icon="Mail" title="Generate email and SMS copy" body="Ask the assistant for listing announcements, open house invites, nurture messages, recruiting emails, and text scripts." />
      </section>
    );
  }

  return (
    <section className="email-sms-grid">
      <article className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">Email</p>
            <h3>Ready-to-send draft</h3>
          </div>
          <button className="icon-btn" onClick={() => copyToClipboard(output.emailVersion, setToast)} title="Copy email">
            <Icon name="Copy" />
          </button>
        </div>
        <pre className="copy-pre">{output.emailVersion}</pre>
      </article>
      <article className="panel">
        <div className="panel-heading">
          <div>
            <p className="eyebrow">SMS</p>
            <h3>Short text script</h3>
          </div>
          <button className="icon-btn" onClick={() => copyToClipboard(output.smsVersion, setToast)} title="Copy SMS">
            <Icon name="Copy" />
          </button>
        </div>
        <p className="sms-card">{output.smsVersion}</p>
      </article>
    </section>
  );
}

function LoginScreen({ onLogin, onForgotPassword, onResetPassword, brand, resetToken = "" }) {
  const [mode, setMode] = useState(resetToken ? "reset" : "login");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [confirmPassword, setConfirmPassword] = useState("");
  const [error, setError] = useState("");
  const [message, setMessage] = useState(resetToken ? "Enter a new password for your Nex Central account." : "");
  const [isSubmitting, setIsSubmitting] = useState(false);

  useEffect(() => {
    if (resetToken) {
      setMode("reset");
      setMessage("Enter a new password for your Nex Central account.");
    }
  }, [resetToken]);

  async function submit(event) {
    event.preventDefault();
    setError("");
    setMessage("");
    setIsSubmitting(true);
    try {
      if (mode === "forgot") {
        const response = await onForgotPassword(email);
        setMessage(response?.message || "If that email is registered, a reset link has been sent.");
      } else if (mode === "reset") {
        const response = await onResetPassword({ token: resetToken, password, confirmPassword });
        setMessage(response?.message || "Password updated. You can sign in now.");
        setPassword("");
        setConfirmPassword("");
        window.history.replaceState({}, document.title, window.location.pathname);
        setMode("login");
      } else {
        await onLogin({ email, password });
      }
    } catch (loginError) {
      setError(friendlyNetworkError(loginError, "Could not sign in."));
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    <main className="login-screen">
      <section className="login-panel">
        <div className="login-brand">
          <Wordmark brand={brand} variant="light" className="login-logo" />
          <div>
            <p className="eyebrow">Secure agent access</p>
            <h1>{APP_NAME}</h1>
          </div>
        </div>
        <p>
          Sign in with your Nex Central email and password. Admins can create accounts, reset passwords, and manage user
          access.
        </p>
        <form onSubmit={submit} className="login-form">
          {mode !== "reset" && <Field label="Email" value={email} onChange={setEmail} type="email" placeholder="agent@nexrealty.com" />}
          {mode === "login" && <Field label="Password" value={password} onChange={setPassword} type="password" placeholder="Enter your password" />}
          {mode === "reset" && (
            <>
              <Field label="New password" value={password} onChange={setPassword} type="password" placeholder="At least 8 characters" />
              <Field label="Confirm new password" value={confirmPassword} onChange={setConfirmPassword} type="password" placeholder="Re-enter password" />
            </>
          )}
          {message && <div className="login-message">{message}</div>}
          {error && (
            <div className="login-error">
              {error}
              <small>If it still fails, contact Nex admin for access help.</small>
            </div>
          )}
          <button className="btn primary" type="submit" disabled={isSubmitting}>
            <Icon name={isSubmitting ? "LoaderCircle" : "LockKeyhole"} className={isSubmitting ? "spin" : ""} />
            <span>
              {isSubmitting
                ? "Working"
                : mode === "forgot"
                  ? "Send Reset Link"
                  : mode === "reset"
                    ? "Reset Password"
                    : "Sign In"}
            </span>
          </button>
          <div className="auth-links">
            {mode === "login" ? (
              <button type="button" onClick={() => { setMode("forgot"); setError(""); setMessage(""); }}>
                Forgot password?
              </button>
            ) : (
              <button type="button" onClick={() => { setMode("login"); setError(""); setMessage(""); }}>
                Back to sign in
              </button>
            )}
          </div>
        </form>
      </section>
    </main>
  );
}

function Toast({ message }) {
  if (!message) return null;
  return <div className="toast">{message}</div>;
}

function App() {
  const [activePage, setActivePage] = useState("Dashboard");
  const [authChecked, setAuthChecked] = useState(false);
  const [currentUser, setCurrentUser] = useState(null);
  const [resetToken, setResetToken] = useState(() => new URLSearchParams(window.location.search).get("resetToken") || "");
  const [brand, setBrand] = useState(localDefaultBrand);
  const [templates, setTemplates] = useState([]);
  const [projects, setProjects] = useState([]);
  const [allowedUsers, setAllowedUsers] = useState([]);
  const [announcements, setAnnouncements] = useState([]);
  const [quickLinks, setQuickLinks] = useState([]);
  const [trainingResources, setTrainingResources] = useState([]);
  const [transactionGuides, setTransactionGuides] = useState([]);
  const [supportTickets, setSupportTickets] = useState([]);
  const [buyerNotifications, setBuyerNotifications] = useState([]);
  const [chatUnreadTotal, setChatUnreadTotal] = useState(0);
  const [latestChat, setLatestChat] = useState(null);
  const [buyerTrackerStartView, setBuyerTrackerStartView] = useState("");
  const [fields, setFields] = useState(defaultFields);
  const [prompt, setPrompt] = useState("");
  const [output, setOutput] = useState(null);
  const [messages, setMessages] = useState([
    {
      id: "welcome",
      type: "assistant",
      text:
        "Tell me what you need. I can create listing posts, flyer copy, open house promotions, rental ads, recruiting copy, emails, SMS scripts, and agent marketing content."
    }
  ]);
  const [propertyPhotos, setPropertyPhotos] = useState([]);
  const [agentHeadshot, setAgentHeadshot] = useState(null);
  const [selectedTemplateId, setSelectedTemplateId] = useState("just-listed");
  const [isGenerating, setIsGenerating] = useState(false);
  const [projectTitle, setProjectTitle] = useState("Untitled Nex marketing project");
  const [saveStatus, setSaveStatus] = useState("Ready");
  const [toast, setToast] = useState("");
  const [canInstall, setCanInstall] = useState(Boolean(window.__nexInstallPrompt));
  const [flyerCopy, setFlyerCopy] = useState(defaultFlyerCopy);
  const [businessCardCopy, setBusinessCardCopy] = useState(defaultBusinessCardCopy);
  const flyerRef = useRef(null);
  const businessCardFrontRef = useRef(null);
  const businessCardBackRef = useRef(null);
  const role = currentUser?.role || "Agent";

  async function loadAppData(user) {
    const [brandSettings, templateList, savedProjects, accessList, announcementList, linkList, trainingList, guideList, ticketList, notificationList, chatBootstrap] = await Promise.all([
      API.request("/api/brand-settings").catch(() => localDefaultBrand),
      API.request("/api/templates").catch(() => []),
      API.request("/api/projects").catch(() => []),
      isAdminRole(user?.role) ? API.request("/api/users").catch(() => []) : Promise.resolve([]),
      API.request("/api/announcements").catch(() => []),
      API.request("/api/quick-links").catch(() => []),
      API.request("/api/training-resources").catch(() => []),
      API.request("/api/transaction-guides").catch(() => []),
      API.request("/api/support-tickets").catch(() => []),
      API.request("/api/notifications").catch(() => []),
      API.request("/api/chat/bootstrap").catch(() => ({ unreadCounts: {} }))
    ]);
    const mergedTemplates = mergeTemplates(templateList);
    const nextBrand = { ...localDefaultBrand, ...brandSettings };
    setBrand(nextBrand);
    setTemplates(mergedTemplates);
    setProjects(savedProjects);
    setAllowedUsers(accessList);
    setAnnouncements(announcementList);
    setQuickLinks(linkList);
    setTrainingResources(trainingList);
    setTransactionGuides(guideList);
    setSupportTickets(ticketList);
    setBuyerNotifications(notificationList);
    setChatUnreadTotal(Object.values(chatBootstrap.unreadCounts || {}).reduce((sum, value) => sum + Number(value || 0), 0));
    setLatestChat((chatBootstrap.messages || []).slice(-1)[0] || null);
    setFlyerCopy((current) => ({ ...current, footer: nextBrand.defaultDisclaimer }));
    if (mergedTemplates[0] && !mergedTemplates.find((template) => template.id === selectedTemplateId)) {
      setSelectedTemplateId(mergedTemplates[0].id);
    }
  }

  useEffect(() => {
    let mounted = true;
    API.request("/api/auth/me")
      .then(async (auth) => {
        if (!mounted) return;
        setCurrentUser(auth.user || null);
        setAuthChecked(true);
        if (auth.authenticated && auth.user) {
          await loadAppData(auth.user);
        }
      })
      .catch(() => {
        if (!mounted) return;
        setCurrentUser(null);
        setAuthChecked(true);
      });
    return () => {
      mounted = false;
    };
  }, []);

  useEffect(() => {
    if (!toast) return;
    const timer = setTimeout(() => setToast(""), 2600);
    return () => clearTimeout(timer);
  }, [toast]);

  useEffect(() => {
    if (!currentUser) return;
    setFields((current) => ({
      ...current,
      agentName: current.agentName || currentUser.name || "",
      agentEmail: current.agentEmail || currentUser.email || ""
    }));
  }, [currentUser]);

  useEffect(() => {
    if (!currentUser) return;
    setSaveStatus("Autosaving");
    const timer = setTimeout(() => {
      const draft = {
        projectTitle,
        fields,
        flyerCopy,
        businessCardCopy,
        selectedTemplateId,
        propertyPhotos,
        agentHeadshot,
        updatedAt: new Date().toISOString()
      };
      localStorage.setItem(`nex-marketing-draft-${currentUser.email}`, JSON.stringify(draft));
      setSaveStatus("Autosaved");
    }, 700);
    return () => clearTimeout(timer);
  }, [currentUser, projectTitle, fields, flyerCopy, businessCardCopy, selectedTemplateId, propertyPhotos, agentHeadshot]);

  useEffect(() => {
    function handleInstallReady() {
      setCanInstall(true);
    }
    function handleInstalled() {
      setCanInstall(false);
      setToast("Nex Central installed.");
    }
    window.addEventListener("nex-install-ready", handleInstallReady);
    window.addEventListener("nex-installed", handleInstalled);
    return () => {
      window.removeEventListener("nex-install-ready", handleInstallReady);
      window.removeEventListener("nex-installed", handleInstalled);
    };
  }, []);

  useEffect(() => {
    const matchingTemplate =
      templates.find((template) => template.name.toLowerCase() === fields.marketingType.toLowerCase()) ||
      templates.find((template) => fields.marketingType.toLowerCase().includes(template.name.toLowerCase().replace(" / production recognition", "")));
    if (matchingTemplate) {
      setSelectedTemplateId(matchingTemplate.id);
      setFlyerCopy((current) => ({
        ...current,
        headline: matchingTemplate.headline || current.headline,
        badge: matchingTemplate.badge || current.badge,
        layout: matchingTemplate.style || current.layout
      }));
    }
  }, [fields.marketingType, templates]);

  const selectedTemplate = useMemo(
    () => templates.find((template) => template.id === selectedTemplateId) || templates[0],
    [templates, selectedTemplateId]
  );

  async function generateContent(customPrompt) {
    const requestPrompt = customPrompt || prompt || `Create ${fields.marketingType} marketing for Nex Realty.`;
    setIsGenerating(true);
    const userMessage = {
      id: `user-${Date.now()}`,
      type: "user",
      text: requestPrompt
    };
    setMessages((current) => [...current, userMessage]);

    try {
      const generated = await API.request("/api/generate", {
        method: "POST",
        body: JSON.stringify({ prompt: requestPrompt, fields, brandSettings: brand })
      });
      applyGeneratedContent(generated);
      setMessages((current) => [
        ...current,
        { id: `assistant-${Date.now()}`, type: "assistant", output: generated }
      ]);
      setPrompt("");
      return generated;
    } catch (error) {
      const generated = fallbackGenerate({ fields, brandSettings: brand, prompt: requestPrompt });
      applyGeneratedContent(generated);
      setMessages((current) => [
        ...current,
        { id: `assistant-${Date.now()}`, type: "assistant", output: generated }
      ]);
      setToast("Generated with local fallback.");
      return generated;
    } finally {
      setIsGenerating(false);
    }
  }

  function applyGeneratedContent(generated) {
    setOutput(generated);
    setFlyerCopy((current) => ({
      ...current,
      headline: generated.suggestedHeadline || current.headline,
      badge: selectedTemplate ? selectedTemplate.badge : current.badge,
      body: generated.flyerReadyCopy || generated.primary || current.body,
      cta: generated.suggestedCTA || current.cta,
      footer: generated.footerDisclaimer || brand.defaultDisclaimer
    }));
  }

  async function onChatSubmit(event) {
    event.preventDefault();
    if (!prompt.trim()) return;
    await generateContent(prompt.trim());
  }

  async function saveBrand() {
    try {
      const saved = await API.request("/api/brand-settings", {
        method: "PUT",
        body: JSON.stringify(brand)
      });
      setBrand(saved);
      setFlyerCopy((current) => ({ ...current, footer: saved.defaultDisclaimer }));
      setToast("Brand settings saved.");
    } catch (error) {
      setToast("Could not save brand settings.");
    }
  }

  async function saveProject() {
    const title = projectTitle && projectTitle !== "Untitled Nex marketing project"
      ? projectTitle
      : `${fields.marketingType || "Marketing"} - ${fields.propertyAddress || fields.agentName || "Nex Realty"}`;
    const project = {
      title,
      projectType: fields.marketingType || "Marketing",
      thumbnail: {
        headline: flyerCopy.headline,
        badge: flyerCopy.badge,
        accent: flyerCopy.accentColor || selectedTemplate?.accent || brand.primaryColor,
        format: flyerCopy.canvasFormat
      },
      fields,
      output,
      flyerCopy,
      businessCardCopy,
      selectedTemplateId,
      propertyPhotos,
      agentHeadshot
    };
    try {
      const saved = await API.request("/api/projects", {
        method: "POST",
        body: JSON.stringify(project)
      });
      setProjects((current) => [saved, ...current]);
      setProjectTitle(saved.title || title);
      setSaveStatus("Saved");
      setToast("Project saved.");
    } catch (error) {
      const saved = { ...project, id: String(Date.now()), createdAt: new Date().toISOString() };
      setProjects((current) => [saved, ...current]);
      localStorage.setItem("nex-projects", JSON.stringify([saved, ...projects]));
      setSaveStatus("Saved locally");
      setToast("Project saved in this browser.");
    }
  }

  function loadProject(project) {
    setProjectTitle(project.title || "Untitled Nex marketing project");
    if (project.fields) setFields(project.fields);
    if (project.output) setOutput(project.output);
    if (project.flyerCopy) setFlyerCopy({ ...defaultFlyerCopy, ...project.flyerCopy });
    if (project.businessCardCopy) setBusinessCardCopy({ ...defaultBusinessCardCopy, ...project.businessCardCopy });
    if (project.selectedTemplateId) setSelectedTemplateId(project.selectedTemplateId);
    setPropertyPhotos(project.propertyPhotos || []);
    setAgentHeadshot(project.agentHeadshot || null);
    setActivePage("Marketing Studio");
    setToast("Project loaded.");
  }

  async function duplicateProject(project = null) {
    const source = project || {
      title: projectTitle,
      fields,
      output,
      flyerCopy,
      businessCardCopy,
      selectedTemplateId,
      propertyPhotos,
      agentHeadshot
    };
    const duplicate = {
      ...source,
      title: `${source.title || projectTitle || "Nex marketing project"} Copy`,
      createdAt: undefined,
      updatedAt: undefined
    };
    try {
      const saved = await API.request("/api/projects", {
        method: "POST",
        body: JSON.stringify(duplicate)
      });
      setProjects((current) => [saved, ...current]);
      setToast("Project duplicated.");
      return saved;
    } catch (error) {
      const saved = { ...duplicate, id: String(Date.now()), createdAt: new Date().toISOString() };
      setProjects((current) => [saved, ...current]);
      setToast("Project duplicated locally.");
      return saved;
    }
  }

  async function deleteProject(projectId) {
    try {
      await API.request(`/api/projects/${encodeURIComponent(projectId)}`, { method: "DELETE" });
      setProjects((current) => current.filter((project) => project.id !== projectId));
      setToast("Project deleted.");
    } catch (error) {
      setProjects((current) => current.filter((project) => project.id !== projectId));
      setToast(error.message || "Project removed locally.");
    }
  }

  async function saveAsTemplate() {
    const template = {
      name: projectTitle || flyerCopy.headline || "Nex Template",
      category: fields.marketingType || "Custom",
      headline: flyerCopy.headline,
      badge: flyerCopy.badge,
      accent: flyerCopy.accentColor || selectedTemplate?.accent || brand.primaryColor,
      style: flyerCopy.layout || "classic"
    };
    try {
      const saved = await API.request("/api/templates", {
        method: "POST",
        body: JSON.stringify(template)
      });
      setTemplates((current) => [saved, ...current]);
      setToast("Template saved.");
    } catch (error) {
      setToast(error.message || "Admin access is required to save global templates.");
    }
  }

  async function captureElement(element, format, fileBase) {
    if (!element) return;
    setToast(`Preparing ${format.toUpperCase()}...`);
    if (document.activeElement && document.activeElement.blur) {
      document.activeElement.blur();
      await new Promise((resolve) => setTimeout(resolve, 50));
    }
    const canvas = await html2canvas(element, {
      scale: 3,
      backgroundColor: "#ffffff",
      useCORS: true
    });
    const image = canvas.toDataURL("image/png", 1);
    const fileName = fileBase.toLowerCase().replace(/[^a-z0-9]+/g, "-");

    if (format === "png" || format === "jpg") {
      const link = document.createElement("a");
      link.download = `${fileName}.${format}`;
      link.href = format === "jpg" ? canvas.toDataURL("image/jpeg", 0.92) : image;
      link.click();
      setToast(`${format.toUpperCase()} downloaded.`);
      return;
    }

    const { jsPDF } = window.jspdf;
    const pdf = new jsPDF({
      orientation: canvas.width > canvas.height ? "landscape" : "portrait",
      unit: "px",
      format: [canvas.width, canvas.height]
    });
    pdf.addImage(image, "PNG", 0, 0, canvas.width, canvas.height);
    pdf.save(`${fileName}.pdf`);
    setToast("PDF downloaded.");
  }

  function captureFlyer(format) {
    const selectedFormat = getCreativeFormat(flyerCopy.canvasFormat);
    return captureElement(flyerRef.current, format, `nex-realty-${fields.marketingType || "flyer"}-${selectedFormat.id}`);
  }

  function captureBusinessCard(format, side) {
    const ref = side === "back" ? businessCardBackRef : businessCardFrontRef;
    return captureElement(ref.current, format, `nex-realty-business-card-${side}-${businessCardCopy.displayName || fields.agentName || "agent"}`);
  }

  function addStockPhoto(photo) {
    const image = {
      name: `${photo.title}.svg`,
      type: "image/svg+xml",
      source: "Nex Studio Stock",
      dataUrl: photo.dataUrl
    };
    setPropertyPhotos((current) => [image, ...current].slice(0, 8));
    setToast(`${photo.title} added to photos.`);
  }

  async function downloadQuickVideo(orientation = flyerCopy.videoOrientation || "vertical") {
    setToast("Rendering quick video...");
    try {
      const blob = await createQuickMarketingVideo({ brand, fields, flyerCopy, propertyPhotos, orientation });
      const spec = getVideoOrientation(orientation);
      downloadBlob(blob, `nex-realty-${spec.id}-video-${fields.marketingType || "marketing"}.webm`.toLowerCase().replace(/[^a-z0-9.]+/g, "-"));
      setFlyerCopy((current) => ({ ...current, videoOrientation: orientation }));
      setToast(`${spec.name} downloaded as WEBM.`);
    } catch (error) {
      setToast(error.message || "Could not render quick video.");
    }
  }

  function openQuickLink(link) {
    if (!link) return;
    if (link.id === "nexu" || link.title === "NexU") {
      setActivePage("NexU / Training Center");
      return;
    }
    const url = String(link.url || "").trim();
    if (!url || url === "#") {
      setToast(`${link.title} link has not been added yet.`);
      return;
    }
    if (url.startsWith("app:")) {
      setActivePage(url.replace("app:", ""));
      return;
    }
    window.open(url, "_blank", "noopener,noreferrer");
  }

  function getMarketingShareText() {
    const locationLine = [fields.propertyAddress, fields.city, fields.state].filter(Boolean).join(", ");
    const hashtags = Array.isArray(output?.hashtags) ? output.hashtags.join(" ") : "#NexRealty #RealEstate";
    return [
      flyerCopy.headline || output?.suggestedHeadline || fields.marketingType || "Nex Realty",
      output?.primary || flyerCopy.body,
      locationLine,
      flyerCopy.cta || fields.cta,
      hashtags
    ]
      .filter(Boolean)
      .join("\n\n");
  }

  function getMarketingShareUrl() {
    return flyerCopy.qrUrl || flyerCopy.agentWebsite || window.location.origin;
  }

  async function shareMarketingDesign(platform = "native") {
    const text = getMarketingShareText();
    const url = getMarketingShareUrl();
    const title = projectTitle || flyerCopy.headline || "Nex Realty marketing asset";

    if (platform === "native" && navigator.share) {
      try {
        await navigator.share({ title, text, url });
        setToast("Share sheet opened.");
        return;
      } catch (error) {
        if (error?.name === "AbortError") return;
      }
    }

    try {
      await navigator.clipboard.writeText(text);
    } catch (error) {
      // Sharing still works even if the browser blocks clipboard access.
    }

    const encodedText = encodeURIComponent(text);
    const encodedUrl = encodeURIComponent(url);
    const shareTargets = {
      facebook: `https://www.facebook.com/sharer/sharer.php?u=${encodedUrl}`,
      x: `https://twitter.com/intent/tweet?text=${encodedText}`,
      linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedUrl}`,
      instagram: "https://www.instagram.com/"
    };

    const target = shareTargets[platform];
    if (target) {
      window.open(target, "_blank", "noopener,noreferrer");
      setToast(platform === "instagram" ? "Caption copied. Download the asset and upload it to Instagram." : "Caption copied and share page opened.");
      return;
    }

    setToast("Caption copied. Download the design before posting.");
  }

  function openVistaPrint() {
    const formatId = flyerCopy.canvasFormat || "letter-flyer";
    const printUrl = formatId === "business-card"
      ? "https://www.vistaprint.com/business-cards"
      : formatId === "postcard"
        ? "https://www.vistaprint.com/postcards"
        : "https://www.vistaprint.com/marketing-materials";
    window.open(printUrl, "_blank", "noopener,noreferrer");
    setToast("Opening VistaPrint. Upload the exported PDF, PNG, or JPG there.");
  }

  async function installApp() {
    if (window.nexPromptInstall) {
      const result = await window.nexPromptInstall();
      setCanInstall(false);
      if (result?.outcome === "accepted") setToast("Nex Central installed.");
      else if (result?.outcome === "unavailable") setToast("Use your browser menu to add Nex Central to your home screen.");
      return;
    }
    setToast("Use your browser menu to add Nex Central to your home screen.");
  }

  async function login(credentials) {
    const auth = await API.request("/api/auth/login", {
      method: "POST",
      body: JSON.stringify(credentials),
      retries: 2,
      retryDelay: 700
    });
    setCurrentUser(auth.user);
    try {
      await loadAppData(auth.user);
      setToast("Signed in.");
    } catch (error) {
      console.error("Post-login data load failed", error);
      setToast("Signed in. Refresh if dashboard data looks incomplete.");
    }
  }

  async function requestPasswordReset(email) {
    return API.request("/api/auth/forgot-password", {
      method: "POST",
      body: JSON.stringify({ email }),
      retries: 1
    });
  }

  async function resetPassword(payload) {
    const response = await API.request("/api/auth/reset-password", {
      method: "POST",
      body: JSON.stringify(payload),
      retries: 1
    });
    setResetToken("");
    return response;
  }

  async function logout() {
    await API.request("/api/auth/logout", { method: "POST" }).catch(() => {});
    setCurrentUser(null);
    setAllowedUsers([]);
    setProjects([]);
    setAnnouncements([]);
    setQuickLinks([]);
    setTrainingResources([]);
    setTransactionGuides([]);
    setSupportTickets([]);
    setBuyerNotifications([]);
    setChatUnreadTotal(0);
    setLatestChat(null);
    setBuyerTrackerStartView("");
    setActivePage("Dashboard");
    setToast("");
  }

  function renderMain() {
    if (activePage === "Dashboard") {
      return (
        <Dashboard
          projects={projects}
          templates={templates}
          announcements={announcements}
          quickLinks={quickLinks}
          trainingResources={trainingResources}
          tickets={supportTickets}
          latestChat={latestChat}
          chatUnreadTotal={chatUnreadTotal}
          buyerNotifications={buyerNotifications}
          currentUser={currentUser}
          setActivePage={setActivePage}
          setBuyerTrackerStartView={setBuyerTrackerStartView}
          setSelectedTemplateId={setSelectedTemplateId}
          setFields={setFields}
          brand={brand}
          openQuickLink={openQuickLink}
        />
      );
    }
    if (activePage === "Marketing Studio") {
      return (
        <MarketingStudioHub
          setActivePage={setActivePage}
          templates={templates}
          brand={brand}
          selectedTemplateId={selectedTemplateId}
          setSelectedTemplateId={setSelectedTemplateId}
          fields={fields}
          setFields={setFields}
          flyerCopy={flyerCopy}
          setFlyerCopy={setFlyerCopy}
          propertyPhotos={propertyPhotos}
          setPropertyPhotos={setPropertyPhotos}
          agentHeadshot={agentHeadshot}
          setAgentHeadshot={setAgentHeadshot}
          flyerRef={flyerRef}
          output={output}
          prompt={prompt}
          setPrompt={setPrompt}
          isGenerating={isGenerating}
          onGenerate={generateContent}
          onQuickAction={generateContent}
          onSaveProject={saveProject}
          onDuplicateProject={() => duplicateProject()}
          onDownloadPdf={() => captureFlyer("pdf")}
          onDownloadPng={() => captureFlyer("png")}
          onDownloadJpg={() => captureFlyer("jpg")}
          onPrint={openVistaPrint}
          onSharePlatform={shareMarketingDesign}
          onSaveAsTemplate={saveAsTemplate}
          projects={projects}
          loadProject={loadProject}
          setToast={setToast}
          projectTitle={projectTitle}
          setProjectTitle={setProjectTitle}
          saveStatus={saveStatus}
        />
      );
    }
    if (activePage === "Bulletin Board") {
      return <BulletinBoard announcements={announcements} role={role} setAnnouncements={setAnnouncements} setToast={setToast} />;
    }
    if (activePage === "Quick Links") {
      return <QuickLinksCenter quickLinks={quickLinks} role={role} setQuickLinks={setQuickLinks} setToast={setToast} openQuickLink={openQuickLink} />;
    }
    if (activePage === "Nex Agent Assist") {
      return <NexAgentAssist setToast={setToast} />;
    }
    if (activePage === "Nex Chat") {
      return <NexChat currentUser={currentUser} role={role} setToast={setToast} onUnreadTotalChange={setChatUnreadTotal} onLatestMessageChange={setLatestChat} />;
    }
    if (activePage === "Nex lite CRM" || activePage === "Nex CRM") {
      return <NexLiteCrm currentUser={currentUser} role={role} setToast={setToast} />;
    }
    if (activePage === "Production Tracker" || activePage === "Agent Production Calculator") {
      return <AgentProductionCalculator currentUser={currentUser} setToast={setToast} />;
    }
    if (activePage === "Buyer Contract Tracker") {
      return (
        <BuyerContractTrackerPage
          currentUser={currentUser}
          role={role}
          setToast={setToast}
          notifications={buyerNotifications}
          setNotifications={setBuyerNotifications}
          initialView={buyerTrackerStartView}
          onInitialViewConsumed={() => setBuyerTrackerStartView("")}
        />
      );
    }
    if (activePage === "NexU / Training Center") {
      return <TrainingCenter resources={trainingResources} role={role} setTrainingResources={setTrainingResources} setToast={setToast} />;
    }
    if (activePage === "Transaction Help Center") {
      return (
        <TransactionHelpCenter
          guides={transactionGuides}
          role={role}
          setTransactionGuides={setTransactionGuides}
          setActivePage={setActivePage}
          setToast={setToast}
        />
      );
    }
    if (activePage === "Support Desk") {
      return <SupportDesk currentUser={currentUser} role={role} tickets={supportTickets} setSupportTickets={setSupportTickets} setToast={setToast} />;
    }
    if (activePage === "Admin Panel") {
      return (
        <AdminPanel
          role={role}
          announcements={announcements}
          quickLinks={quickLinks}
          trainingResources={trainingResources}
          transactionGuides={transactionGuides}
          supportTickets={supportTickets}
          allowedUsers={allowedUsers}
          setActivePage={setActivePage}
        />
      );
    }
    if (activePage === "Flyer Builder") {
      return (
        <FlyerBuilder
          brand={brand}
          fields={fields}
          setFields={setFields}
          templates={templates}
          selectedTemplateId={selectedTemplateId}
          setSelectedTemplateId={setSelectedTemplateId}
          flyerCopy={flyerCopy}
          setFlyerCopy={setFlyerCopy}
          propertyPhotos={propertyPhotos}
          agentHeadshot={agentHeadshot}
          flyerRef={flyerRef}
          onDownloadPdf={() => captureFlyer("pdf")}
          onDownloadPng={() => captureFlyer("png")}
          onDownloadVideo={downloadQuickVideo}
          onGenerate={() => generateContent(`Regenerate flyer copy for ${fields.marketingType}.`)}
          onAddStockPhoto={addStockPhoto}
        />
      );
    }
    if (activePage === "Quick Video Builder") {
      return (
        <QuickVideoBuilder
          brand={brand}
          fields={fields}
          flyerCopy={flyerCopy}
          setFlyerCopy={setFlyerCopy}
          propertyPhotos={propertyPhotos}
          onAddStockPhoto={addStockPhoto}
          onGenerate={() => generateContent(`Create a short ${flyerCopy.videoOrientation === "horizontal" ? "horizontal video" : "vertical reel"} script for ${fields.marketingType}.`)}
          onDownloadVideo={downloadQuickVideo}
        />
      );
    }
    if (activePage === "Business Card Builder") {
      return (
        <BusinessCardBuilder
          brand={brand}
          fields={fields}
          agentHeadshot={agentHeadshot}
          setAgentHeadshot={setAgentHeadshot}
          businessCardCopy={businessCardCopy}
          setBusinessCardCopy={setBusinessCardCopy}
          businessCardFrontRef={businessCardFrontRef}
          businessCardBackRef={businessCardBackRef}
          onDownloadFrontPdf={() => captureBusinessCard("pdf", "front")}
          onDownloadBackPdf={() => captureBusinessCard("pdf", "back")}
          onDownloadFrontPng={() => captureBusinessCard("png", "front")}
          onDownloadBackPng={() => captureBusinessCard("png", "back")}
          onPrint={openVistaPrint}
        />
      );
    }
    if (activePage === "Social Media Generator") {
      return <SocialGenerator output={output} setToast={setToast} />;
    }
    if (activePage === "Email/SMS Generator") {
      return <EmailSmsGenerator output={output} setToast={setToast} />;
    }
    if (activePage === "Saved Projects") {
      return <SavedProjects projects={projects} loadProject={loadProject} duplicateProject={duplicateProject} deleteProject={deleteProject} brand={brand} />;
    }
    if (activePage === "Brand Settings / Admin") {
      return (
        <BrandSettings
          brand={brand}
          setBrand={setBrand}
          saveBrand={saveBrand}
          role={role}
          allowedUsers={allowedUsers}
          setAllowedUsers={setAllowedUsers}
          setToast={setToast}
        />
      );
    }
    if (activePage === "Template Library") {
      return <TemplateLibrary templates={templates} setTemplates={setTemplates} brand={brand} role={role} setSelectedTemplateId={setSelectedTemplateId} setFields={setFields} setActivePage={setActivePage} setToast={setToast} />;
    }
    return (
      <ChatWorkspace
        messages={messages}
        prompt={prompt}
        setPrompt={setPrompt}
        onSubmit={onChatSubmit}
        isGenerating={isGenerating}
        output={output}
        setToast={setToast}
        onQuickAction={generateContent}
      />
    );
  }

  if (!authChecked) {
    return (
      <main className="login-screen">
        <section className="login-panel">
          <div className="login-brand">
            <Wordmark brand={brand} variant="light" className="login-logo" />
            <div>
              <p className="eyebrow">Loading</p>
              <h1>{APP_NAME}</h1>
            </div>
          </div>
        </section>
      </main>
    );
  }

  if (!currentUser) {
    return <LoginScreen onLogin={login} onForgotPassword={requestPasswordReset} onResetPassword={resetPassword} brand={brand} resetToken={resetToken} />;
  }

  const isMarketingPage = ["Marketing Studio", ...marketingStudioPages.map(([page]) => page)].includes(activePage);
  const showMarketingInputPanel = marketingStudioPages.map(([page]) => page).includes(activePage);

  return (
    <div className={classNames("app-shell", !showMarketingInputPanel && "portal-shell")}>
      <Sidebar activePage={activePage} setActivePage={setActivePage} currentUser={currentUser} onLogout={logout} brand={brand} chatUnreadTotal={chatUnreadTotal} />
      <main className="main-area">
        <Header
          activePage={activePage}
          onGenerate={() => generateContent()}
          onSaveProject={saveProject}
          isGenerating={isGenerating}
          isMarketingPage={isMarketingPage}
          canInstall={canInstall}
          onInstall={installApp}
          notifications={buyerNotifications}
          onOpenNotifications={() => {
            setBuyerTrackerStartView("Notifications");
            setActivePage("Buyer Contract Tracker");
          }}
        />
        {renderMain()}
      </main>
      {showMarketingInputPanel && (
        <InputPanel
          fields={fields}
          setFields={setFields}
          agentHeadshot={agentHeadshot}
          setAgentHeadshot={setAgentHeadshot}
          propertyPhotos={propertyPhotos}
          setPropertyPhotos={setPropertyPhotos}
        />
      )}
      <Toast message={toast} />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
