// Woodbury Operator — shared data, roles, page header + toast (window globals).
(function () {
  window.WB_SITES = ["Lashibi Townhouses", "Lashibi Executive Houses", "Mirage", "Onehive Tse Addo", "Hamlet", "Cantonments City"];

  // Role model. `nav` lists which sidebar entries the role sees; `scope` is the
  // context shown in the sidebar. Super Admin is national; others are site-scoped.
  window.WB_ROLES = [
    { key: "super", label: "Super Administrator", scope: "All Ghana Locations", scopeLabel: "Admin Scope",
      nav: ["Dashboard", "Properties", "Tenants", "Screening", "Leases", "Maintenance", "Vendors", "Inspections", "Move In/Out", "Documents", "Messages", "Reports", "Users", "Audit Log", "Settings"] },
    { key: "site", label: "Site Administrator", scope: "Lashibi Townhouses", scopeLabel: "Assigned Site",
      nav: ["Dashboard", "Properties", "Tenants", "Screening", "Leases", "Maintenance", "Inspections", "Move In/Out", "Documents", "Messages", "Reports"] },
    { key: "operator", label: "Site Operator", scope: "Lashibi Townhouses", scopeLabel: "Assigned Site",
      nav: ["Dashboard", "Maintenance", "Vendors", "Inspections", "Messages"] },
    { key: "agent", label: "Leasing Agent", scope: "Cantonments City + 2", scopeLabel: "Assigned Sites",
      nav: ["Dashboard", "Screening", "Leases", "Tenants", "Move In/Out", "Documents", "Messages"] },
  ];

  // ---- Permissions (real capability gating, not just nav) ----
  // Capabilities each role holds. Screens call window.WBCan("cap") to gate actions.
  window.WB_PERMS = {
    super:    ["*"],
    site:     ["property.edit", "unit.add", "tenant.add", "tenant.edit", "lease.new", "lease.renew", "screening.decide", "maintenance.new", "maintenance.assign", "inspection.new", "doc.upload", "move.manage", "alert.send"],
    operator: ["maintenance.new", "maintenance.assign", "inspection.new"],
    agent:    ["tenant.add", "tenant.edit", "lease.new", "lease.renew", "screening.decide", "screening.invite", "doc.upload", "move.manage"],
  };
  window.WBCan = function (cap) {
    const role = window.WBRoleKey || "super";
    const perms = window.WB_PERMS[role] || [];
    return perms.indexOf("*") !== -1 || perms.indexOf(cap) !== -1;
  };

  // ---- Cross-screen store + event bus (connects the lifecycle loops) ----
  // Newly created records added by one screen and picked up live by others.
  window.WB_NEW = { tenants: [], moves: [], tickets: [] };
  window.WBbus = new EventTarget();
  window.WBadd = function (kind, item) {
    (window.WB_NEW[kind] = window.WB_NEW[kind] || []).push(item);
    window.WBbus.dispatchEvent(new CustomEvent(kind));
  };
  // React hook: returns the current WB_NEW[kind] list, re-rendering on change.
  window.WBuseNew = function (kind) {
    const [, tick] = React.useReducer((x) => x + 1, 0);
    React.useEffect(() => {
      const h = () => tick();
      window.WBbus.addEventListener(kind, h);
      return () => window.WBbus.removeEventListener(kind, h);
    }, [kind]);
    return window.WB_NEW[kind] || [];
  };

  // ---- Resident → Operator bridge (same-origin localStorage channel) ----
  // Resident portal writes maintenance requests here; operator Maintenance reads them.
  window.WBportalKey = "wb_portal_requests";
  window.WBreadPortalRequests = function () {
    try { return JSON.parse(localStorage.getItem(window.WBportalKey) || "[]"); } catch (e) { return []; }
  };
  // Convenience: render children only if permitted.
  window.WBGate = function ({ cap, children, fallback }) {
    return window.WBCan(cap) ? children : (fallback || null);
  };

  // ---- Empty & loading states (uniform across tables) ----
  window.WBEmpty = function ({ icon, title, message, action }) {
    return (
      <div style={{ display: "flex", flexDirection: "column", alignItems: "center", textAlign: "center", padding: "48px 24px", gap: 6 }}>
        <div style={{ width: 56, height: 56, borderRadius: "var(--radius-lg)", background: "var(--cream-100)", display: "flex", alignItems: "center", justifyContent: "center", marginBottom: 6 }}>
          <span className="wb-ico" style={{ width: 26, height: 26, color: "var(--text-muted)" }}><i data-lucide={icon || "inbox"}></i></span>
        </div>
        <div style={{ fontFamily: "var(--font-serif)", fontWeight: 700, fontSize: 18, color: "var(--text-heading)" }}>{title}</div>
        {message && <p style={{ margin: 0, fontSize: 14, color: "var(--text-muted)", maxWidth: 340, lineHeight: 1.5 }}>{message}</p>}
        {action && <div style={{ marginTop: 10 }}>{action}</div>}
      </div>
    );
  };
  window.WBSkeletonRows = function ({ rows, cols }) {
    rows = rows || 4; cols = cols || 4;
    return (
      <div>
        <style>{"@keyframes wbShimmer{0%{opacity:.5}50%{opacity:1}100%{opacity:.5}}"}</style>
        {Array.from({ length: rows }).map((_, r) => (
          <div key={r} style={{ display: "grid", gridTemplateColumns: "repeat(" + cols + ", 1fr)", gap: 16, alignItems: "center", padding: "18px 24px", borderTop: r ? "1px solid var(--border)" : "none" }}>
            {Array.from({ length: cols }).map((__, c) => (
              <div key={c} style={{ display: "flex", alignItems: "center", gap: 12 }}>
                {c === 0 && <div style={{ width: 40, height: 40, borderRadius: 9999, background: "var(--cream-200)", flexShrink: 0, animation: "wbShimmer 1.3s ease-in-out infinite" }} />}
                <div style={{ height: 12, borderRadius: 6, background: "var(--cream-200)", width: c === 0 ? "60%" : (c === cols - 1 ? "40%" : "75%"), animation: "wbShimmer 1.3s ease-in-out infinite", animationDelay: (r * 0.05 + c * 0.08) + "s" }} />
              </div>
            ))}
          </div>
        ))}
      </div>
    );
  };
  // Hook: brief loading state on first mount (demonstrates skeletons).
  window.WBuseLoad = function (ms) {
    const [loading, setLoading] = React.useState(true);
    React.useEffect(() => { const t = setTimeout(() => setLoading(false), ms || 550); return () => clearTimeout(t); }, []);
    return loading;
  };

  window.WB_DATA = {
    applicants: [
      { name: "Efua Asante", email: "efua.asante@gmail.com", phone: "+233 24 411 9283", asset: "Lashibi Townhouses", status: "Pending", avatar: "../../assets/images/av-efua.jpg" },
      { name: "Kofi Mensah", email: "kofi.mensah@yahoo.com", phone: "+233 20 887 3341", asset: "Lashibi Townhouses", status: "Verified", avatar: "../../assets/images/av-kofi.jpg" },
      { name: "Ama Osei", email: "ama.osei@outlook.com", phone: "+233 55 120 9987", asset: "Cantonments City", status: "Verified", avatar: "../../assets/images/av-ama.jpg" },
      { name: "Yaw Boateng", email: "yboateng@gmail.com", phone: "+233 24 330 7765", asset: "Mirage", status: "Pending", avatar: "../../assets/images/av-yaw.jpg" },
      { name: "Nana Adjei", email: "nana.adjei@ghana.com", phone: "+233 50 445 2210", asset: "Onehive Tse Addo", status: "Verified", avatar: "../../assets/images/av-nana.jpg" },
    ],
    maintenance: [
      { unit: "House 1 — AC Leak", meta: "Reported 2 hours ago · HVAC team notified", tone: "danger", badge: "Urgent" },
      { unit: "Villa 7 — Roof Tile Repair", meta: "Scheduled for tomorrow · Roofer assigned", tone: "accent" },
      { unit: "Flat 2B — Geyser Fault", meta: "Parts ordered · ETA 3 days", tone: "success" },
      { unit: "Villa 12 — Gate Remote Sync", meta: "Low priority · Backlog", tone: "neutral" },
    ],
    properties: [
      { name: "Lashibi Townhouses", type: "3-Bedroom Townhouses", units: 16, occ: 56, vacant: 7, status: "Premium", img: "../../assets/images/featured-cantonments.jpg" },
      { name: "Lashibi Executive Houses", type: "Executive Houses", units: 8, occ: 88, vacant: 1, status: "Premium", img: "../../assets/images/property-1.jpg" },
      { name: "Mirage", type: "Apartments", units: 24, occ: 92, vacant: 2, status: "Standard", img: "../../assets/images/building-hero.jpg" },
      { name: "Onehive Tse Addo", type: "Serviced Apartments", units: 18, occ: 78, vacant: 4, status: "Standard", img: "../../assets/images/featured-cantonments.jpg" },
      { name: "Hamlet", type: "Townhouses", units: 12, occ: 100, vacant: 0, status: "Flagship", img: "../../assets/images/property-1.jpg" },
      { name: "Cantonments City", type: "Luxury Apartments", units: 30, occ: 90, vacant: 3, status: "Flagship", img: "../../assets/images/building-hero.jpg" },
    ],
    documents: [
      { name: "Signed Tenancy Agreement.pdf", meta: "248 KB · Signed 12 Apr", tone: "brand" },
      { name: "Land Registration Papers.pdf", meta: "1.1 MB · Uploaded 30 Mar", tone: "sunken" },
      { name: "Insurance Certificate 2026.pdf", meta: "512 KB · Uploaded 04 Feb", tone: "sunken" },
      { name: "Lashibi Townhouses Floor Plan.pdf", meta: "3.4 MB · Uploaded 21 Jan", tone: "accent" },
    ],
    vendors: [
      { name: "Kojo Roofing & Works", trade: "Roofing", phone: "+233 24 555 1200", jobs: 12, rating: "On-site", tone: "accent", icon: "home" },
      { name: "Mensah Plumbing Co.", trade: "Plumbing", phone: "+233 20 771 4432", jobs: 28, rating: "Preferred", tone: "brand", icon: "droplet" },
      { name: "AirCool HVAC Services", trade: "HVAC", phone: "+233 55 880 9910", jobs: 19, rating: "Preferred", tone: "success", icon: "wind" },
      { name: "Accra Electricals Ltd", trade: "Electrical", phone: "+233 24 330 0055", jobs: 9, rating: "Standard", tone: "sunken", icon: "zap" },
      { name: "GateTech Automation", trade: "Access & Gates", phone: "+233 50 442 8821", jobs: 6, rating: "Standard", tone: "sunken", icon: "door-closed" },
    ],
    users: [
      { name: "Kwame Ansah", email: "kwame.ansah@woodbury.com", role: "Super Administrator", site: "All Ghana Locations", status: "Active", twoFA: true, avatar: "../../assets/images/avatar-1.jpg" },
      { name: "Adjoa Owusu", email: "adjoa.owusu@woodbury.com", role: "Site Administrator", site: "Lashibi Townhouses", status: "Active", twoFA: true, avatar: "../../assets/images/av-ama.jpg" },
      { name: "Kwesi Appiah", email: "kwesi.appiah@woodbury.com", role: "Site Operator", site: "Cantonments City", status: "Active", twoFA: false, avatar: "../../assets/images/av-yaw.jpg" },
      { name: "Abena Sarpong", email: "abena.sarpong@woodbury.com", role: "Leasing Agent", site: "Mirage + 2", status: "Invited", twoFA: false, avatar: "../../assets/images/av-efua.jpg" },
      { name: "Yaw Darko", email: "yaw.darko@woodbury.com", role: "Site Operator", site: "Onehive Tse Addo", status: "Suspended", twoFA: true, avatar: "../../assets/images/av-nana.jpg" },
    ],
  };

  window.WBPageHeader = function ({ eyebrow, title, sub, actions }) {
    return (
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: 24, flexWrap: "wrap" }}>
        <div style={{ display: "flex", flexDirection: "column", gap: 7 }}>
          <span style={{ fontFamily: "var(--font-sans)", fontWeight: 700, fontSize: 12, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--terracotta-600)" }}>{eyebrow}</span>
          <h1 style={{ margin: 0, fontFamily: "var(--font-serif)", fontWeight: 700, fontSize: 40, lineHeight: 1.05, color: "var(--text-heading)", whiteSpace: "pre-line" }}>{title}</h1>
          {sub && <p style={{ margin: 0, fontSize: 15, color: "var(--text-body)", maxWidth: 520 }}>{sub}</p>}
        </div>
        {actions && <div style={{ display: "flex", gap: 12, flexShrink: 0 }}>{actions}</div>}
      </div>
    );
  };

  // 6-digit verification code input (auto-advances). onChange(code), onComplete(code).
  window.WBCodeInput = function ({ onChange, onComplete }) {
    const [vals, setVals] = React.useState(["", "", "", "", "", ""]);
    const refs = React.useRef([]);
    const set = (i, v) => {
      v = v.replace(/\D/g, "").slice(-1);
      const next = vals.slice(); next[i] = v; setVals(next);
      const code = next.join("");
      onChange && onChange(code);
      if (v && i < 5) refs.current[i + 1] && refs.current[i + 1].focus();
      if (code.length === 6 && !next.includes("")) onComplete && onComplete(code);
    };
    const onKey = (i, e) => { if (e.key === "Backspace" && !vals[i] && i > 0) refs.current[i - 1] && refs.current[i - 1].focus(); };
    return (
      <div style={{ display: "flex", gap: 10, justifyContent: "center" }}>
        {vals.map((v, i) => (
          <input key={i} ref={(el) => (refs.current[i] = el)} value={v} inputMode="numeric" maxLength={1}
            onChange={(e) => set(i, e.target.value)} onKeyDown={(e) => onKey(i, e)}
            style={{ width: 52, height: 60, textAlign: "center", fontFamily: "var(--font-serif)", fontWeight: 700, fontSize: 26, color: "var(--text-heading)", border: "1px solid var(--border-strong)", borderRadius: "var(--radius-md)", background: "var(--surface)", outline: "none", boxShadow: "var(--shadow-xs)" }}
            onFocus={(e) => { e.target.style.borderColor = "var(--brand)"; e.target.style.boxShadow = "var(--focus-ring)"; }}
            onBlur={(e) => { e.target.style.borderColor = "var(--border-strong)"; e.target.style.boxShadow = "var(--shadow-xs)"; }} />
        ))}
      </div>
    );
  };

  // Units per property (drives the Add Tenant unit picker + Properties unit list).
  window.WB_UNITS = {
    "Lashibi Townhouses": [
      { label: "House 1", status: "Occupied", tenant: "Kofi Mensah" }, { label: "House 2", status: "Occupied", tenant: "Ama Osei" }, { label: "House 3", status: "Vacant" }, { label: "House 4", status: "Occupied", tenant: "Nii Armah" },
      { label: "House 5", status: "Occupied", tenant: "Efua Asante" }, { label: "House 6", status: "Vacant" }, { label: "House 7", status: "Occupied", tenant: "Yaw Boateng" }, { label: "House 8", status: "Vacant" },
      { label: "House 9", status: "Occupied", tenant: "Adjoa Owusu" }, { label: "House 10", status: "Occupied", tenant: "Kwesi Appiah" }, { label: "House 11", status: "Vacant" }, { label: "House 12", status: "Occupied", tenant: "Abena Sarpong" },
      { label: "House 13", status: "Vacant" }, { label: "House 14", status: "Vacant" }, { label: "House 15", status: "Occupied", tenant: "Nana Adjei" }, { label: "House 16", status: "Vacant" },
    ],
    "Lashibi Executive Houses": [
      { label: "House 1", status: "Occupied", tenant: "Kojo Danso" }, { label: "House 2", status: "Occupied", tenant: "Akua Frimpong" }, { label: "House 3", status: "Occupied", tenant: "Kwabena Osei" }, { label: "House 4", status: "Vacant" },
      { label: "House 5", status: "Occupied", tenant: "Yaa Serwaa" }, { label: "House 6", status: "Occupied", tenant: "Kofi Boadu" }, { label: "House 7", status: "Occupied", tenant: "Esi Quaye" }, { label: "House 8", status: "Occupied", tenant: "Kojo Danso" },
    ],
    "Mirage": [
      { label: "Apartment A1", status: "Occupied", tenant: "Ama Osei" }, { label: "Apartment A2", status: "Occupied", tenant: "Nii Armah" }, { label: "Apartment B3", status: "Vacant" }, { label: "Apartment C1", status: "Vacant" },
    ],
    "Onehive Tse Addo": [
      { label: "Unit 101", status: "Occupied", tenant: "Nana Adjei" }, { label: "Unit 104", status: "Vacant" }, { label: "Unit 202", status: "Occupied", tenant: "Efua Asante" }, { label: "Unit 205", status: "Vacant" },
    ],
    "Hamlet": [
      { label: "House 1", status: "Occupied", tenant: "Abena Sarpong" }, { label: "House 2", status: "Occupied", tenant: "Yaw Boateng" }, { label: "House 3", status: "Occupied", tenant: "Kwesi Appiah" }, { label: "House 4", status: "Occupied", tenant: "Adjoa Owusu" },
    ],
    "Cantonments City": [
      { label: "Apt 5A", status: "Occupied", tenant: "Kwabena Osei" }, { label: "Apt 9B", status: "Occupied", tenant: "Akua Frimpong" }, { label: "Apt 11C", status: "Vacant" }, { label: "Penthouse 2", status: "Vacant" },
    ],
  };
  window.WBunitsFor = function (site, onlyVacant) {
    const list = window.WB_UNITS[site] || [];
    return (onlyVacant ? list.filter((u) => u.status === "Vacant") : list).map((u) => u.label);
  };
  // Tenant names available to link to units.
  window.WB_TENANT_NAMES = ["Kofi Mensah", "Ama Osei", "Efua Asante", "Nana Adjei", "Nii Armah", "Yaw Boateng", "Adjoa Owusu", "Kwesi Appiah", "Abena Sarpong", "Kojo Danso", "Akua Frimpong", "Kwabena Osei", "Yaa Serwaa", "Kofi Boadu", "Esi Quaye"];

  // Tenant-linked documents. Keyed by tenant name; each files into a folder too.
  window.WB_TENANT_DOCS = {
    "Kofi Mensah": [
      { name: "Signed Tenancy Agreement.pdf", meta: "248 KB · Signed 12 Apr", folder: "Tenancy Agreements" },
      { name: "ID Verification.pdf", meta: "512 KB · Verified 10 Apr", folder: "Screening" },
    ],
  };
  window.WBAddTenantDoc = function (tenant, doc) {
    if (!window.WB_TENANT_DOCS[tenant]) window.WB_TENANT_DOCS[tenant] = [];
    window.WB_TENANT_DOCS[tenant].unshift(doc);
  };

  // Tenant roster (name + site) for targeted alerts.
  window.WB_TENANT_ROSTER = [
    { name: "Kofi Mensah", site: "Lashibi Townhouses" },
    { name: "Efua Asante", site: "Lashibi Townhouses" },
    { name: "Yaw Mensah", site: "Lashibi Townhouses" },
    { name: "Ama Osei", site: "Cantonments City" },
    { name: "Adjoa Boateng", site: "Cantonments City" },
    { name: "Yaw Boateng", site: "Mirage" },
    { name: "Akua Darko", site: "Mirage" },
    { name: "Nana Adjei", site: "Onehive Tse Addo" },
    { name: "Kojo Owusu", site: "Hamlet" },
    { name: "Abena Sarfo", site: "Lashibi Executive Houses" },
  ];

  // Reusable image-upload dropzone with live thumbnail previews.
  window.WBImageUpload = function ({ label }) {
    const [imgs, setImgs] = React.useState([]);
    const ref = React.useRef(null);
    const onFiles = (e) => {
      const files = Array.from(e.target.files || []);
      const urls = files.map((f) => URL.createObjectURL(f));
      setImgs((s) => [...s, ...urls]);
      if (urls.length) window.WBToast(urls.length + " image" + (urls.length === 1 ? "" : "s") + " added");
    };
    const remove = (u) => setImgs((s) => s.filter((x) => x !== u));
    return (
      <div>
        {label && <div style={{ fontFamily: "var(--font-sans)", fontWeight: 700, fontSize: 11, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--text-muted)", marginBottom: 8 }}>{label}</div>}
        <div onClick={() => ref.current && ref.current.click()} style={{ border: "2px dashed var(--border-strong)", borderRadius: "var(--radius-lg)", padding: "24px", textAlign: "center", background: "var(--cream-50)", cursor: "pointer" }}>
          <span className="wb-ico" style={{ width: 28, height: 28, color: "var(--text-muted)", display: "inline-flex" }}><i data-lucide="image-plus"></i></span>
          <div style={{ fontWeight: 600, fontSize: 14, color: "var(--text-heading)", marginTop: 8 }}>Click to upload images</div>
          <div style={{ fontSize: 12, color: "var(--text-muted)", marginTop: 2 }}>JPG or PNG · up to 10 MB each</div>
        </div>
        <input ref={ref} type="file" accept="image/*" multiple onChange={onFiles} style={{ display: "none" }} />
        {imgs.length > 0 && (
          <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginTop: 12 }}>
            {imgs.map((u) => (
              <div key={u} style={{ position: "relative", width: 76, height: 76, borderRadius: "var(--radius-md)", overflow: "hidden", boxShadow: "inset 0 0 0 1px var(--border)" }}>
                <img src={u} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                <button onClick={(e) => { e.stopPropagation(); remove(u); }} style={{ position: "absolute", top: 3, right: 3, width: 20, height: 20, borderRadius: 9999, border: "none", background: "rgba(40,24,9,0.7)", color: "#fff", cursor: "pointer", fontSize: 12, lineHeight: 1, display: "flex", alignItems: "center", justifyContent: "center" }}>×</button>
              </div>
            ))}
          </div>
        )}
      </div>
    );
  };

  // Shared Notify / Send Alert modal with tenant targeting.
  window.WBNotifyModal = function ({ open, onClose, title, defaultSite, lockSite }) {
    const NS = window.WoodburyPropertiesDesignSystem_7290ca;
    const { Modal, Input, Select, Textarea, Button } = NS;
    const [audience, setAudience] = React.useState("Residents only");
    const [site, setSite] = React.useState(defaultSite || "All Ghana Locations");
    const [sel, setSel] = React.useState([]);
    React.useEffect(() => { if (open) { setSel([]); setSite(defaultSite || "All Ghana Locations"); setAudience("Residents only"); } }, [open]);
    const specific = audience === "Specific tenants";
    const roster = window.WB_TENANT_ROSTER.filter((t) => site === "All Ghana Locations" ? true : t.site === site);
    const toggle = (n) => setSel((s) => s.includes(n) ? s.filter((x) => x !== n) : [...s, n]);
    const allSel = roster.length > 0 && sel.length === roster.length;
    const send = () => {
      let desc = specific ? (sel.length + " selected tenant" + (sel.length === 1 ? "" : "s")) : (audience + " · " + (site === "All Ghana Locations" ? "all sites" : site));
      onClose();
      window.WBToast("Alert sent to " + desc);
    };
    const siteOptions = lockSite ? [defaultSite] : ["All Ghana Locations", ...window.WB_SITES];
    return (
      <Modal open={open} onClose={onClose} icon="megaphone" title={title || "Send Alert"} subtitle="Broadcast a notice to residents." width={520}
        footer={<React.Fragment><Button variant="secondary" onClick={onClose}>Cancel</Button><Button variant="primary" icon="send" disabled={specific && sel.length === 0} onClick={send}>Send Alert</Button></React.Fragment>}>
        <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
          <Select label="Send to" value={audience} onChange={(e) => setAudience(e.target.value)} options={["All residents & staff", "Residents only", "Staff only", "Specific tenants"]} />
          <Select label="Site" value={site} onChange={(e) => setSite(e.target.value)} options={siteOptions} disabled={lockSite} />
          {specific && (
            <div>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
                <span style={{ fontSize: 11, fontWeight: 700, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--text-muted)" }}>Select tenants ({sel.length})</span>
                <a href="#" onClick={(e) => { e.preventDefault(); setSel(allSel ? [] : roster.map((r) => r.name)); }} style={{ fontSize: 12, fontWeight: 600, color: "var(--terracotta-600)" }}>{allSel ? "Clear all" : "Select all"}</a>
              </div>
              <div style={{ maxHeight: 200, overflowY: "auto", border: "1px solid var(--border)", borderRadius: "var(--radius-md)", padding: 6 }}>
                {roster.map((t) => {
                  const on = sel.includes(t.name);
                  return (
                    <label key={t.name} style={{ display: "flex", alignItems: "center", gap: 10, padding: "9px 8px", borderRadius: 8, cursor: "pointer" }}>
                      <input type="checkbox" checked={on} onChange={() => toggle(t.name)} style={{ accentColor: "var(--brand)", width: 16, height: 16 }} />
                      <span style={{ fontWeight: 600, fontSize: 14, color: "var(--text-heading)" }}>{t.name}</span>
                      <span style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-muted)" }}>{t.site}</span>
                    </label>
                  );
                })}
                {roster.length === 0 && <div style={{ padding: 16, textAlign: "center", fontSize: 13, color: "var(--text-muted)" }}>No tenants at this site.</div>}
              </div>
            </div>
          )}
          <Input label="Subject" defaultValue="Scheduled water maintenance" />
          <Textarea label="Message" defaultValue="Please note water supply will be briefly interrupted tomorrow between 9am and 12pm for pipe maintenance." />
        </div>
      </Modal>
    );
  };

  // Lightweight DOM toast — works across all screen React trees.
  window.WBToast = function (msg, tone) {
    let host = document.getElementById("wb-toast-host");
    if (!host) {
      host = document.createElement("div");
      host.id = "wb-toast-host";
      host.style.cssText = "position:fixed;right:24px;bottom:24px;z-index:2000;display:flex;flex-direction:column;gap:10px;align-items:flex-end";
      document.body.appendChild(host);
    }
    const el = document.createElement("div");
    const bg = tone === "danger" ? "#B00320" : tone === "accent" ? "#A66533" : "#38230F";
    el.style.cssText = "display:flex;align-items:center;gap:10px;padding:13px 18px;border-radius:12px;background:" + bg + ";color:#FAF9F6;font-family:'Plus Jakarta Sans',sans-serif;font-size:14px;font-weight:600;box-shadow:0 14px 34px -12px rgba(58,36,16,.5);opacity:0;transform:translateY(8px);transition:all .18s cubic-bezier(.22,1,.36,1);max-width:360px";
    el.textContent = "✓  " + msg;
    host.appendChild(el);
    requestAnimationFrame(() => { el.style.opacity = "1"; el.style.transform = "none"; });
    setTimeout(() => { el.style.opacity = "0"; el.style.transform = "translateY(8px)"; setTimeout(() => el.remove(), 220); }, 2600);
  };

  // Responsive: returns { mobile (<768), tablet (<1080) }.
  window.WBuseMedia = function () {
    const get = () => ({ mobile: window.matchMedia("(max-width: 767px)").matches, tablet: window.matchMedia("(max-width: 1079px)").matches });
    const [m, setM] = React.useState(get);
    React.useEffect(() => {
      const on = () => setM(get());
      window.addEventListener("resize", on);
      return () => window.removeEventListener("resize", on);
    }, []);
    return m;
  };

  // Confirm dialog (Promise-based, DOM-rendered). await window.WBConfirm({...}).
  window.WBConfirm = function (opts) {
    opts = opts || {};
    return new Promise((resolve) => {
      const scrim = document.createElement("div");
      scrim.style.cssText = "position:fixed;inset:0;z-index:3000;background:rgba(40,24,9,.42);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;padding:24px;font-family:'Plus Jakarta Sans',sans-serif;opacity:0;transition:opacity .14s";
      const danger = opts.danger;
      scrim.innerHTML = '<div style="width:420px;max-width:100%;background:#fff;border-radius:24px;box-shadow:0 14px 34px -12px rgba(58,36,16,.4);overflow:hidden">'
        + '<div style="padding:24px 24px 8px"><div style="font-family:Merriweather,serif;font-weight:700;font-size:20px;color:#4A2E15">' + (opts.title || "Are you sure?") + '</div>'
        + '<p style="margin:8px 0 0;font-size:14px;line-height:1.5;color:#6B7280">' + (opts.message || "") + '</p></div>'
        + '<div style="display:flex;justify-content:flex-end;gap:12px;padding:20px 24px">'
        + '<button data-x="c" style="height:40px;padding:0 20px;border-radius:9999px;border:1px solid #E9E8E3;background:#fff;font:600 13px/1 \'Plus Jakarta Sans\',sans-serif;color:#4A2E15;cursor:pointer">' + (opts.cancelLabel || "Cancel") + '</button>'
        + '<button data-x="o" style="height:40px;padding:0 22px;border-radius:9999px;border:none;background:' + (danger ? "#D90429" : "#5E3B1A") + ';color:#FAF9F6;font:600 13px/1 \'Plus Jakarta Sans\',sans-serif;cursor:pointer">' + (opts.confirmLabel || "Confirm") + '</button>'
        + '</div></div>';
      document.body.appendChild(scrim);
      requestAnimationFrame(() => { scrim.style.opacity = "1"; });
      const done = (v) => { scrim.style.opacity = "0"; setTimeout(() => scrim.remove(), 160); resolve(v); };
      scrim.querySelector('[data-x="o"]').onclick = () => done(true);
      scrim.querySelector('[data-x="c"]').onclick = () => done(false);
      scrim.onclick = (e) => { if (e.target === scrim) done(false); };
    });
  };

  // Notifications feed (super-admin view).
  window.WB_NOTIFS = [
    { icon: "triangle-alert", tone: "danger", title: "Urgent maintenance", body: "House 1 — AC Leak reported at Lashibi Townhouses.", time: "2h ago", unread: true, screen: "Maintenance" },
    { icon: "user-check", tone: "accent", title: "New applicant", body: "Efua Asante applied for Lashibi Townhouses.", time: "4h ago", unread: true, screen: "Screening" },
    { icon: "calendar-clock", tone: "accent", title: "Lease expiring", body: "Kofi Mensah's lease ends in 28 days.", time: "1d ago", unread: true, screen: "Leases" },
    { icon: "shield-check", tone: "success", title: "Screening cleared", body: "Ama Osei passed identity verification.", time: "1d ago", unread: false, screen: "Screening" },
    { icon: "file-text", tone: "brand", title: "Document uploaded", body: "Land Registration Papers.pdf added.", time: "2d ago", unread: false, screen: "Documents" },
  ];

  // Audit trail.
  window.WB_AUDIT = [
    { who: "Kwame Ansah", avatar: "../../assets/images/avatar-1.jpg", action: "changed role", target: "Abena Sarpong → Leasing Agent", cat: "Access", time: "Today, 09:42" },
    { who: "Adjoa Owusu", avatar: "../../assets/images/av-ama.jpg", action: "approved tenancy", target: "Ama Osei · Cantonments City", cat: "Screening", time: "Today, 08:15" },
    { who: "Kwesi Appiah", avatar: "../../assets/images/av-yaw.jpg", action: "assigned vendor", target: "AirCool HVAC → House 1", cat: "Maintenance", time: "Yesterday, 16:30" },
    { who: "Kwame Ansah", avatar: "../../assets/images/avatar-1.jpg", action: "enabled 2FA", target: "Yaw Darko", cat: "Security", time: "Yesterday, 11:02" },
    { who: "Adjoa Owusu", avatar: "../../assets/images/av-ama.jpg", action: "uploaded document", target: "Signed Tenancy Agreement.pdf", cat: "Documents", time: "12 Apr, 14:20" },
    { who: "Kwame Ansah", avatar: "../../assets/images/avatar-1.jpg", action: "sent broadcast", target: "Water maintenance notice · All sites", cat: "Comms", time: "10 Apr, 10:05" },
  ];

  // Inspections (move-in / move-out / routine).
  window.WB_INSPECTIONS = [
    { unit: "Villa 2 — Cantonments City", tenant: "Ama Osei", type: "Move-in", date: "22 Apr 2026", status: "Scheduled", tone: "accent", inspector: "Kwesi Appiah" },
    { unit: "House 1 — Lashibi Townhouses", tenant: "Kofi Mensah", type: "Routine", date: "18 Apr 2026", status: "In Progress", tone: "warning", inspector: "Adjoa Owusu" },
    { unit: "House 5 — Lashibi Townhouses", tenant: "Efua Asante", type: "Move-out", date: "30 Apr 2026", status: "Scheduled", tone: "accent", inspector: "Adjoa Owusu" },
    { unit: "Unit 101 — Onehive Tse Addo", tenant: "Nana Adjei", type: "Routine", date: "02 Apr 2026", status: "Completed", tone: "success", inspector: "Kwesi Appiah" },
  ];
  window.WB_INSPECTION_CHECKLIST = [
    { area: "Walls & ceilings", done: true },
    { area: "Flooring", done: true },
    { area: "Plumbing & fixtures", done: true },
    { area: "Electrical & lighting", done: false },
    { area: "Appliances", done: false },
    { area: "Doors, locks & keys", done: false },
    { area: "Windows & screens", done: false },
  ];

  // Compliance documents with expiry tracking.
  window.WB_COMPLIANCE = [
    { doc: "Fire Safety Certificate", site: "Lashibi Townhouses", expires: "12 May 2026", days: 24, status: "Expiring", tone: "danger" },
    { doc: "Property Insurance", site: "Portfolio-wide", expires: "04 Feb 2027", days: 292, status: "Valid", tone: "success" },
    { doc: "Land Registration", site: "Cantonments City", expires: "30 Sep 2026", days: 165, status: "Valid", tone: "success" },
    { doc: "Lift Inspection Permit", site: "Mirage", expires: "28 Apr 2026", days: 10, status: "Expiring", tone: "danger" },
    { doc: "Waste Management License", site: "Onehive Tse Addo", expires: "15 Jun 2026", days: 58, status: "Due Soon", tone: "warning" },
  ];

  // Broadcast / announcement history.
  window.WB_ANNOUNCEMENTS = [
    { subject: "Scheduled water maintenance", scope: "All Ghana Locations", audience: "All residents", sent: "10 Apr 2026", by: "Kwame Ansah" },
    { subject: "Gate access upgrade", scope: "Lashibi Townhouses", audience: "Residents", sent: "02 Apr 2026", by: "Adjoa Owusu" },
    { subject: "Q2 inspection schedule", scope: "Cantonments City", audience: "Residents & staff", sent: "28 Mar 2026", by: "Abena Sarpong" },
  ];

  // ==========================================================================
  // DEV BACKEND WIRING (WOO-19) — hydrate all seed data from wb-dev-api.
  // Same-origin GET /api/operator/bootstrap (Caddy proxies /api/* -> :8090).
  // Synchronous so server data is in place before any screen renders; falls
  // back SILENTLY to the inline design-system seed above if the API is
  // unreachable, so the prototype look never breaks. Also write-throughs any
  // records created in-app back to the dev backend.
  // ==========================================================================
  (function wireDevApi() {
    window.WB_API_BASE = "/api/operator";
    try {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", window.WB_API_BASE + "/bootstrap", false); // sync: block until seeded
      xhr.withCredentials = true;
      xhr.send(null);
      if (xhr.status !== 200) throw new Error("HTTP " + xhr.status);
      var B = JSON.parse(xhr.responseText);
      if (B.sites) window.WB_SITES = B.sites;
      if (B.roles) window.WB_ROLES = B.roles;
      if (B.perms) window.WB_PERMS = B.perms;
      if (B.data) window.WB_DATA = B.data;
      if (B.units) window.WB_UNITS = B.units;
      if (B.tenantNames) window.WB_TENANT_NAMES = B.tenantNames;
      if (B.tenantDocs) window.WB_TENANT_DOCS = B.tenantDocs;
      if (B.tenantRoster) window.WB_TENANT_ROSTER = B.tenantRoster;
      if (B.notifs) window.WB_NOTIFS = B.notifs;
      if (B.audit) window.WB_AUDIT = B.audit;
      if (B.inspections) window.WB_INSPECTIONS = B.inspections;
      if (B.inspectionChecklist) window.WB_INSPECTION_CHECKLIST = B.inspectionChecklist;
      if (B.compliance) window.WB_COMPLIANCE = B.compliance;
      if (B.announcements) window.WB_ANNOUNCEMENTS = B.announcements;
      if (B.live) {
        window.WB_NEW = { tenants: (B.live.tenants || []).slice(), moves: (B.live.moves || []).slice(), tickets: (B.live.tickets || []).slice() };
        window.WB_SERVER_PORTAL = B.live.portalRequests || [];
      }
      window.WB_DATA_SOURCE = "dev.api";
    } catch (e) {
      window.WB_DATA_SOURCE = "inline-fallback";
      if (window.console && console.warn) console.warn("[WOO-19] dev.api bootstrap unavailable — using inline seed:", e && e.message);
    }

    // Write-through: records created in one screen also POST to the dev backend.
    var _post = function (path, body) {
      try {
        var x = new XMLHttpRequest();
        x.open("POST", window.WB_API_BASE + path, true);
        x.withCredentials = true;
        x.setRequestHeader("Content-Type", "application/json");
        x.send(JSON.stringify(body || {}));
      } catch (e) {}
    };
    var _endpoint = { tenants: "/tenants", moves: "/moves", tickets: "/tickets" };
    var _origAdd = window.WBadd;
    window.WBadd = function (kind, item) {
      _origAdd(kind, item);
      if (_endpoint[kind]) _post(_endpoint[kind], item);
    };

    // Merge server-side resident portal requests into the operator's reader
    // (resident -> operator bridge now also flows through dev.api, not just
    // the same-browser localStorage channel).
    var _origPortal = window.WBreadPortalRequests;
    window.WBreadPortalRequests = function () {
      var local = _origPortal();
      var server = window.WB_SERVER_PORTAL || [];
      var seen = {}, out = [];
      local.concat(server).forEach(function (r) {
        var k = r && (r.id || JSON.stringify(r));
        if (k && seen[k]) return;
        if (k) seen[k] = 1;
        out.push(r);
      });
      return out;
    };
  })();
})();
