/*
 * cenora-bundle-5-screens-c.tsx — auto-generated. DO NOT EDIT.
 */

/* ═════ apps/web/components/tax/ph-bir.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/tax/ph-bir.tsx
 * ----------------------------------------------------------------------------
 * Tax &amp; Compliance — Philippines BIR Compliance Hub.
 *
 * Single-jurisdiction dashboard scoped to PH BIR. Covers:
 *   • BIR eFPS connection status + last sync
 *   • This-month forms (1601-C, 1601-E, 0619-E, 2550M)
 *   • VAT summary (Output VAT — Input VAT = Net VAT payable)
 *   • EWT (2307) certificates issued + received
 *   • Annual filings calendar
 * ============================================================================
 */

interface BIRMonthly {
  form: string
  description: string
  due: string
  daysUntilDue: number
  amount: number
  status: "draft" | "ready" | "submitted" | "accepted" | "overdue"
}

const BIR_MONTHLY: BIRMonthly[] = [
  { form: "BIR 1601-C", description: "Compensation WHT · employees",     due: "Apr 10", daysUntilDue: -7, amount: 84200,  status: "overdue" },
  { form: "BIR 0619-E", description: "Monthly remittance · EWT",         due: "Apr 20", daysUntilDue: 3,  amount: 12800,  status: "draft"   },
  { form: "BIR 1601-E", description: "Expanded WHT · suppliers",         due: "Apr 20", daysUntilDue: 3,  amount: 32400,  status: "ready"   },
  { form: "BIR 2550M",  description: "Monthly VAT return",                due: "Apr 20", daysUntilDue: 3,  amount: 248000, status: "draft"   },
  { form: "BIR 2307",   description: "Certificate of WHT · batch",        due: "Apr 30", daysUntilDue: 13, amount: 0,      status: "submitted" },
]

interface VatRow { label: string; amount: number; tone?: "in" | "out" | "net" }
const VAT_SUMMARY: VatRow[] = [
  { label: "Sales · standard rate (12%)",              amount: 2840000, tone: "out" },
  { label: "Sales · zero rate (exports)",              amount:  420000, tone: "out" },
  { label: "Sales · exempt",                            amount:  168000, tone: "out" },
  { label: "Output VAT · standard rate",                amount:  340800, tone: "out" },
  { label: "Purchases · standard rate",                 amount:  860000, tone: "in"  },
  { label: "Input VAT · standard rate",                 amount:  103200, tone: "in"  },
  { label: "Input VAT · attributable to zero-rated",    amount:   28000, tone: "in"  },
  { label: "Net VAT payable",                           amount:  209600, tone: "net" },
]

interface QuarterlyMilestone {
  date: string
  forms: string
  status: "done" | "due" | "upcoming"
}
const QUARTERLY: QuarterlyMilestone[] = [
  { date: "Apr 30", forms: "2307 (Q1 certs) · 1604-CF (Q1)",   status: "due"      },
  { date: "May 15", forms: "1701Q (Q1 income tax)",             status: "upcoming" },
  { date: "May 25", forms: "2550Q (Q1 VAT)",                    status: "upcoming" },
  { date: "Jul 31", forms: "2307 (Q2 certs) · 1604-E (Q2)",     status: "upcoming" },
  { date: "Aug 15", forms: "1701Q (Q2 income tax)",             status: "upcoming" },
]

function PHBir() {
  const { push } = useRouter()
  const monthlyTotal = BIR_MONTHLY.reduce((s, m) => s + m.amount, 0)
  const overdueCount = BIR_MONTHLY.filter(m => m.status === "overdue").length
  const netVAT       = VAT_SUMMARY.find(r => r.tone === "net")!.amount

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Tax & Compliance" }, { label: "PH BIR" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export schedules</Button>
            <Button variant="ghost" leadingIcon="external">Open BIR eFPS</Button>
            <Button variant="primary" leadingIcon="upload">Submit Apr bundle</Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          {/* Hero — eFPS connection + this-month total */}
          <Card>
            <div className="p-5 bg-gradient-to-br from-brand to-brand-light text-white relative overflow-hidden">
              <div className="flex items-start justify-between gap-3 mb-3">
                <div>
                  <div className="text-[10px] font-bold uppercase tracking-wider text-accent">PH BIR · April 2026</div>
                  <div className="font-serif text-[40px] leading-none mt-1.5">₱{monthlyTotal.toLocaleString()}</div>
                  <div className="text-[11px] text-white/70 mt-1.5">total payable across {BIR_MONTHLY.length} forms · HVPH</div>
                </div>
                <div className="inline-flex items-center gap-1.5 bg-white/10 rounded-full px-2.5 py-1 shrink-0">
                  <span className="size-1.5 rounded-full bg-success animate-pulse" />
                  <span className="text-[10px] font-bold uppercase tracking-wider text-accent">eFPS connected · synced 12 min ago</span>
                </div>
              </div>
              <div className="mt-5 grid grid-cols-3 gap-6">
                <HeroStat2 label="Overdue"    value={overdueCount}            sub={overdueCount > 0 ? "penalties accruing" : "All clear"} tone={overdueCount > 0 ? "danger" : "success"} />
                <HeroStat2 label="Net VAT"    value={`₱${netVAT.toLocaleString()}`} sub="2550M payable" tone="accent" />
                <HeroStat2 label="2307 sent · YTD" value="142"                sub="to 22 PH suppliers" tone="success" />
              </div>
            </div>
          </Card>

          {/* AI brief */}
          <AICallout
            title="1601-C is 7 days late · file today to cap penalty"
            body="Compensation WHT was due Apr 10 (₱84,200). At 7d late the BIR penalty is 25% surcharge + 12% annualised interest + ₱1k compromise — about ₱22,800 if filed today. Each additional week adds ~₱2,500. Want to draft the late filing now and route to Aldo K. for sign-off?"
            matches={[
              { code: "1601-C", name: "₱84,200 due · 7d late · ₱22.8k penalty", pct: 100 },
            ]}
            dismissLabel="Defer"
            proceedLabel="Draft late filing"
          />

          <div className="grid grid-cols-[1.4fr_1fr] gap-4">
            {/* Monthly forms */}
            <Card>
              <CardHeader>
                <CardTitle>This-month forms · HVPH</CardTitle>
                <Button variant="ghost" size="sm" trailingIcon="arrow-right" onClick={() => push("/tax/filings")}>All filings</Button>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {BIR_MONTHLY.map(m => (
                  <button key={m.form} onClick={() => push("/tax/filings")} className="w-full text-left flex items-center gap-3 px-4 py-3 hover:bg-cream/40 transition-colors">
                    <span className={cn(
                      "size-9 rounded-md flex items-center justify-center font-mono font-bold text-[10px] shrink-0",
                      m.status === "overdue"  ? "bg-danger text-white" :
                      m.status === "draft"    ? "bg-cream-deep text-ink-soft" :
                      m.status === "ready"    ? "bg-info-soft text-info" :
                      m.status === "submitted"? "bg-accent-soft text-accent-deep" :
                      "bg-success-soft text-success"
                    )}>
                      {m.form.split(" ")[1].slice(0, 4)}
                    </span>
                    <div className="flex-1 min-w-0">
                      <div className="font-semibold text-ink text-[12.5px]">{m.form}</div>
                      <div className="text-[10.5px] text-ink-mute">{m.description}</div>
                    </div>
                    <div className="text-right">
                      <div className="font-mono font-bold text-ink">{m.amount > 0 ? `₱${m.amount.toLocaleString()}` : "—"}</div>
                      <div className={cn("text-[10px] font-semibold mt-0.5",
                        m.status === "overdue" ? "text-danger" :
                        m.status === "draft" || m.status === "ready" ? "text-warn" :
                        "text-success"
                      )}>{m.status === "overdue" ? `${Math.abs(m.daysUntilDue)}d late` : m.daysUntilDue === 0 ? "due today" : `${m.daysUntilDue}d`}</div>
                    </div>
                    <Badge variant={m.status === "overdue" ? "danger" : m.status === "draft" ? "neutral" : m.status === "ready" ? "info" : m.status === "submitted" ? "accent" : "success"} dot>{m.status}</Badge>
                  </button>
                ))}
              </div>
            </Card>

            {/* VAT summary */}
            <Card>
              <CardHeader>
                <CardTitle>VAT 2550M · Apr 2026</CardTitle>
                <Badge variant="warn" dot>Draft</Badge>
              </CardHeader>
              <div className="divide-y divide-divider-soft text-[12px]">
                {VAT_SUMMARY.map(r => (
                  <div key={r.label} className={cn("px-4 py-2 flex items-center justify-between", r.tone === "net" && "bg-warn-soft/60 font-bold")}>
                    <span className={cn(r.tone === "net" ? "text-warn" : "text-ink-soft")}>{r.label}</span>
                    <span className={cn("font-mono", r.tone === "net" ? "text-warn" : r.tone === "in" ? "text-info" : "text-ink")}>₱{r.amount.toLocaleString()}</span>
                  </div>
                ))}
              </div>
              <div className="px-4 py-3 bg-cream/60 border-t border-divider text-[10.5px] text-ink-mute">
                Auto-derived from <span className="font-mono">2550M-Apr-2026.draft</span>. Schedules link to AR, AP, journal entries.
              </div>
            </Card>
          </div>

          {/* Quarterly / Annual + 2307 stats */}
          <div className="grid grid-cols-[1.4fr_1fr] gap-4">
            <Card>
              <CardHeader>
                <CardTitle>Quarterly &amp; annual calendar · HVPH</CardTitle>
                <span className="text-[10.5px] text-ink-mute">2026</span>
              </CardHeader>
              <div className="p-4">
                <div className="relative">
                  <div className="absolute left-3 top-2 bottom-2 w-px bg-divider" />
                  <div className="space-y-3">
                    {QUARTERLY.map(q => (
                      <div key={q.date} className="flex items-start gap-3 relative">
                        <span className={cn("relative z-10 size-6 rounded-full flex items-center justify-center text-[10px] font-bold shrink-0",
                          q.status === "done" ? "bg-success text-white" :
                          q.status === "due"  ? "bg-warn text-white animate-pulse" :
                          "bg-cream-deep text-ink-mute"
                        )}>{q.status === "done" ? <Icon name="check" size={10} strokeWidth={3} /> : "·"}</span>
                        <div className="flex-1 min-w-0">
                          <div className="text-[11px] font-bold text-ink">{q.date}</div>
                          <div className="text-[11.5px] text-ink-soft">{q.forms}</div>
                        </div>
                        <Badge variant={q.status === "done" ? "success" : q.status === "due" ? "warn" : "neutral"} dot>
                          {q.status === "due" ? "Due" : q.status === "done" ? "Filed" : "Upcoming"}
                        </Badge>
                      </div>
                    ))}
                  </div>
                </div>
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>2307 certificates · YTD</CardTitle>
              </CardHeader>
              <div className="p-4 grid grid-cols-2 gap-3">
                <div className="bg-cream/60 border border-divider rounded-md p-3 text-center">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Issued</div>
                  <div className="font-serif text-[28px] text-ink mt-0.5">142</div>
                  <div className="text-[10px] text-ink-mute">to suppliers</div>
                </div>
                <div className="bg-cream/60 border border-divider rounded-md p-3 text-center">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Received</div>
                  <div className="font-serif text-[28px] text-ink mt-0.5">38</div>
                  <div className="text-[10px] text-ink-mute">from customers</div>
                </div>
                <div className="col-span-2 bg-warn-soft/40 border border-warn/30 rounded-md p-3">
                  <div className="text-[9.5px] uppercase tracking-wider font-bold text-warn">Reconciliation</div>
                  <div className="text-[11.5px] text-ink-soft mt-0.5 leading-snug">
                    8 expected 2307s missing from Cebu Pacific and HK Logistics; ₱11.4k of input WHT pending substantiation for 1701Q.
                  </div>
                  <Button variant="ghost" size="sm" trailingIcon="arrow-right" className="mt-2">View missing 8</Button>
                </div>
              </div>
            </Card>
          </div>

          {/* Footer · entity quick switch */}
          <div className="bg-cream/30 border border-divider rounded-lg p-4 flex items-center gap-3">
            <Icon name="info" size={14} className="text-ink-mute" />
            <span className="text-[11.5px] text-ink-soft">Showing <strong className="text-ink">HVPH</strong> only. Calamba (HVPH-CAL) is a branch — its forms roll up into this entity. Switch to HVSG or HVHK in the topbar to see foreign-jurisdiction filings.</span>
          </div>

        </div>
      </div>
    </>
  )
}

function HeroStat2({ label, value, sub, tone }: any) {
  const tones: Record<string, string> = { success: "text-success", danger: "text-accent", accent: "text-accent" }
  return (
    <div>
      <div className="text-[10px] font-bold uppercase tracking-wider text-accent">{label}</div>
      <div className={cn("font-serif text-[24px] leading-none mt-1", tones[tone] ?? "text-white")}>{value}</div>
      <div className="text-[10.5px] text-white/65 mt-1">{sub}</div>
    </div>
  )
}

// SANDBOX
;(globalThis as any).PHBir = PHBir

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/field/dispatch.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/field/dispatch.tsx
 * ----------------------------------------------------------------------------
 * Field Service hero — Dispatch Board. Two-panel layout:
 *   • Left: stylized map showing crew/jobs across the service region
 *   • Right: live queue (active → assigned → unassigned), crew bench at bottom
 *
 * The map is a hand-drawn SVG of the Metro Manila + Calabarzon region with
 * pins at the lat/lng grid coords from FIELD_JOBS — good-enough for a demo
 * without pulling Mapbox/Google in as a dependency.
 * ============================================================================
 */

function DispatchBoard() {
  const { push } = useRouter()
  const [selected, setSelected] = React.useState<FieldJob | null>(null)

  const activeJobs   = FIELD_JOBS.filter(j => j.status === "on-site" || j.status === "en-route")
  const assignedJobs = FIELD_JOBS.filter(j => j.status === "assigned")
  const queuedJobs   = FIELD_JOBS.filter(j => j.status === "queued")
  const completedJobs = FIELD_JOBS.filter(j => j.status === "completed")
  const slaBreached = FIELD_JOBS.filter(j => j.sla === "breached" && j.status !== "completed").length
  const available = FIELD_CREW.filter(c => c.status === "available").length

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Field Service" }, { label: "Dispatch Board" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="filter">Today</Button>
            <Button variant="ghost" leadingIcon="grid">Schedule view</Button>
            <Button variant="primary" leadingIcon="plus">New Job</Button>
          </>
        }
      />

      <div className="flex-1 overflow-hidden bg-app">
        <div className="max-w-[1600px] mx-auto h-full flex flex-col px-6 pt-4 pb-2 gap-4">

          {/* KPI strip */}
          <div className="grid grid-cols-5 gap-3 shrink-0">
            <KpiTile label="Active now" value={activeJobs.length.toString()} sub={`${FIELD_CREW.filter(c => c.status === "on-job").length} crew dispatched`} accent="brand" />
            <KpiTile label="Scheduled" value={assignedJobs.length.toString()} sub="Awaiting dispatch" accent="info" />
            <KpiTile label="Unassigned" value={queuedJobs.length.toString()} sub={queuedJobs.length > 0 ? "Needs attention" : "Queue clear"} accent={queuedJobs.length > 0 ? "warn" : "success"} />
            <KpiTile label="SLA breached" value={slaBreached.toString()} sub={slaBreached > 0 ? "Penalty risk" : "All on-track"} accent={slaBreached > 0 ? "danger" : "success"} />
            <KpiTile label="Crew available" value={available.toString()} sub="Bench" accent="accent" />
          </div>

          {/* Map + queue */}
          <div className="grid grid-cols-[3fr_2fr] gap-4 flex-1 min-h-0">

            {/* Map panel */}
            <div className="bg-surface border border-divider rounded-lg overflow-hidden flex flex-col">
              <header className="px-4 py-2.5 border-b border-divider bg-cream/50 flex items-center justify-between">
                <div className="flex items-center gap-2">
                  <Icon name="globe" size={13} className="text-ink-mute" />
                  <div className="text-[11px] font-bold uppercase tracking-wider text-ink-mute">Service Region · NCR + Calabarzon · Apr 18</div>
                </div>
                <div className="flex items-center gap-2 text-[10px]">
                  <span className="inline-flex items-center gap-1"><span className="size-2 rounded-full bg-brand-mid" /> On-site</span>
                  <span className="inline-flex items-center gap-1"><span className="size-2 rounded-full bg-accent" /> En route</span>
                  <span className="inline-flex items-center gap-1"><span className="size-2 rounded-full bg-info" /> Scheduled</span>
                  <span className="inline-flex items-center gap-1"><span className="size-2 rounded-full bg-warn" /> Queued</span>
                </div>
              </header>
              <div className="flex-1 relative p-2 bg-cream/40">
                <RegionMap jobs={FIELD_JOBS.filter(j => j.status !== "completed")} onSelect={setSelected} selected={selected} />
              </div>
            </div>

            {/* Queue panel */}
            <div className="bg-surface border border-divider rounded-lg overflow-hidden flex flex-col min-h-0">
              <header className="px-4 py-2.5 border-b border-divider bg-cream/50 flex items-center justify-between shrink-0">
                <div className="text-[11px] font-bold uppercase tracking-wider text-ink-mute">Job Queue</div>
                <span className="text-[10px] text-ink-mute">{FIELD_JOBS.length - completedJobs.length} active · {completedJobs.length} done today</span>
              </header>

              <div className="flex-1 overflow-y-auto divide-y divide-divider-soft min-h-0">
                {/* Active */}
                <Section title="Active" tone="brand" jobs={activeJobs} onSelect={setSelected} selected={selected} />
                <Section title="Assigned" tone="info" jobs={assignedJobs} onSelect={setSelected} selected={selected} />
                <Section title="Unassigned" tone="warn" jobs={queuedJobs} onSelect={setSelected} selected={selected} />
                <Section title="Completed today" tone="success" jobs={completedJobs} onSelect={setSelected} selected={selected} compact />
              </div>

              {/* Crew bench */}
              <footer className="border-t border-divider bg-cream/40 px-3 py-2.5 shrink-0">
                <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute mb-1.5">Crew bench · available now</div>
                <div className="flex items-center gap-1.5 flex-wrap">
                  {FIELD_CREW.filter(c => c.status === "available").map(c => (
                    <button key={c.initials} className="inline-flex items-center gap-1.5 bg-surface border border-divider hover:border-brand-mid rounded-full pl-0.5 pr-2 py-0.5 transition-colors">
                      <Avatar initials={c.initials} tone={c.tone as any} size="xs" />
                      <span className="text-[10.5px] font-semibold text-ink">{c.name.split(" ")[0]}</span>
                      <span className="text-[9px] text-ink-mute">· {c.skills[0]}</span>
                    </button>
                  ))}
                </div>
              </footer>
            </div>
          </div>
        </div>
      </div>
    </>
  )
}

/* ─── Map ──────────────────────────────────────────────────────────────── */
function RegionMap({ jobs, onSelect, selected }: { jobs: FieldJob[]; onSelect: (j: FieldJob) => void; selected: FieldJob | null }) {
  /* The "map" is a stylized abstract: irregular polygons in cream tones for
   * provinces, faint road lines, and pins for each job. */
  return (
    <svg viewBox="0 0 100 100" className="w-full h-full">
      {/* Province polygons (Metro Manila, Cavite, Laguna, Batangas, Cebu) */}
      <path d="M 30 40 L 55 38 L 60 50 L 50 58 L 35 55 Z" fill="#F2EDE5" stroke="#E2DDD6" strokeWidth="0.2" />
      <path d="M 20 50 L 35 55 L 30 70 L 18 68 Z"          fill="#EFEAE3" stroke="#E2DDD6" strokeWidth="0.2" />
      <path d="M 50 58 L 75 50 L 80 70 L 60 75 L 35 70 L 30 70 Z" fill="#F2EDE5" stroke="#E2DDD6" strokeWidth="0.2" />
      <path d="M 75 50 L 95 40 L 100 65 L 80 70 Z"         fill="#EFEAE3" stroke="#E2DDD6" strokeWidth="0.2" />
      <path d="M 82 70 L 96 70 L 92 95 L 80 92 Z"          fill="#F2EDE5" stroke="#E2DDD6" strokeWidth="0.2" />
      {/* Subtle roads */}
      <path d="M 35 50 L 55 45 L 70 55 L 85 50" fill="none" stroke="#D9D3C9" strokeWidth="0.3" strokeDasharray="0.6 0.4" />
      <path d="M 40 55 L 55 65 L 65 60 L 80 72" fill="none" stroke="#D9D3C9" strokeWidth="0.3" strokeDasharray="0.6 0.4" />
      <path d="M 50 38 L 50 75"                  fill="none" stroke="#D9D3C9" strokeWidth="0.3" strokeDasharray="0.6 0.4" />
      {/* Province labels */}
      <text x="44" y="46" fontSize="2" fill="#B5B0A8" fontFamily="Arial, sans-serif" fontWeight="bold" letterSpacing="0.3">NCR</text>
      <text x="20" y="62" fontSize="1.6" fill="#B5B0A8" fontFamily="Arial, sans-serif">CAVITE</text>
      <text x="58" y="70" fontSize="1.6" fill="#B5B0A8" fontFamily="Arial, sans-serif">LAGUNA</text>
      <text x="85" y="64" fontSize="1.6" fill="#B5B0A8" fontFamily="Arial, sans-serif">BATANGAS</text>
      <text x="86" y="86" fontSize="1.4" fill="#B5B0A8" fontFamily="Arial, sans-serif">CEBU</text>

      {/* Connector lines from crew → on-site jobs (subtle) */}
      {jobs.filter(j => j.status === "en-route").map(j => (
        <line key={`route-${j.id}`}
          x1={48} y1={52}  /* approx HQ position */
          x2={j.lng} y2={100 - j.lat}
          stroke="#D4A96A" strokeWidth="0.35" strokeDasharray="0.5 0.4" opacity="0.7" />
      ))}

      {/* Pins */}
      {jobs.map(j => {
        const isSelected = selected?.id === j.id
        const color =
          j.status === "on-site"  ? "#0D4A4A" :
          j.status === "en-route" ? "#D4A96A" :
          j.status === "assigned" ? "#2563EB" :
          j.status === "queued"   ? "#C88A2E" :
                                    "#8A8A8A"
        return (
          <g key={j.id} style={{ cursor: "pointer" }} onClick={() => onSelect(j)}>
            {/* Halo for active jobs */}
            {(j.status === "on-site" || j.sla === "breached") && (
              <circle cx={j.lng} cy={100 - j.lat} r={isSelected ? 5 : 4} fill={color} opacity="0.18">
                <animate attributeName="r" values="3;5;3" dur="2s" repeatCount="indefinite" />
                <animate attributeName="opacity" values="0.35;0.05;0.35" dur="2s" repeatCount="indefinite" />
              </circle>
            )}
            <circle cx={j.lng} cy={100 - j.lat} r={isSelected ? 2.4 : 1.8} fill={color} stroke="white" strokeWidth="0.5" />
            {isSelected && (
              <g>
                <rect x={j.lng + 2.5} y={100 - j.lat - 5} width="38" height="9" rx="1.2" fill="#1A1A1A" opacity="0.92" />
                <text x={j.lng + 4} y={100 - j.lat - 1.5} fontSize="1.7" fill="white" fontFamily="Arial, sans-serif" fontWeight="bold">{j.customer.name}</text>
                <text x={j.lng + 4} y={100 - j.lat + 1.2} fontSize="1.4" fill="#D4A96A" fontFamily="Arial, sans-serif">{j.scheduledStart} · {j.serviceType}</text>
              </g>
            )}
          </g>
        )
      })}
    </svg>
  )
}

/* ─── Queue section ──────────────────────────────────────────────────── */
function Section({ title, tone, jobs, onSelect, selected, compact }: {
  title: string; tone: string; jobs: FieldJob[]; onSelect: (j: FieldJob) => void; selected: FieldJob | null; compact?: boolean
}) {
  if (jobs.length === 0) return null
  return (
    <div>
      <div className="px-4 py-2 sticky top-0 bg-cream/90 backdrop-blur-sm border-b border-divider-soft flex items-baseline justify-between">
        <Badge variant={tone as any} dot>{title}</Badge>
        <span className="text-[10px] text-ink-mute">{jobs.length}</span>
      </div>
      <div className="divide-y divide-divider-soft">
        {jobs.map(j => <JobItem key={j.id} j={j} onSelect={onSelect} selected={selected} compact={compact} />)}
      </div>
    </div>
  )
}

function JobItem({ j, onSelect, selected, compact }: { j: FieldJob; onSelect: (j: FieldJob) => void; selected: FieldJob | null; compact?: boolean }) {
  const isSelected = selected?.id === j.id
  return (
    <button
      onClick={() => onSelect(j)}
      className={cn(
        "w-full px-4 py-2.5 flex items-center gap-3 text-left hover:bg-cream/60 transition-colors",
        isSelected && "bg-brand-soft/50",
        j.sla === "breached" && !compact && "bg-danger-soft/30"
      )}>
      <Avatar initials={j.customer.initials} tone={j.customer.tone as any} size="sm" />
      <div className="flex-1 min-w-0">
        <div className="flex items-baseline gap-1.5">
          <Badge variant={
            j.priority === "P1" ? "danger" : j.priority === "P2" ? "warn" : "neutral"
          }>{j.priority}</Badge>
          <span className="text-[12px] font-semibold text-ink truncate">{j.title}</span>
        </div>
        <div className="text-[10.5px] text-ink-mute mt-0.5 truncate">
          {j.customer.name} · {j.address}{j.scheduledStart !== "—" && ` · ${j.scheduledStart}`}
        </div>
      </div>
      {j.crew && j.crew.length > 0 && (
        <AvatarStack people={j.crew.map(c => ({ initials: c.initials, tone: c.tone }))} max={2} size="xs" />
      )}
      {j.sla === "breached" && (
        <Icon name="alert-triangle" size={12} className="text-danger shrink-0" />
      )}
    </button>
  )
}

// SANDBOX
;(globalThis as any).DispatchBoard = DispatchBoard

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/field/crew.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/field/crew.tsx
 * ----------------------------------------------------------------------------
 * Field Service — Crew Mobile (technician's mobile app preview).
 *
 * Layout: phone bezel showing the technician's job inbox + active job view,
 * with a dispatcher panel beside it showing the technician's day, location,
 * and current parts allocation.
 * ============================================================================
 */

function CrewMobile() {
  const [active, setActive] = React.useState<any>(() => FIELD_JOBS.find(j => j.crew?.some((c: any) => c.initials === "JD")) ?? FIELD_JOBS[0])
  const tech = { name: "Joel D.", initials: "JD", tone: "amber", role: "Sr. Field Technician", skills: ["Electrical", "POS"], device: "iPhone 15 · Cenora Field 2.4.1" }
  const myJobs = FIELD_JOBS.filter(j => j.crew?.some((c: any) => c.initials === "JD"))

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Field Service" }, { label: "Crew Mobile" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="message">Page Joel</Button>
            <Button variant="ghost" leadingIcon="map">Live track</Button>
            <Button variant="primary" leadingIcon="external">Open dispatch</Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 grid grid-cols-[360px_1fr] gap-6">

          {/* Phone preview */}
          <div className="flex flex-col items-center gap-3">
            <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">{tech.device}</div>
            <FieldPhoneBezel>
              <FieldMobileApp tech={tech} jobs={myJobs} active={active} setActive={setActive} />
            </FieldPhoneBezel>
            <div className="text-[10.5px] text-ink-mute text-center max-w-[300px] leading-relaxed">
              <strong className="text-ink">{tech.name}</strong> is on-site at <strong>{active.customer.name}</strong> · Makati CBD. SOS button, offline cache + GPS auto-clock-in are always on.
            </div>
          </div>

          {/* Dispatcher panel */}
          <div className="space-y-4">
            <AICallout
              title="Joel finishes at 10:30 — auto-route him to FJ-0418-05?"
              body="Pacific Solar is 8 minutes away vs Globe Telecom's BGC site (24 min). The Solar inverter swap is queued and SLA at-risk; shifting it earlier closes the queue and saves the 18-min drive. Want to confirm the route?"
              dismissLabel="Leave manual"
              proceedLabel="Auto-route"
            />

            <div className="grid grid-cols-3 gap-3">
              <KpiTile label="Jobs · today"    value={`${myJobs.filter(j => j.status === "completed").length} / ${myJobs.length}`} sub="2 completed, 1 active" accent="brand" />
              <KpiTile label="Hours clocked"   value="6h 12m"                                                                       sub="GPS auto-clock"        accent="success" />
              <KpiTile label="On-time rate · MTD" value="96%"                                                                       sub="22 of 23 jobs"         accent="accent"  />
            </div>

            <Card>
              <CardHeader>
                <CardTitle>Joel's day · Apr 18</CardTitle>
                <Badge variant="warn" dot>On site · FJ-0418-02</Badge>
              </CardHeader>
              <div className="p-4">
                <div className="space-y-3">
                  {myJobs.map(j => (
                    <DayRow key={j.id} job={j} active={j.id === active.id} onClick={() => setActive(j)} />
                  ))}
                </div>
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>Active job · {active.id}</CardTitle>
                <Badge variant={(FJ_STATUS_TONE[active.status as keyof typeof FJ_STATUS_TONE]) as any} dot>{FJ_STATUS_LABEL[active.status as keyof typeof FJ_STATUS_LABEL]}</Badge>
              </CardHeader>
              <div className="p-4 grid grid-cols-2 gap-3 text-[12px]">
                <Detail label="Customer"  value={active.customer.name} />
                <Detail label="Address"   value={active.address} />
                <Detail label="Service"   value={active.serviceType} />
                <Detail label="Priority"  value={<Badge variant={active.priority === "P1" ? "danger" : active.priority === "P2" ? "warn" : "neutral"}>{active.priority}</Badge>} />
                <Detail label="Scheduled" value={`${active.scheduledStart} – ${active.scheduledEnd}`} />
                <Detail label="SLA"       value={<Badge variant={active.sla === "on-track" ? "success" : active.sla === "at-risk" ? "warn" : "danger"} dot>{active.sla}</Badge>} />
                {active.partsRequired && (
                  <Detail label="Parts required" value={<div className="text-[11px] text-ink-soft">{active.partsRequired.join(" · ")}</div>} full />
                )}
                {active.notes && (
                  <Detail label="Notes" value={<div className="text-[11px] text-ink-soft">{active.notes}</div>} full />
                )}
              </div>
            </Card>
          </div>
        </div>
      </div>
    </>
  )
}

function DayRow({ job, active, onClick }: any) {
  return (
    <button onClick={onClick}
      className={cn(
        "w-full text-left grid grid-cols-[60px_1fr_80px] gap-3 items-center p-2 rounded-md border transition-colors",
        active ? "border-brand bg-brand-soft/50" : "border-divider bg-surface hover:border-divider-strong"
      )}>
      <div className="text-center">
        <div className="text-[10px] font-mono text-ink-mute">{job.scheduledStart}</div>
        <div className="text-[10px] font-mono text-ink-faint">{job.scheduledEnd}</div>
      </div>
      <div className="min-w-0">
        <div className="text-[12px] font-semibold text-ink truncate">{job.title}</div>
        <div className="text-[10px] text-ink-mute truncate">{job.customer.name} · {job.address}</div>
      </div>
      <div className="text-right">
        <Badge variant={FJ_STATUS_TONE[job.status as keyof typeof FJ_STATUS_TONE] as any} dot>{FJ_STATUS_LABEL[job.status as keyof typeof FJ_STATUS_LABEL]}</Badge>
        <div className="text-[10px] text-ink-mute mt-1">{job.durationMin}m</div>
      </div>
    </button>
  )
}
function Detail({ label, value, full }: any) {
  return (
    <div className={cn(full && "col-span-2")}>
      <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute mb-0.5">{label}</div>
      <div className="text-[12px] text-ink font-medium">{value}</div>
    </div>
  )
}

/* ─── Field-tech phone bezel ───────────────────────────────────────────── */
function FieldPhoneBezel({ children }: { children: React.ReactNode }) {
  return (
    <div className="relative w-[320px] h-[660px] bg-ink rounded-[44px] p-2.5 shadow-lg">
      <div className="absolute top-3 left-1/2 -translate-x-1/2 z-20 w-24 h-5 bg-ink rounded-full" />
      <div className="relative w-full h-full bg-surface rounded-[34px] overflow-hidden flex flex-col">
        <div className="h-9 shrink-0 flex items-center justify-between px-6 pt-2 text-[11px] font-bold text-ink">
          <span>10:42</span>
          <span className="flex items-center gap-1">
            <Icon name="globe" size={11} />
            <span className="size-3 inline-block rounded-sm border-[1.5px] border-ink relative">
              <span className="absolute inset-0.5 bg-ink rounded-sm" />
            </span>
          </span>
        </div>
        <div className="flex-1 overflow-hidden min-h-0">{children}</div>
      </div>
    </div>
  )
}

function FieldMobileApp({ tech, jobs, active, setActive }: any) {
  return (
    <div className="flex flex-col h-full bg-app">
      {/* Header */}
      <header className="shrink-0 bg-brand text-white px-4 py-3">
        <div className="flex items-center gap-2.5">
          <div className="size-9 rounded-md bg-accent text-brand flex items-center justify-center font-serif text-base font-bold">
            {tech.initials}
          </div>
          <div className="flex-1 min-w-0">
            <div className="text-[10px] text-white/65">Cenora Field · Apr 18</div>
            <div className="text-[13px] font-semibold truncate">{tech.name}</div>
          </div>
          <button className="size-8 rounded-md bg-white/10 flex items-center justify-center">
            <Icon name="bell" size={13} />
          </button>
        </div>
        <div className="mt-3 flex items-center gap-2 text-[10px]">
          <span className="bg-success/30 text-white rounded-full px-2 py-px font-semibold flex items-center gap-1">
            <span className="size-1.5 rounded-full bg-success" />Clocked in
          </span>
          <span className="text-white/60">6h 12m today</span>
          <span className="ml-auto text-accent font-bold">{jobs.filter((j: any) => j.status !== "completed").length} open</span>
        </div>
      </header>

      {/* Active job */}
      <div className="shrink-0 p-3 bg-warn-soft border-b border-warn/40">
        <div className="flex items-center gap-2 mb-1.5">
          <div className="text-[9px] font-bold uppercase tracking-wider text-warn">On site · {active.scheduledStart}</div>
          <Badge variant="danger">{active.priority}</Badge>
        </div>
        <div className="text-[13px] font-semibold text-ink leading-snug">{active.title}</div>
        <div className="text-[11px] text-ink-mute mt-0.5">{active.customer.name} · {active.address}</div>
        <div className="grid grid-cols-3 gap-2 mt-3">
          <button className="h-9 rounded-md bg-brand text-white text-[11px] font-bold flex items-center justify-center gap-1.5">
            <Icon name="check" size={11} strokeWidth={3} /> Complete
          </button>
          <button className="h-9 rounded-md bg-surface border border-divider text-[11px] font-semibold text-ink-soft flex items-center justify-center gap-1.5">
            <Icon name="alert-triangle" size={11} /> Issue
          </button>
          <button className="h-9 rounded-md bg-surface border border-divider text-[11px] font-semibold text-ink-soft flex items-center justify-center gap-1.5">
            <Icon name="image" size={11} /> Photo
          </button>
        </div>
      </div>

      {/* Job list */}
      <div className="flex-1 overflow-y-auto">
        <div className="px-4 py-2 text-[9px] font-bold uppercase tracking-wider text-ink-mute">Today's queue</div>
        {jobs.map((j: any) => {
          const isActive = j.id === active.id
          return (
            <button key={j.id} onClick={() => setActive(j)}
              className={cn(
                "w-full text-left px-4 py-2.5 border-t border-divider-soft flex items-center gap-2 hover:bg-cream",
                isActive && "bg-brand-soft"
              )}>
              <span className={cn(
                "size-7 rounded-md flex items-center justify-center text-[9px] font-bold shrink-0",
                j.status === "completed" ? "bg-success text-white" :
                j.status === "on-site"   ? "bg-warn text-white" :
                j.status === "en-route"  ? "bg-info text-white" :
                "bg-cream-deep text-ink-mute",
              )}>
                {j.status === "completed" && <Icon name="check" size={11} strokeWidth={3} />}
                {j.status === "on-site"   && <Icon name="check-square" size={11} />}
                {j.status === "en-route"  && <Icon name="arrow-right" size={11} />}
                {j.status === "assigned"  && j.scheduledStart.slice(0, 2)}
              </span>
              <div className="flex-1 min-w-0">
                <div className="text-[11px] font-semibold text-ink truncate">{j.title}</div>
                <div className="text-[9.5px] text-ink-mute">{j.scheduledStart} · {j.customer.name}</div>
              </div>
              <Badge variant={j.priority === "P1" ? "danger" : j.priority === "P2" ? "warn" : "neutral"}>{j.priority}</Badge>
            </button>
          )
        })}
      </div>

      {/* Tab bar */}
      <nav className="shrink-0 bg-surface border-t border-divider grid grid-cols-4 text-[9px] text-ink-mute py-2">
        <button className="flex flex-col items-center gap-1 text-brand"><Icon name="list" size={16} /><span className="font-bold">Jobs</span></button>
        <button className="flex flex-col items-center gap-1"><Icon name="globe" size={16} /><span>Map</span></button>
        <button className="flex flex-col items-center gap-1"><Icon name="package" size={16} /><span>Parts</span></button>
        <button className="flex flex-col items-center gap-1"><Icon name="user" size={16} /><span>Me</span></button>
      </nav>
    </div>
  )
}

// SANDBOX
;(globalThis as any).CrewMobile = CrewMobile

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/field/sla.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/field/sla.tsx
 * ----------------------------------------------------------------------------
 * Field Service — SLA Tracking. Per-tier SLA compliance, breach watchlist,
 * per-customer scoreboard, recent incidents.
 * ============================================================================
 */

interface SLATier {
  tier: "P1" | "P2" | "P3"
  responseMin: number          /* commit */
  resolveHours: number
  jobsThisMonth: number
  breached: number
  onTrack: number
  atRisk: number
  rolling30Compliance: number  /* % */
}

const SLA_TIERS: SLATier[] = [
  { tier: "P1", responseMin: 30,  resolveHours: 4,  jobsThisMonth: 18,  breached: 1, onTrack: 14, atRisk: 3, rolling30Compliance: 94.0 },
  { tier: "P2", responseMin: 120, resolveHours: 8,  jobsThisMonth: 64,  breached: 2, onTrack: 56, atRisk: 6, rolling30Compliance: 96.2 },
  { tier: "P3", responseMin: 480, resolveHours: 48, jobsThisMonth: 142, breached: 4, onTrack: 128,atRisk: 10,rolling30Compliance: 97.1 },
]

interface SLAIncident {
  id: string
  jobId: string
  customer: { name: string; initials: string; tone: any }
  tier: "P1" | "P2" | "P3"
  type: "response" | "resolution"
  raisedAt: string
  breachedBy: string            /* e.g. "12min" or "3h 20m" */
  status: "open" | "mitigating" | "resolved"
  rootCause?: string
  owner: { name: string; initials: string; tone: any }
}

const SLA_INCIDENTS: SLAIncident[] = [
  { id: "SLA-INC-2026-0418-01", jobId: "FJ-2026-0418-08", customer: { name: "Prime Hotels Group",   initials: "PH", tone: "amber" }, tier: "P1", type: "response",   raisedAt: "Apr 18 · 09:31", breachedBy: "12 min",  status: "mitigating", rootCause: "All crew on active P2 jobs; no overlap match", owner: { name: "Olivia P.", initials: "OP", tone: "amber" } },
  { id: "SLA-INC-2026-0417-04", jobId: "FJ-2026-0417-22", customer: { name: "Globe Telecom",        initials: "GT", tone: "amber" }, tier: "P2", type: "resolution", raisedAt: "Apr 17 · 16:48", breachedBy: "1h 14m",  status: "resolved",   rootCause: "Wrong part dispatched — required POS-T4 not POS-T3",   owner: { name: "Joel D.", initials: "JD", tone: "amber" } },
  { id: "SLA-INC-2026-0417-02", jobId: "FJ-2026-0417-14", customer: { name: "Cebu Pacific",         initials: "CP", tone: "amber" }, tier: "P3", type: "resolution", raisedAt: "Apr 17 · 09:12", breachedBy: "6 h",      status: "resolved",   rootCause: "Customer rescheduled twice; auto-extension policy unclear",                   owner: { name: "Olivia P.", initials: "OP", tone: "amber" } },
  { id: "SLA-INC-2026-0416-01", jobId: "FJ-2026-0416-08", customer: { name: "Metro Retail Chain",   initials: "MR", tone: "ink"   }, tier: "P2", type: "response",   raisedAt: "Apr 16 · 14:22", breachedBy: "8 min",   status: "resolved",   rootCause: "Traffic in BGC during peak rains",                          owner: { name: "Pedro S.", initials: "PS", tone: "blue" } },
]

interface CustomerScore {
  name: string
  initials: string
  tone: any
  jobsThisMonth: number
  breaches: number
  compliancePct: number
  credits: number              /* SLA credits payable, USD */
  trend: "up" | "down" | "flat"
}
const CUSTOMER_SCORES: CustomerScore[] = [
  { name: "Globe Telecom",          initials: "GT", tone: "amber", jobsThisMonth: 28, breaches: 1, compliancePct: 96.4, credits: 0,    trend: "up"   },
  { name: "PH National Bank",       initials: "PNB", tone: "blue", jobsThisMonth: 24, breaches: 0, compliancePct: 100,  credits: 0,    trend: "flat" },
  { name: "Metro Retail Chain",     initials: "MR", tone: "ink",   jobsThisMonth: 22, breaches: 1, compliancePct: 95.5, credits: 240,  trend: "down" },
  { name: "Prime Hotels Group",     initials: "PH", tone: "amber", jobsThisMonth: 12, breaches: 1, compliancePct: 91.7, credits: 480,  trend: "down" },
  { name: "Pacific Solar PH",       initials: "PS", tone: "blue",  jobsThisMonth: 18, breaches: 0, compliancePct: 100,  credits: 0,    trend: "up"   },
  { name: "Cebu Pacific",           initials: "CP", tone: "amber", jobsThisMonth: 14, breaches: 1, compliancePct: 92.9, credits: 320,  trend: "flat" },
  { name: "TechNet Solutions",      initials: "TN", tone: "teal",  jobsThisMonth: 8,  breaches: 0, compliancePct: 100,  credits: 0,    trend: "up"   },
  { name: "Filinvest Land",         initials: "FL", tone: "teal",  jobsThisMonth: 10, breaches: 0, compliancePct: 100,  credits: 0,    trend: "up"   },
]

function FieldSLA() {
  const { push } = useRouter()
  const [tier, setTier] = React.useState<"all" | "P1" | "P2" | "P3">("all")
  const [selected, setSelected] = React.useState<SLAIncident | null>(null)

  const totalJobs       = SLA_TIERS.reduce((s, t) => s + t.jobsThisMonth, 0)
  const totalBreached   = SLA_TIERS.reduce((s, t) => s + t.breached, 0)
  const totalAtRisk     = SLA_TIERS.reduce((s, t) => s + t.atRisk, 0)
  const overallComp     = ((totalJobs - totalBreached) / Math.max(totalJobs, 1)) * 100
  const totalCredits    = CUSTOMER_SCORES.reduce((s, c) => s + c.credits, 0)
  const openIncidents   = SLA_INCIDENTS.filter(i => i.status !== "resolved")

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Field Service" }, { label: "SLA Tracking" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="settings">Edit SLA policy</Button>
            <Button variant="primary" leadingIcon="arrow-right" onClick={() => push("/field/dispatch")}>Open dispatch</Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          {/* Hero KPIs */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Overall compliance · MTD" value={`${overallComp.toFixed(1)}%`}              sub={`${totalJobs} jobs, ${totalBreached} breached`} accent="success" delta="+0.4pp" deltaDir="up" />
            <KpiTile label="At-risk · right now"      value={totalAtRisk}                              sub="jobs trending late"                            accent="warn"     />
            <KpiTile label="Open incidents"           value={openIncidents.length}                      sub={openIncidents.length === 0 ? "All clear" : "currently mitigating"} accent="danger" />
            <KpiTile label="SLA credits payable"      value={`$${totalCredits.toLocaleString()}`}      sub={`${CUSTOMER_SCORES.filter(c => c.credits > 0).length} customers`}    accent="accent"   />
          </div>

          <AICallout
            title="P1 SLA at risk · 3 jobs need a body before noon"
            body="Three P1 jobs are queued with no crew assigned. The Prime Hotels cold-storage leak hit 12-min response breach already. Pooling Ben Q. (HVAC, available) on the leak repair and assigning Ann M. (electrical, idle in BGC) to the POS rush eliminates 2 of the 3. Want to apply the suggestion?"
            matches={[
              { code: "FJ-2026-0418-08", name: "Prime Hotels · leak repair · P1",       pct: 100 },
              { code: "FJ-2026-0418-09", name: "Pacific Solar · inverter swap · P2",     pct: 92  },
              { code: "FJ-2026-0418-05", name: "Globe Telecom · fire inspection · P2",   pct: 88  },
            ]}
            dismissLabel="Manual triage"
            proceedLabel="Apply assignments"
          />

          {/* Tier scoreboard */}
          <div className="grid grid-cols-3 gap-4">
            {SLA_TIERS.map(t => {
              const comp = ((t.jobsThisMonth - t.breached) / Math.max(t.jobsThisMonth, 1)) * 100
              const target = t.tier === "P1" ? 95 : 98
              return (
                <Card key={t.tier} className={cn("cursor-pointer transition-colors", tier === t.tier && "border-brand")} onClick={() => setTier(tier === t.tier ? "all" : t.tier as any)}>
                  <CardHeader className="bg-cream/30">
                    <div className="flex items-center gap-2">
                      <Badge variant={t.tier === "P1" ? "danger" : t.tier === "P2" ? "warn" : "neutral"}>{t.tier}</Badge>
                      <CardTitle>{t.tier === "P1" ? "Critical" : t.tier === "P2" ? "Standard" : "Routine"}</CardTitle>
                    </div>
                    <span className="text-[10.5px] text-ink-mute font-mono">{t.responseMin}min · {t.resolveHours}h</span>
                  </CardHeader>
                  <div className="p-4">
                    <div className="flex items-baseline gap-2">
                      <span className={cn("font-serif text-[36px] leading-none", comp >= target ? "text-success" : "text-warn")}>{comp.toFixed(1)}%</span>
                      <span className="text-[10px] text-ink-mute">vs {target}% target</span>
                    </div>
                    <div className="h-2 rounded-full bg-cream-deep overflow-hidden mt-3">
                      <div className={cn("h-full rounded-full", comp >= target ? "bg-success" : "bg-warn")} style={{ width: `${comp}%` }} />
                    </div>
                    <div className="grid grid-cols-3 gap-2 mt-3 text-[11px]">
                      <div className="text-center">
                        <div className="font-mono font-bold text-success">{t.onTrack}</div>
                        <div className="text-[9px] uppercase tracking-wider text-ink-mute">On track</div>
                      </div>
                      <div className="text-center">
                        <div className="font-mono font-bold text-warn">{t.atRisk}</div>
                        <div className="text-[9px] uppercase tracking-wider text-ink-mute">At risk</div>
                      </div>
                      <div className="text-center">
                        <div className="font-mono font-bold text-danger">{t.breached}</div>
                        <div className="text-[9px] uppercase tracking-wider text-ink-mute">Breached</div>
                      </div>
                    </div>
                    <div className="text-[10.5px] text-ink-mute mt-3 text-center">Rolling 30d: <strong className="text-ink">{t.rolling30Compliance.toFixed(1)}%</strong></div>
                  </div>
                </Card>
              )
            })}
          </div>

          <div className="grid grid-cols-[1.4fr_1fr] gap-4">
            {/* Incidents */}
            <Card>
              <CardHeader>
                <CardTitle>SLA incidents · recent</CardTitle>
                <Badge variant={openIncidents.length > 0 ? "danger" : "success"}>{openIncidents.length} open</Badge>
              </CardHeader>
              <table className="w-full text-[12px]">
                <thead>
                  <tr className="bg-cream text-left text-[10px] font-bold uppercase tracking-wider text-ink-mute border-b border-divider">
                    <th className="px-4 py-2">Incident</th>
                    <th>Customer</th>
                    <th>Tier</th>
                    <th>Type</th>
                    <th>Breach</th>
                    <th>Status</th>
                    <th>Owner</th>
                    <th></th>
                  </tr>
                </thead>
                <tbody>
                  {SLA_INCIDENTS.filter(i => tier === "all" || i.tier === tier).map(i => (
                    <tr key={i.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(i)}>
                      <td className="px-4 py-2.5">
                        <div className="font-mono font-bold text-brand-mid text-[11px]">{i.id}</div>
                        <div className="text-[10px] text-ink-mute">{i.raisedAt}</div>
                      </td>
                      <td>
                        <div className="flex items-center gap-2">
                          <Avatar initials={i.customer.initials} tone={i.customer.tone} size="xs" />
                          <span className="font-semibold text-ink">{i.customer.name}</span>
                        </div>
                      </td>
                      <td><Badge variant={i.tier === "P1" ? "danger" : i.tier === "P2" ? "warn" : "neutral"}>{i.tier}</Badge></td>
                      <td className="text-ink-soft text-[11px]">{i.type === "response" ? "Response" : "Resolution"}</td>
                      <td className="text-danger font-semibold">+{i.breachedBy}</td>
                      <td><Badge variant={i.status === "open" ? "danger" : i.status === "mitigating" ? "warn" : "success"} dot>{i.status}</Badge></td>
                      <td><Avatar initials={i.owner.initials} tone={i.owner.tone} size="xs" /></td>
                      <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </Card>

            {/* Customer scoreboard */}
            <Card>
              <CardHeader>
                <CardTitle>Customer scorecard · MTD</CardTitle>
                <span className="text-[10.5px] text-ink-mute">Apr 2026</span>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {CUSTOMER_SCORES.map(c => (
                  <div key={c.name} className="px-4 py-2.5 flex items-center gap-2.5">
                    <Avatar initials={c.initials} tone={c.tone} size="sm" />
                    <div className="flex-1 min-w-0">
                      <div className="text-[12px] font-semibold text-ink truncate">{c.name}</div>
                      <div className="text-[10px] text-ink-mute">{c.jobsThisMonth} jobs · {c.breaches} breached</div>
                    </div>
                    <div className="text-right">
                      <div className={cn("text-[12px] font-mono font-bold flex items-center gap-1",
                        c.compliancePct >= 98 ? "text-success" : c.compliancePct >= 95 ? "text-warn" : "text-danger")}>
                        {c.compliancePct.toFixed(1)}%
                        <Icon name={c.trend === "up" ? "trending-up" : c.trend === "down" ? "trending-down" : "minus"} size={10} strokeWidth={2.5} />
                      </div>
                      {c.credits > 0 && <div className="text-[10px] text-danger">−${c.credits} credit</div>}
                    </div>
                  </div>
                ))}
              </div>
            </Card>
          </div>

        </div>
      </div>

      {selected && (
        <Sheet open={true} onOpenChange={() => setSelected(null)} side="right">
          <SheetHeader>
            <div>
              <div className="font-mono text-[10px] text-ink-mute">{selected.id}</div>
              <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{selected.customer.name} · {selected.tier} {selected.type === "response" ? "Response" : "Resolution"} breach</h2>
              <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
                <Badge variant={selected.status === "open" ? "danger" : selected.status === "mitigating" ? "warn" : "success"} dot>{selected.status}</Badge>
                <span>·</span><span>Raised {selected.raisedAt}</span>
                <span>·</span><span className="text-danger font-semibold">+{selected.breachedBy}</span>
              </div>
            </div>
            <SheetClose onClick={() => setSelected(null)} />
          </SheetHeader>
          <SheetBody>
            <section>
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Linked job</div>
              <button className="w-full bg-surface border border-divider rounded-md px-3 py-2.5 text-left hover:border-divider-strong">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-2">
                    <Icon name="truck" size={12} className="text-ink-mute" />
                    <span className="text-ink-soft">Job</span>
                    <span className="font-mono font-bold text-brand-mid">{selected.jobId}</span>
                  </div>
                  <Icon name="arrow-right" size={12} className="text-ink-faint" />
                </div>
              </button>
            </section>

            {selected.rootCause && (
              <section className="mt-5">
                <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Root cause (preliminary)</div>
                <div className="bg-warn-soft/40 border border-warn/30 rounded-md p-3 text-[11.5px] text-ink-soft">{selected.rootCause}</div>
              </section>
            )}

            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Mitigation</div>
              <div className="space-y-2">
                <button className="w-full bg-surface border border-divider rounded-md px-3 py-2.5 text-left hover:border-divider-strong">
                  <div className="text-[12px] font-semibold text-ink">Notify customer + issue credit</div>
                  <div className="text-[10.5px] text-ink-mute mt-0.5">Standard {selected.tier} breach credit per master agreement (5% of monthly fee)</div>
                </button>
                <button className="w-full bg-surface border border-divider rounded-md px-3 py-2.5 text-left hover:border-divider-strong">
                  <div className="text-[12px] font-semibold text-ink">Open postmortem</div>
                  <div className="text-[10.5px] text-ink-mute mt-0.5">Trigger the SLA incident review process (Olivia P., 5d SLA)</div>
                </button>
              </div>
            </section>

            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Timeline</div>
              <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
                <TLine when="t=0"      what="Job created · SLA clock started" />
                <TLine when={`t+${selected.tier === "P1" ? "30m" : "2h"}`}    what={`${selected.type === "response" ? "Response" : "Resolution"} SLA target`} />
                <TLine when={`t+${selected.tier === "P1" ? "42m" : "3h 14m"}`} what={`Breach by ${selected.breachedBy}`} tone="danger" />
                {selected.status !== "open" && <TLine when="t+ now" what="Mitigation in progress" tone="warn" />}
                {selected.status === "resolved" && <TLine when="t+ now+2h" what="Customer notified · credit applied" tone="success" />}
              </div>
            </section>
          </SheetBody>
          <SheetFooter>
            <Button variant="ghost" onClick={() => setSelected(null)}>Close</Button>
            {selected.status === "open" && <Button variant="primary" leadingIcon="play" className="ml-auto">Start mitigation</Button>}
            {selected.status === "mitigating" && <Button variant="primary" leadingIcon="check" className="ml-auto">Mark resolved</Button>}
          </SheetFooter>
        </Sheet>
      )}
    </>
  )
}

function TLine({ when, what, tone }: any) {
  const tones: Record<string, string> = { danger: "text-danger", warn: "text-warn", success: "text-success" }
  return (
    <div className="px-3 py-2 flex items-center gap-2">
      <span className="font-mono text-[10px] text-ink-faint w-20 shrink-0">{when}</span>
      <span className={cn("text-ink-soft", tone && tones[tone])}>{what}</span>
    </div>
  )
}

// SANDBOX
;(globalThis as any).FieldSLA = FieldSLA

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/logistics/dashboard.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/logistics/dashboard.tsx
 * Logistics / TMS — module dashboard.
 */

function LogisticsDashboard() {
  const k = FLEET_KPIS as any
  const transport = TRANSPORT as any[]
  const inTransit = transport.filter(t => t.status === "in-transit")
  const exceptions = transport.filter(t => t.status === "exception")
  const scheduled = transport.filter(t => t.status === "scheduled")
  return (
    <>
      <TopBar breadcrumb={[{ label: "Logistics" }, { label: "Dashboard" }]} />
      <ActionBar
        title="Logistics Dashboard"
        status="TMS · 5 entities · Updated just now"
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New Transport Order</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Ask AI</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 space-y-4">
          {/* Top KPIs */}
          <div className="grid grid-cols-[1.5fr_1fr_1fr_1fr_1fr] gap-3">
            <div className="bg-gradient-to-br from-brand to-brand-light rounded-lg p-5 text-white">
              <div className="text-[10px] font-bold uppercase tracking-wider text-accent">Deliveries Today</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">{k.deliveriesToday}</div>
              <div className="text-[12px] text-white/70 mt-1.5">{Math.round(k.onTimeDeliveries * 100)}% on-time · {k.exceptionsActive} exceptions active</div>
            </div>
            <KpiTile label="In transit"    value={String(inTransit.length)}  sub="Live tracked"   accent="info" />
            <KpiTile label="Scheduled"     value={String(scheduled.length)}  sub="Next 24 hrs"    accent="warn" />
            <KpiTile label="Fleet active"  value={`${k.active}/${k.totalVehicles}`} sub={`${k.maintenance} in service`} accent="success" />
            <KpiTile label="Freight MTD"   value={`$${(k.freightSpendMtd / 1000).toFixed(0)}k`} sub="-4.2% vs last month" accent="accent" deltaDir="down" delta="-4.2%" />
          </div>

          {/* Two columns */}
          <div className="grid grid-cols-[2fr_1fr] gap-4">
            {/* In-transit list */}
            <Card>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">In transit</div>
                  <div className="font-serif text-[18px] text-ink mt-0.5">{inTransit.length} active deliveries</div>
                </div>
                <Button variant="ghost" size="sm" trailingIcon="arrow-right" onClick={() => location.hash = "#/logistics/transport"}>All transport</Button>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {inTransit.map(t => <TransitRow key={t.id} t={t} />)}
              </div>
            </Card>

            {/* Side: exceptions + routes */}
            <div className="space-y-4">
              <Card>
                <CardHeader>
                  <div>
                    <div className="text-[10px] uppercase tracking-wider font-bold text-danger">Exceptions</div>
                    <div className="font-serif text-[18px] text-ink mt-0.5">{exceptions.length} active</div>
                  </div>
                </CardHeader>
                <div className="p-3 space-y-2">
                  {exceptions.map(e => (
                    <div key={e.id} className="bg-danger-soft/40 border border-danger/20 rounded-md p-2.5">
                      <div className="flex items-center gap-2">
                        <span className="font-mono text-[10px] font-bold text-danger">{e.id}</span>
                        <span className="text-[10.5px] text-ink-mute">{e.entity}</span>
                      </div>
                      <div className="text-[11.5px] text-ink mt-0.5 font-medium">{e.origin.city} → {e.destination.city}</div>
                      <div className="text-[10.5px] text-danger font-semibold mt-0.5">⚠ {e.eta}</div>
                    </div>
                  ))}
                  {exceptions.length === 0 && <div className="text-[11px] text-ink-mute italic text-center py-2">No exceptions</div>}
                </div>
              </Card>
              <Card>
                <CardHeader>
                  <div>
                    <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Top routes</div>
                    <div className="font-serif text-[18px] text-ink mt-0.5">Efficiency this week</div>
                  </div>
                </CardHeader>
                <div className="p-3 space-y-2.5">
                  {(ROUTES_TMS as any[]).slice(0, 4).map(r => (
                    <div key={r.id}>
                      <div className="flex items-center justify-between mb-1">
                        <span className="text-[11.5px] text-ink font-medium truncate">{r.name}</span>
                        <span className="text-[10.5px] font-mono font-bold text-ink-mute">{Math.round(r.efficiency * 100)}%</span>
                      </div>
                      <div className="h-1.5 bg-cream rounded-full overflow-hidden">
                        <div className={cn(
                          "h-full",
                          r.efficiency >= 0.9 ? "bg-success" : r.efficiency >= 0.8 ? "bg-warn" : "bg-danger"
                        )} style={{ width: `${r.efficiency * 100}%` }} />
                      </div>
                    </div>
                  ))}
                </div>
              </Card>
            </div>
          </div>
        </div>
      </div>
    </>
  )
}

function TransitRow({ t }: { t: any }) {
  return (
    <div className="px-4 py-3 hover:bg-cream/40 flex items-center gap-3">
      <div className="size-8 rounded bg-info-soft text-info inline-flex items-center justify-center shrink-0">
        <Icon name="truck" size={14} />
      </div>
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2">
          <span className="font-mono text-[10.5px] font-bold text-brand-mid">{t.id}</span>
          <span className="text-[10px] text-ink-mute">· {t.entity}</span>
          <span className="text-[10px] font-mono text-ink-mute">· {t.ref}</span>
        </div>
        <div className="text-[12px] text-ink font-medium mt-0.5 truncate">
          {t.origin.city} → {t.destination.city}
          {t.destination.customer && <span className="text-ink-mute"> · {t.destination.customer}</span>}
        </div>
        <div className="text-[10.5px] text-ink-mute mt-0.5">
          {t.vehicle} · {t.driver}
        </div>
      </div>
      <div className="text-right shrink-0">
        <div className="text-[10px] font-bold text-ink uppercase tracking-wider">{t.eta}</div>
        <div className="w-20 h-1.5 mt-1.5 bg-cream rounded-full overflow-hidden ml-auto">
          <div className="h-full bg-info" style={{ width: `${t.progress * 100}%` }} />
        </div>
      </div>
    </div>
  )
}

// SANDBOX
;(globalThis as any).LogisticsDashboard = LogisticsDashboard

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/logistics/transport-orders.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/logistics/transport-orders.tsx
 * Transport Orders list — the operational TO list with status filters and
 * inline edit. Each TO references an upstream SO/PO/IR/PR.
 */

function TransportOrders() {
  const [view, setView] = React.useState<"list" | "cards">("list")
  const [status, setStatus] = React.useState<string>("active")
  const [search, setSearch] = React.useState("")
  const rows = (TRANSPORT as any[])
  const counts = {
    all:        rows.length,
    active:     rows.filter(t => t.status === "in-transit" || t.status === "scheduled").length,
    "in-transit": rows.filter(t => t.status === "in-transit").length,
    scheduled:  rows.filter(t => t.status === "scheduled").length,
    delivered:  rows.filter(t => t.status === "delivered").length,
    exception:  rows.filter(t => t.status === "exception").length,
    draft:      rows.filter(t => t.status === "draft").length,
  } as Record<string, number>

  let filtered = status === "all" ? rows : status === "active" ? rows.filter(t => t.status === "in-transit" || t.status === "scheduled") : rows.filter(t => t.status === status)
  if (search.trim()) {
    const q = search.toLowerCase()
    filtered = filtered.filter(t => t.id.toLowerCase().includes(q) || t.origin.city.toLowerCase().includes(q) || t.destination.city.toLowerCase().includes(q) || (t.destination.customer ?? "").toLowerCase().includes(q))
  }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Logistics" }, { label: "Transport Orders" }]} />
      <ActionBar
        title="Transport Orders"
        status={<span>{filtered.length} matches · {counts.exception > 0 && <span className="text-danger font-semibold">⚠ {counts.exception} exceptions</span>}</span>}
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search TO, customer, city…" className="w-[300px]" />}
        view={<ListCardToggle value={view} onChange={setView} />}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New TO</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Ask AI</Button>}
        filters={
          <>
            <FilterChip active={status === "active"} onClick={() => setStatus("active")} count={counts.active}>Active</FilterChip>
            <FilterChip active={status === "all"} onClick={() => setStatus("all")} count={counts.all}>All</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={status === "in-transit"} onClick={() => setStatus("in-transit")} count={counts["in-transit"]}>In Transit</FilterChip>
            <FilterChip active={status === "scheduled"} onClick={() => setStatus("scheduled")} count={counts.scheduled}>Scheduled</FilterChip>
            <FilterChip active={status === "delivered"} onClick={() => setStatus("delivered")} count={counts.delivered}>Delivered</FilterChip>
            <FilterChip active={status === "exception"} onClick={() => setStatus("exception")} count={counts.exception} tone="danger">Exceptions</FilterChip>
            <FilterChip active={status === "draft"} onClick={() => setStatus("draft")} count={counts.draft}>Draft</FilterChip>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6">
          {view === "list" ? <TOList rows={filtered} /> : <TOCards rows={filtered} />}
        </div>
      </div>
    </>
  )
}

function TOList({ rows }: { rows: any[] }) {
  return (
    <div className="bg-surface border border-divider rounded-lg overflow-hidden">
      <div className="grid grid-cols-[140px_120px_1.6fr_1fr_140px_120px_100px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
        <span>TO #</span><span>Status</span><span>Origin → Destination</span><span>Driver / Vehicle</span><span className="text-right">ETA / Progress</span><span className="text-right">Cost</span><span></span>
      </div>
      {rows.map(t => (
        <div key={t.id} className="grid grid-cols-[140px_120px_1.6fr_1fr_140px_120px_100px] px-4 py-2.5 items-center border-t border-divider-soft hover:bg-cream/40">
          <div>
            <div className="font-mono text-[11px] font-bold text-brand-mid">{t.id}</div>
            <div className="text-[10px] font-mono text-ink-mute">{t.ref} · {t.entity}</div>
          </div>
          <TOStatusBadge status={t.status} />
          <div>
            <div className="text-[12px] text-ink font-medium">
              {t.origin.city} ({t.origin.country}) → {t.destination.city} ({t.destination.country})
            </div>
            {t.destination.customer && <div className="text-[10.5px] text-ink-mute">{t.destination.customer}</div>}
            <div className="text-[10px] text-ink-mute mt-0.5">{t.loadKg.toLocaleString()} kg · {t.loadM3.toFixed(1)} m³</div>
          </div>
          <div className="flex items-center gap-2">
            {t.driverInitials && <Avatar initials={t.driverInitials} tone="sand" size="sm" />}
            <div className="min-w-0">
              <div className="text-[11.5px] text-ink truncate">{t.driver ?? "—"}</div>
              <div className="text-[10px] font-mono text-ink-mute">{t.vehicle ?? "—"}</div>
            </div>
          </div>
          <div className="text-right">
            <div className={cn(
              "text-[11px] font-semibold",
              t.status === "exception" ? "text-danger" : t.status === "delivered" ? "text-success" : "text-ink"
            )}>{t.eta}</div>
            {t.progress > 0 && t.progress < 1 && (
              <div className="w-24 h-1 mt-1 bg-cream rounded-full overflow-hidden ml-auto">
                <div className="h-full bg-info" style={{ width: `${t.progress * 100}%` }} />
              </div>
            )}
          </div>
          <div className="text-right text-[11.5px] font-mono">{t.cost.amount > 0 ? `${t.cost.ccy === "PHP" ? "₱" : t.cost.ccy === "SGD" ? "S$" : t.cost.ccy === "HKD" ? "HK$" : t.cost.ccy === "AUD" ? "A$" : "₹"} ${t.cost.amount.toLocaleString()}` : "—"}</div>
          <div className="flex items-center gap-1 justify-end">
            <button title="View" className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-cream hover:text-ink"><Icon name="eye" size={11} /></button>
            <button title="Edit" className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-cream hover:text-ink"><Icon name="edit" size={11} /></button>
          </div>
        </div>
      ))}
    </div>
  )
}

function TOCards({ rows }: { rows: any[] }) {
  return (
    <div className="grid grid-cols-2 lg:grid-cols-3 gap-3">
      {rows.map(t => (
        <Card key={t.id} className="hover:shadow-md transition-shadow cursor-pointer">
          <div className="p-4">
            <div className="flex items-center gap-2 mb-2">
              <span className="font-mono text-[11px] font-bold text-brand-mid">{t.id}</span>
              <span className="text-[10px] text-ink-mute">{t.entity}</span>
              <span className="ml-auto"><TOStatusBadge status={t.status} /></span>
            </div>
            <div className="text-[14px] text-ink font-semibold">{t.origin.city} → {t.destination.city}</div>
            {t.destination.customer && <div className="text-[11px] text-ink-mute">{t.destination.customer}</div>}
            <div className="mt-3 flex items-center gap-2">
              {t.driverInitials && <Avatar initials={t.driverInitials} tone="sand" size="sm" />}
              <div className="text-[10.5px] text-ink-mute">{t.driver} · {t.vehicle}</div>
            </div>
            <div className="flex items-center justify-between mt-3">
              <span className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">ETA · {t.eta}</span>
              {t.progress > 0 && t.progress < 1 && (
                <div className="w-20 h-1 bg-cream rounded-full overflow-hidden">
                  <div className="h-full bg-info" style={{ width: `${t.progress * 100}%` }} />
                </div>
              )}
            </div>
          </div>
        </Card>
      ))}
    </div>
  )
}

function TOStatusBadge({ status }: { status: string }) {
  if (status === "in-transit") return <Badge variant="info" dot>In transit</Badge>
  if (status === "scheduled")  return <Badge variant="warn" dot>Scheduled</Badge>
  if (status === "delivered")  return <Badge variant="success">Delivered</Badge>
  if (status === "exception")  return <Badge variant="danger">⚠ Exception</Badge>
  return <Badge variant="neutral">Draft</Badge>
}

// SANDBOX
;(globalThis as any).TransportOrders = TransportOrders

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/logistics/route-planning.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/logistics/route-planning.tsx
 * Route Planning — choose a route, see stops + efficiency + planned vs actual.
 */

function RoutePlanning() {
  const routes = ROUTES_TMS as any[]
  const [selectedId, setSelectedId] = React.useState<string>(routes[0].id)
  const selected = routes.find(r => r.id === selectedId)
  const transport = (TRANSPORT as any[]).filter(t => t.origin.city === selected.name.split(" → ")[0])

  return (
    <>
      <TopBar breadcrumb={[{ label: "Logistics" }, { label: "Route Planning" }]} />
      <ActionBar
        title="Route Planning"
        status={`${routes.length} configured routes · 4 entities`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New route</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Optimize with AI</Button>}
      />

      <div className="flex-1 flex overflow-hidden bg-app">
        {/* Route list */}
        <div className="w-[340px] shrink-0 border-r border-divider overflow-y-auto bg-surface">
          {routes.map(r => (
            <button key={r.id} onClick={() => setSelectedId(r.id)}
              className={cn(
                "w-full text-left p-3 border-b border-divider-soft transition-colors",
                selectedId === r.id ? "bg-brand-soft" : "hover:bg-cream"
              )}>
              <div className="flex items-center justify-between mb-1">
                <span className="font-mono text-[10px] font-bold text-brand-mid">{r.id}</span>
                <span className="text-[10px] font-mono font-bold text-ink-mute">{Math.round(r.efficiency * 100)}%</span>
              </div>
              <div className="text-[12.5px] text-ink font-semibold leading-tight">{r.name}</div>
              <div className="text-[10.5px] text-ink-mute mt-1.5">
                {r.stops} stops · {r.distanceKm} km · {r.avgHours.toFixed(1)}h avg
              </div>
              <div className="h-1.5 mt-2 bg-cream rounded-full overflow-hidden">
                <div className={cn(
                  "h-full",
                  r.efficiency >= 0.9 ? "bg-success" : r.efficiency >= 0.8 ? "bg-warn" : "bg-danger"
                )} style={{ width: `${r.efficiency * 100}%` }} />
              </div>
            </button>
          ))}
        </div>

        {/* Route detail */}
        <div className="flex-1 overflow-y-auto">
          <div className="max-w-[1080px] mx-auto p-6 space-y-4">
            <div>
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Route Plan</div>
              <h2 className="font-serif text-[28px] text-ink mt-1 leading-tight">{selected.name}</h2>
              <div className="text-[12.5px] text-ink-mute mt-1">{selected.id} · Last run: {selected.lastRun}</div>
            </div>

            {/* KPIs */}
            <div className="grid grid-cols-4 gap-3">
              <KpiTile label="Stops"        value={selected.stops}                    accent="brand" />
              <KpiTile label="Distance"     value={`${selected.distanceKm} km`}        accent="info"  />
              <KpiTile label="Avg duration" value={`${selected.avgHours.toFixed(1)} h`} accent="warn"  />
              <KpiTile label="Efficiency"   value={`${Math.round(selected.efficiency * 100)}%`} accent={selected.efficiency >= 0.9 ? "success" : "warn"} />
            </div>

            {/* Map placeholder */}
            <div className="bg-gradient-to-br from-brand-soft via-cream to-info-soft rounded-lg border border-divider relative overflow-hidden" style={{ height: 280 }}>
              <svg viewBox="0 0 600 280" className="w-full h-full">
                {/* Route line */}
                <path d={`M 60 220 Q 200 50 360 140 T 540 80`} stroke="var(--brand-mid)" strokeWidth="3" fill="none" strokeDasharray="6 4" />
                {/* Stops */}
                {Array.from({ length: selected.stops }).map((_, i) => {
                  const t = i / (selected.stops - 1)
                  const x = 60 + t * 480
                  const y = 220 - Math.sin(t * Math.PI) * 140
                  return (
                    <g key={i}>
                      <circle cx={x} cy={y} r="8" fill="white" stroke="var(--brand)" strokeWidth="2.5" />
                      <text x={x} y={y + 4} fontSize="10" fontWeight="bold" textAnchor="middle" fill="var(--brand)">{i + 1}</text>
                    </g>
                  )
                })}
              </svg>
              <div className="absolute bottom-3 left-3 bg-surface/90 backdrop-blur-sm border border-divider rounded-md px-3 py-1.5 text-[10.5px] text-ink-soft">
                <span className="font-semibold">Mock map</span> · Real production: Google Maps Routing
              </div>
            </div>

            {/* Stops table */}
            <div className="bg-surface border border-divider rounded-lg overflow-hidden">
              <div className="px-4 py-2.5 border-b border-divider">
                <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Stops · planned sequence</div>
              </div>
              {Array.from({ length: selected.stops }).map((_, i) => (
                <div key={i} className="grid grid-cols-[40px_1fr_120px_100px_80px] px-4 py-2.5 items-center border-t border-divider-soft text-[11.5px]">
                  <span className="size-7 rounded-full bg-brand-soft text-brand-mid font-bold text-center inline-flex items-center justify-center">{i + 1}</span>
                  <span className="text-ink font-medium">Stop {i + 1} · {["Warehouse", "Customer A", "Customer B", "Distribution C", "Cross-dock D", "Hub E", "Drop F", "Drop G"][i] ?? `Site ${i + 1}`}</span>
                  <span className="font-mono text-ink-mute">+{(i * (selected.distanceKm / selected.stops)).toFixed(1)} km</span>
                  <span className="font-mono text-ink-mute">+{(i * (selected.avgHours / selected.stops) * 60).toFixed(0)} min</span>
                  <Badge variant={i === selected.stops - 1 ? "success" : "neutral"}>{i === selected.stops - 1 ? "End" : `Stop`}</Badge>
                </div>
              ))}
            </div>

            {/* Active TOs */}
            <Card>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Active TOs on this route</div>
                  <div className="font-serif text-[16px] text-ink mt-0.5">{transport.length} in flight</div>
                </div>
              </CardHeader>
              <div className="p-3 space-y-1.5">
                {transport.length === 0 ? <div className="text-[11px] text-ink-mute italic p-4 text-center">None today</div> :
                  transport.slice(0, 4).map(t => (
                    <div key={t.id} className="flex items-center gap-2 p-2 bg-cream/40 rounded">
                      <span className="font-mono text-[10.5px] font-bold text-brand-mid w-24 shrink-0">{t.id}</span>
                      <span className="text-[11.5px] text-ink truncate flex-1">{t.driver}</span>
                      <Badge variant={t.status === "delivered" ? "success" : t.status === "in-transit" ? "info" : t.status === "exception" ? "danger" : "warn"}>{t.status}</Badge>
                    </div>
                  ))
                }
              </div>
            </Card>
          </div>
        </div>
      </div>
    </>
  )
}

// SANDBOX
;(globalThis as any).RoutePlanning = RoutePlanning

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/logistics/fleet.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/logistics/fleet.tsx
 * Fleet + Drivers — vehicles, capacity, drivers, service schedule.
 */

function FleetDrivers() {
  const [view, setView] = React.useState<"list" | "cards">("cards")
  const [entityFilter, setEntityFilter] = React.useState<string>("all")
  const [search, setSearch] = React.useState("")
  let vehicles = (VEHICLES as any[])
  if (entityFilter !== "all") vehicles = vehicles.filter(v => v.entity === entityFilter)
  if (search.trim()) {
    const q = search.toLowerCase()
    vehicles = vehicles.filter(v => v.plate.toLowerCase().includes(q) || v.driver.toLowerCase().includes(q) || v.model.toLowerCase().includes(q))
  }
  const entities = Array.from(new Set((VEHICLES as any[]).map(v => v.entity)))

  return (
    <>
      <TopBar breadcrumb={[{ label: "Logistics" }, { label: "Fleet & Drivers" }]} />
      <ActionBar
        title="Fleet & Drivers"
        status={`${(VEHICLES as any[]).length} vehicles · ${(VEHICLES as any[]).filter(v => v.status === "active").length} active · ${(VEHICLES as any[]).filter(v => v.status === "maintenance").length} in service`}
        search={<SearchBox value={search} onChange={setSearch} placeholder="Plate, driver, model…" className="w-[280px]" />}
        view={<ListCardToggle value={view} onChange={setView} />}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">Add vehicle</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        filters={
          <>
            <FilterChip active={entityFilter === "all"} onClick={() => setEntityFilter("all")}>All entities</FilterChip>
            {entities.map(e => (
              <FilterChip key={e} active={entityFilter === e} onClick={() => setEntityFilter(e)}>{e}</FilterChip>
            ))}
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6">
          {view === "cards" ? (
            <div className="grid grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3">
              {vehicles.map(v => (
                <Card key={v.plate} className="hover:shadow-md transition-shadow">
                  <div className="p-4">
                    <div className="flex items-start justify-between mb-2">
                      <div className="size-10 rounded-md bg-brand-soft text-brand-mid inline-flex items-center justify-center">
                        <Icon name="truck" size={18} />
                      </div>
                      {v.status === "active"      && <Badge variant="success" dot>Active</Badge>}
                      {v.status === "maintenance" && <Badge variant="warn">⚒ In service</Badge>}
                    </div>
                    <div className="font-mono text-[16px] font-bold text-ink">{v.plate}</div>
                    <div className="text-[10.5px] text-ink-mute mt-0.5">{v.entity} · {v.type}</div>
                    <div className="text-[11.5px] text-ink mt-2 leading-tight">{v.model}</div>
                    <div className="text-[10px] text-ink-mute mt-0.5">{v.capacityKg.toLocaleString()} kg capacity</div>
                    {v.driver !== "—" && (
                      <div className="flex items-center gap-2 mt-3 pt-3 border-t border-divider-soft">
                        <Avatar initials={v.driverInitials} tone="sand" size="sm" />
                        <div className="text-[11px] text-ink-soft">{v.driver}</div>
                      </div>
                    )}
                    <div className="text-[10px] text-ink-mute mt-2 pt-2 border-t border-divider-soft">
                      <div>Odo · <span className="font-mono">{v.odometer.toLocaleString()} km</span></div>
                      <div>Next service · <span className="font-mono">{v.nextService}</span></div>
                    </div>
                  </div>
                </Card>
              ))}
            </div>
          ) : (
            <div className="bg-surface border border-divider rounded-lg overflow-hidden">
              <div className="grid grid-cols-[120px_80px_140px_1fr_140px_120px_140px_80px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
                <span>Plate</span><span>Entity</span><span>Type / Model</span><span>Driver</span><span className="text-right">Odometer</span><span>Status</span><span>Next service</span><span></span>
              </div>
              {vehicles.map(v => (
                <div key={v.plate} className="grid grid-cols-[120px_80px_140px_1fr_140px_120px_140px_80px] px-4 py-2.5 items-center border-t border-divider-soft hover:bg-cream/40 text-[11.5px]">
                  <span className="font-mono font-bold text-ink">{v.plate}</span>
                  <span className="font-mono text-ink-mute">{v.entity}</span>
                  <div>
                    <div className="text-ink">{v.type}</div>
                    <div className="text-[10px] text-ink-mute">{v.model}</div>
                  </div>
                  <div className="flex items-center gap-2">
                    {v.driver !== "—" && <Avatar initials={v.driverInitials} tone="sand" size="xs" />}
                    <span className="text-ink-soft">{v.driver}</span>
                  </div>
                  <span className="font-mono text-right">{v.odometer.toLocaleString()} km</span>
                  {v.status === "active"      ? <Badge variant="success" dot>Active</Badge>
                  : v.status === "maintenance" ? <Badge variant="warn">In service</Badge>
                                                : <Badge variant="neutral">Off</Badge>}
                  <span className="font-mono text-ink-mute">{v.nextService}</span>
                  <button className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-cream hover:text-ink"><Icon name="more" size={12} /></button>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </>
  )
}

// SANDBOX
;(globalThis as any).FleetDrivers = FleetDrivers

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/logistics/gps-tracking.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/logistics/gps-tracking.tsx
 * GPS Tracking — live vehicle positions on a regional map mock.
 */

function GPSTracking() {
  const active = (TRANSPORT as any[]).filter(t => t.status === "in-transit" || t.status === "exception" || t.status === "scheduled")
  const [selectedId, setSelectedId] = React.useState<string>(active[0]?.id)
  const selected = active.find(t => t.id === selectedId) ?? active[0]

  return (
    <>
      <TopBar breadcrumb={[{ label: "Logistics" }, { label: "GPS Tracking" }]} />
      <ActionBar
        title="GPS Tracking"
        status={`${active.length} vehicles live · Telematics: Geotab + Cenora App`}
        secondary={<Button variant="ghost" leadingIcon="refresh" size="sm">Refresh now</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Predict ETAs</Button>}
      />

      <div className="flex-1 flex overflow-hidden bg-app">
        {/* Vehicle list */}
        <div className="w-[340px] shrink-0 border-r border-divider overflow-y-auto bg-surface">
          {active.map(t => (
            <button key={t.id} onClick={() => setSelectedId(t.id)}
              className={cn(
                "w-full text-left p-3 border-b border-divider-soft transition-colors",
                selectedId === t.id ? "bg-brand-soft" : "hover:bg-cream"
              )}>
              <div className="flex items-center gap-2 mb-1">
                <span className={cn("size-2 rounded-full",
                  t.status === "in-transit" ? "bg-info animate-pulse" : t.status === "exception" ? "bg-danger animate-pulse" : "bg-warn"
                )} />
                <span className="font-mono text-[10px] font-bold text-brand-mid">{t.id}</span>
                <span className="text-[10px] text-ink-mute ml-auto">{t.entity}</span>
              </div>
              <div className="text-[12px] text-ink font-semibold leading-tight">{t.origin.city} → {t.destination.city}</div>
              <div className="text-[10.5px] text-ink-mute mt-1">{t.driver} · {t.vehicle}</div>
              <div className="flex items-center justify-between mt-1.5">
                <span className={cn("text-[10px] font-semibold", t.status === "exception" ? "text-danger" : "text-ink-mute")}>{t.eta}</span>
                {t.progress > 0 && t.progress < 1 && (
                  <div className="w-20 h-1 bg-cream rounded-full overflow-hidden">
                    <div className="h-full bg-info" style={{ width: `${t.progress * 100}%` }} />
                  </div>
                )}
              </div>
            </button>
          ))}
        </div>

        {/* Map */}
        <div className="flex-1 flex flex-col">
          <div className="flex-1 relative overflow-hidden bg-gradient-to-br from-brand-soft via-cream to-info-soft">
            {/* Simulated map grid */}
            <svg viewBox="0 0 1000 600" className="absolute inset-0 w-full h-full" preserveAspectRatio="xMidYMid meet">
              {/* lat/lng gridlines */}
              {Array.from({ length: 10 }).map((_, i) => (
                <line key={"v" + i} x1={i * 100} y1={0} x2={i * 100} y2={600} stroke="var(--brand-soft)" strokeWidth="1" />
              ))}
              {Array.from({ length: 6 }).map((_, i) => (
                <line key={"h" + i} x1={0} y1={i * 100} x2={1000} y2={i * 100} stroke="var(--brand-soft)" strokeWidth="1" />
              ))}

              {/* Country labels */}
              {COUNTRY_PINS.map(c => (
                <text key={c.label} x={c.x} y={c.y - 24} fontSize="11" fontWeight="600" fill="var(--ink-mute)" textAnchor="middle">{c.label}</text>
              ))}
              {/* Country anchor dots */}
              {COUNTRY_PINS.map(c => (
                <circle key={c.label + "dot"} cx={c.x} cy={c.y} r="3" fill="var(--ink-faint)" />
              ))}

              {/* Vehicle pins */}
              {active.map((t, i) => {
                const pos = positionFor(t, i)
                const isSelected = t.id === selected.id
                const tone = t.status === "exception" ? "var(--danger)" : t.status === "in-transit" ? "var(--info)" : "var(--warn)"
                return (
                  <g key={t.id} className="cursor-pointer" onClick={() => setSelectedId(t.id)}>
                    {isSelected && <circle cx={pos.x} cy={pos.y} r="22" fill={tone} opacity="0.15" />}
                    {isSelected && <circle cx={pos.x} cy={pos.y} r="14" fill={tone} opacity="0.30" />}
                    <circle cx={pos.x} cy={pos.y} r="9" fill={tone} stroke="white" strokeWidth="2" />
                    <text x={pos.x} y={pos.y + 3} fontSize="9" fontWeight="bold" textAnchor="middle" fill="white">{t.vehicle?.slice(0, 2) ?? "?"}</text>
                  </g>
                )
              })}
            </svg>

            {/* Map controls */}
            <div className="absolute top-3 right-3 bg-surface border border-divider rounded-md p-1 shadow-sm flex flex-col">
              <button className="size-7 inline-flex items-center justify-center rounded hover:bg-cream"><Icon name="plus" size={13} /></button>
              <div className="h-px bg-divider mx-1" />
              <button className="size-7 inline-flex items-center justify-center rounded hover:bg-cream"><Icon name="minus" size={13} /></button>
            </div>

            <div className="absolute bottom-3 left-3 bg-surface/90 backdrop-blur-sm border border-divider rounded-md px-3 py-1.5 text-[10.5px] text-ink-soft">
              <span className="font-semibold">Mock map.</span> Real: Geotab Telematics + Mapbox.
            </div>
          </div>

          {/* Selected detail */}
          {selected && (
            <div className="border-t border-divider bg-surface px-5 py-4">
              <div className="flex items-center gap-4">
                <Avatar initials={selected.driverInitials} tone="sand" size="lg" />
                <div className="flex-1 min-w-0">
                  <div className="flex items-center gap-2">
                    <span className="font-mono text-[12px] font-bold text-brand-mid">{selected.id}</span>
                    <span className="text-[10.5px] text-ink-mute">{selected.entity}</span>
                    <span className="text-[10.5px] font-mono text-ink-mute">· {selected.vehicle}</span>
                  </div>
                  <div className="text-[15px] text-ink font-semibold mt-0.5">{selected.driver}</div>
                  <div className="text-[11.5px] text-ink-mute">{selected.origin.city} → {selected.destination.city}{selected.destination.customer && ` · ${selected.destination.customer}`}</div>
                </div>
                <div className="grid grid-cols-3 gap-6 text-right">
                  <div>
                    <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Speed</div>
                    <div className="font-mono text-[14px] font-bold text-ink">{Math.round(36 + Math.random() * 38)} km/h</div>
                  </div>
                  <div>
                    <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">ETA</div>
                    <div className={cn("font-mono text-[14px] font-bold", selected.status === "exception" ? "text-danger" : "text-ink")}>{selected.eta}</div>
                  </div>
                  <div>
                    <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Progress</div>
                    <div className="font-mono text-[14px] font-bold text-ink">{Math.round(selected.progress * 100)}%</div>
                  </div>
                </div>
                <Button variant="primary" leadingIcon="phone" size="sm">Call driver</Button>
              </div>
            </div>
          )}
        </div>
      </div>
    </>
  )
}

const COUNTRY_PINS = [
  { label: "Philippines", x: 240, y: 220 },
  { label: "Hong Kong",   x: 280, y: 130 },
  { label: "Singapore",   x: 180, y: 320 },
  { label: "India",       x: 80,  y: 200 },
  { label: "Australia",   x: 540, y: 460 },
]

function positionFor(t: any, i: number) {
  const country = t.origin.country
  const base = { "PH": [240, 220], "HK": [280, 130], "SG": [180, 320], "IN": [80, 200], "AU": [540, 460] } as any
  const [bx, by] = base[country] ?? [400, 300]
  const offset = ((i % 4) - 1.5) * 40
  return { x: bx + offset, y: by + ((i % 3) - 1) * 20 + t.progress * 30 }
}

// SANDBOX
;(globalThis as any).GPSTracking = GPSTracking

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/approvals/inbox.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/approvals/inbox.tsx
 * ----------------------------------------------------------------------------
 * Core Approvals — central inbox of every pending approval the current user
 * owns or watches, across all document types and entities.
 *
 * Left:  list of approvals (filter chips above)
 * Right: detail panel with approver chain, requestor profile, conversation
 *        thread, and Approve / Reject with remarks
 * ============================================================================ */

function ApprovalsInbox() {
  const { push } = useRouter()
  const [docFilter, setDocFilter] = React.useState<string>("all")
  const [entityFilter, setEntityFilter] = React.useState<string>("all")
  const [urgencyFilter, setUrgencyFilter] = React.useState<string>("all")
  const [search, setSearch] = React.useState("")
  const [selected, setSelected] = React.useState<any>(APPROVALS[0])

  const docs: string[] = ["all", ...Array.from(new Set(APPROVALS.map((a: any) => a.type)))]
  const entities = ["all", ...Array.from(new Set(APPROVALS.map((a: any) => a.entity)))]

  const filtered = React.useMemo(() => {
    let r = APPROVALS as any[]
    if (docFilter !== "all")     r = r.filter(a => a.type === docFilter)
    if (entityFilter !== "all")  r = r.filter(a => a.entity === entityFilter)
    if (urgencyFilter !== "all") r = r.filter(a => a.urgency === urgencyFilter)
    if (search.trim()) {
      const q = search.toLowerCase()
      r = r.filter(a => a.id.toLowerCase().includes(q) || a.title.toLowerCase().includes(q) || a.requestor.name.toLowerCase().includes(q))
    }
    return r
  }, [docFilter, entityFilter, urgencyFilter, search])

  const totalUsd = filtered.reduce((s, a) => s + (a.amountUsd ?? 0), 0)

  return (
    <>
      <TopBar breadcrumb={[{ label: "Core" }, { label: "Approvals", href: "/approvals/inbox" }, { label: "Inbox" }]} />

      <ActionBar
        title="Approval Inbox"
        status={
          <span>
            <span className="font-mono font-bold text-ink">{filtered.length}</span> awaiting action ·
            $<span className="font-mono font-bold text-ink">{(totalUsd / 1000).toFixed(0)}k</span> USD-equiv ·
            <span className="text-warn font-semibold ml-1">{(APPROVALS as any[]).filter(a => a.urgency === "urgent" || a.urgency === "blocker").length} urgent</span>
          </span>
        }
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search ID, title, requestor…" className="w-[280px]" />}
        leading={
          <div className="flex items-center gap-1.5 ml-2">
            {(["inbox", "rules", "history"] as const).map(t => (
              <button key={t}
                onClick={() => push(t === "inbox" ? "/approvals/inbox" : `/approvals/${t}`)}
                className={cn(
                  "h-7 px-3 rounded-md text-[11px] font-semibold transition-colors",
                  t === "inbox" ? "bg-brand text-white" : "bg-cream text-ink-soft hover:bg-cream-deep"
                )}>
                {t === "inbox" ? "Inbox" : t === "rules" ? "Rules" : "History"}
              </button>
            ))}
          </div>
        }
        primary={<Button variant="primary" leadingIcon="check" size="sm">Bulk approve</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        filters={
          <>
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mr-1">Doc</span>
            {docs.slice(0, 9).map(d => (
              <FilterChip key={d} active={docFilter === d} onClick={() => setDocFilter(d)}>{d === "all" ? "All types" : d}</FilterChip>
            ))}
            <span className="w-px h-5 bg-divider mx-1.5" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mr-1">Entity</span>
            {entities.map(e => (
              <FilterChip key={e} active={entityFilter === e} onClick={() => setEntityFilter(e)}>{e === "all" ? "All entities" : e}</FilterChip>
            ))}
            <span className="w-px h-5 bg-divider mx-1.5" />
            <FilterChip active={urgencyFilter === "urgent"} onClick={() => setUrgencyFilter(urgencyFilter === "urgent" ? "all" : "urgent")}>⚠ Urgent</FilterChip>
            <FilterChip active={urgencyFilter === "blocker"} onClick={() => setUrgencyFilter(urgencyFilter === "blocker" ? "all" : "blocker")} tone="danger">Blocker</FilterChip>
          </>
        }
      />

      <div className="flex-1 flex overflow-hidden bg-app">
        {/* List */}
        <div className="w-[440px] shrink-0 border-r border-divider overflow-y-auto bg-surface">
          {filtered.map(a => <ApprovalRow key={a.id} a={a} active={selected?.id === a.id} onClick={() => setSelected(a)} />)}
          {filtered.length === 0 && (
            <div className="p-10 text-center">
              <div className="size-12 mx-auto rounded-full bg-success-soft text-success flex items-center justify-center mb-3">
                <Icon name="check" size={20} />
              </div>
              <div className="font-serif text-lg text-ink">Inbox zero</div>
              <div className="text-[11px] text-ink-mute mt-1">No approvals match your filters.</div>
            </div>
          )}
        </div>

        {/* Detail */}
        <div className="flex-1 overflow-y-auto">
          {selected ? <ApprovalDetail a={selected} /> : (
            <div className="flex-1 flex items-center justify-center h-full text-ink-mute text-[12px]">
              Pick an approval on the left
            </div>
          )}
        </div>
      </div>
    </>
  )
}

function ApprovalRow({ a, active, onClick }: { a: any; active: boolean; onClick: () => void }) {
  return (
    <button onClick={onClick}
      className={cn(
        "w-full text-left p-3 border-b border-divider-soft transition-colors block",
        active ? "bg-brand-soft" : "hover:bg-cream"
      )}>
      <div className="flex items-center gap-2">
        <span className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">{a.typeLabel}</span>
        <span className="text-[10px] font-mono text-brand-mid font-bold">{a.id}</span>
        <span className="text-[10px] font-mono text-ink-mute ml-auto">{a.entity}</span>
      </div>
      <div className="text-[12.5px] text-ink font-semibold mt-1 leading-tight line-clamp-2">{a.title}</div>
      <div className="flex items-center justify-between mt-2">
        <div className="flex items-center gap-1.5">
          <Avatar initials={a.requestor.initials} tone="sand" size="xs" />
          <span className="text-[10.5px] text-ink-mute">{a.requestor.name} · {a.submittedAt}</span>
        </div>
        {a.amount && <span className="text-[11px] font-mono font-bold text-ink">{a.amount}</span>}
      </div>
      <div className="flex items-center gap-2 mt-2">
        <div className="flex items-center gap-1">
          {Array.from({ length: a.step.total }).map((_, i) => (
            <span key={i} className={cn("h-1 w-6 rounded-full", i < a.step.current ? "bg-brand" : "bg-divider")} />
          ))}
        </div>
        <span className="text-[10px] text-ink-mute">{a.step.label} · {a.step.current}/{a.step.total}</span>
        <span className="ml-auto">
          {a.urgency === "urgent" && <Badge variant="warn">⚠ Urgent</Badge>}
          {a.urgency === "blocker" && <Badge variant="danger">Blocker</Badge>}
        </span>
      </div>
    </button>
  )
}

function ApprovalDetail({ a }: { a: any }) {
  const [remark, setRemark] = React.useState("")
  return (
    <div className="max-w-[820px] mx-auto p-6 space-y-5">
      {/* Header */}
      <div>
        <div className="flex items-center gap-2 mb-2">
          <span className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">{a.typeLabel}</span>
          <span className="text-[11px] font-mono font-bold text-brand-mid">{a.id}</span>
          <span className="text-[10px] font-mono text-ink-mute">· {a.entity}</span>
          {a.urgency === "urgent" && <Badge variant="warn">⚠ Urgent</Badge>}
          {a.urgency === "blocker" && <Badge variant="danger">Blocker</Badge>}
        </div>
        <h2 className="font-serif text-[26px] text-ink leading-tight">{a.title}</h2>
        {a.amount && (
          <div className="mt-2 inline-flex items-baseline gap-2">
            <span className="font-serif text-[22px] text-ink">{a.amount}</span>
            {a.amountUsd && <span className="text-[11px] text-ink-mute">≈ ${a.amountUsd.toLocaleString()} USD</span>}
          </div>
        )}
      </div>

      {/* Requestor + approval chain */}
      <div className="grid grid-cols-[1.5fr_2fr] gap-4">
        <div className="bg-surface border border-divider rounded-lg p-4">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2.5">Requestor</div>
          <div className="flex items-center gap-3">
            <Avatar initials={a.requestor.initials} tone="sand" size="lg" />
            <div className="flex-1 min-w-0">
              <div className="text-[14px] font-semibold text-ink">{a.requestor.name}</div>
              <div className="text-[11px] text-ink-mute">{a.requestor.role}</div>
              <button className="text-[10.5px] font-semibold text-brand-mid hover:text-brand mt-1 inline-flex items-center gap-1">
                Open profile <Icon name="arrow-right" size={10} />
              </button>
            </div>
          </div>
          <div className="text-[10.5px] text-ink-mute mt-3">Submitted {a.submittedAt}</div>
        </div>

        <div className="bg-surface border border-divider rounded-lg p-4">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2.5">Approval chain</div>
          <div className="space-y-2">
            {Array.from({ length: a.step.total }).map((_, i) => {
              const done = i < a.step.current - 1
              const current = i === a.step.current - 1
              return (
                <div key={i} className="flex items-center gap-2.5">
                  <div className={cn(
                    "size-6 rounded-full inline-flex items-center justify-center text-[10px] font-bold shrink-0",
                    done    ? "bg-success text-white" :
                    current ? "bg-warn-soft text-warn ring-2 ring-warn" :
                              "bg-cream text-ink-mute border border-divider"
                  )}>
                    {done ? "✓" : i + 1}
                  </div>
                  <div className="flex-1 min-w-0">
                    <div className={cn("text-[12px] font-medium", done ? "text-ink line-through opacity-60" : current ? "text-warn font-bold" : "text-ink-soft")}>
                      Step {i + 1} · {i === a.step.current - 1 ? a.step.label : i < a.step.current - 1 ? "Approved" : "Pending"}
                    </div>
                  </div>
                  {done && <span className="text-[10px] text-ink-mute">Today</span>}
                </div>
              )
            })}
          </div>
        </div>
      </div>

      {/* Context */}
      {a.context && a.context.length > 0 && (
        <div className="bg-surface border border-divider rounded-lg p-4">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2.5">Context</div>
          <div className="grid grid-cols-2 gap-3">
            {a.context.map((c: any, i: number) => (
              <div key={i}>
                <div className="text-[10px] text-ink-mute">{c.label}</div>
                <div className="text-[12.5px] text-ink font-medium font-mono">{c.value}</div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* Thread */}
      <div className="bg-surface border border-divider rounded-lg p-4">
        <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2.5">Conversation</div>
        {a.thread && a.thread.length > 0 ? (
          <div className="space-y-3">
            {a.thread.map((m: any, i: number) => (
              <div key={i} className="flex items-start gap-2.5">
                <Avatar initials={m.initials} tone="sand" size="sm" />
                <div className="flex-1 bg-cream rounded-md p-2.5">
                  <div className="flex items-center gap-1.5 mb-1">
                    <span className="text-[11px] font-semibold text-ink">{m.who}</span>
                    <span className="text-[10px] text-ink-mute">{m.when}</span>
                  </div>
                  <div className="text-[12px] text-ink-soft">{m.text}</div>
                </div>
              </div>
            ))}
          </div>
        ) : (
          <div className="text-[11px] text-ink-mute italic">No conversation yet. Be the first to comment.</div>
        )}
      </div>

      {/* Action box */}
      <div className="bg-surface border border-divider rounded-lg p-4">
        <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2.5">Your decision</div>
        <textarea
          value={remark}
          onChange={e => setRemark(e.target.value)}
          placeholder="Add approver remarks (required for reject, recommended for approve)…"
          className="w-full h-20 bg-cream border border-divider rounded-md p-2.5 text-[12.5px] resize-none focus:outline-none focus:border-brand-mid placeholder:text-ink-faint"
        />
        <div className="flex items-center justify-between mt-3">
          <div className="text-[10.5px] text-ink-mute">
            Approving this will route to <span className="font-semibold text-ink">{a.step.current < a.step.total ? `step ${a.step.current + 1}` : "final disposition"}</span>
          </div>
          <div className="flex items-center gap-2">
            <Button variant="ghost" size="sm">Request changes</Button>
            <Button variant="danger" size="sm" leadingIcon="x">Reject</Button>
            <Button variant="primary" size="sm" leadingIcon="check">Approve</Button>
          </div>
        </div>
      </div>
    </div>
  )
}

// SANDBOX
;(globalThis as any).ApprovalsInbox = ApprovalsInbox

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/approvals/rules.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/approvals/rules.tsx
 * Approval Rules — the rules engine that drives the inbox routing.
 */

function ApprovalsRules() {
  const { push } = useRouter()
  const [search, setSearch] = React.useState("")
  const filtered = search.trim()
    ? (APPROVAL_RULES as any[]).filter(r => r.scope.toLowerCase().includes(search.toLowerCase()) || r.trigger.toLowerCase().includes(search.toLowerCase()))
    : (APPROVAL_RULES as any[])

  return (
    <>
      <TopBar breadcrumb={[{ label: "Core" }, { label: "Approvals", href: "/approvals/inbox" }, { label: "Rules" }]} />

      <ActionBar
        title="Approval Rules"
        status={`${(APPROVAL_RULES as any[]).length} rules · ${(APPROVAL_RULES as any[]).filter(r => r.active).length} active · Last updated 3 days ago`}
        leading={
          <div className="flex items-center gap-1.5 ml-2">
            {(["inbox", "rules", "history"] as const).map(t => (
              <button key={t}
                onClick={() => push(t === "inbox" ? "/approvals/inbox" : `/approvals/${t}`)}
                className={cn(
                  "h-7 px-3 rounded-md text-[11px] font-semibold transition-colors",
                  t === "rules" ? "bg-brand text-white" : "bg-cream text-ink-soft hover:bg-cream-deep"
                )}>
                {t === "inbox" ? "Inbox" : t === "rules" ? "Rules" : "History"}
              </button>
            ))}
          </div>
        }
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search rules…" className="w-[260px]" />}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New rule</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1180px] mx-auto p-6 space-y-3">
          <div className="bg-info-soft border border-info/30 rounded-lg p-3 flex items-start gap-2.5">
            <Icon name="info" size={14} className="text-info mt-0.5" />
            <div className="text-[11.5px] text-info">
              <span className="font-semibold">Rules engine.</span> When a document is submitted, rules are evaluated in order. The first matching rule's chain becomes the routing path. Rules are tenant-wide unless scoped to an entity.
            </div>
          </div>

          <div className="bg-surface border border-divider rounded-lg overflow-hidden">
            <div className="grid grid-cols-[64px_140px_2fr_2fr_80px_80px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
              <span>Active</span><span>Scope</span><span>Trigger</span><span>Approval chain</span><span>Status</span><span></span>
            </div>
            {filtered.map(r => (
              <div key={r.id} className="grid grid-cols-[64px_140px_2fr_2fr_80px_80px] px-4 py-3 items-center border-t border-divider-soft hover:bg-cream/40">
                <span><Switch on={r.active} /></span>
                <span className="text-[11.5px] font-semibold text-ink">{r.scope}</span>
                <span className="text-[11.5px] text-ink-soft">{r.trigger}</span>
                <div className="flex items-center gap-1.5 flex-wrap">
                  {r.chain.map((c: string, i: number) => (
                    <React.Fragment key={i}>
                      <span className="px-1.5 py-0.5 rounded bg-brand-soft text-brand-mid text-[10.5px] font-semibold">{c}</span>
                      {i < r.chain.length - 1 && <Icon name="arrow-right" size={9} className="text-ink-mute" />}
                    </React.Fragment>
                  ))}
                </div>
                <Badge variant={r.active ? "success" : "neutral"} dot>{r.active ? "Active" : "Off"}</Badge>
                <div className="flex items-center gap-1">
                  <button title="Edit" className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-cream hover:text-ink"><Icon name="edit" size={11} /></button>
                  <button title="Duplicate" className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-cream hover:text-ink"><Icon name="copy" size={11} /></button>
                  <button title="Delete" className="size-6 inline-flex items-center justify-center rounded text-ink-mute hover:bg-danger-soft hover:text-danger"><Icon name="trash" size={11} /></button>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </>
  )
}

function Switch({ on }: { on: boolean }) {
  return (
    <span className={cn(
      "inline-flex h-4 w-7 rounded-full transition-colors p-px",
      on ? "bg-brand" : "bg-divider"
    )}>
      <span className={cn("size-3.5 rounded-full bg-white shadow transition-transform", on && "translate-x-3")} />
    </span>
  )
}

// SANDBOX
;(globalThis as any).ApprovalsRules = ApprovalsRules

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/approvals/history.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/approvals/history.tsx
 * Approval History — past actions taken by/on behalf of the current user.
 */

function ApprovalsHistory() {
  const { push } = useRouter()
  const [statusFilter, setStatusFilter] = React.useState<"all" | "approved" | "rejected">("all")
  const [search, setSearch] = React.useState("")
  const hist = APPROVAL_HISTORY as any[]
  let filtered = statusFilter === "all" ? hist : hist.filter(h => h.status === statusFilter)
  if (search.trim()) {
    const q = search.toLowerCase()
    filtered = filtered.filter(h => h.id.toLowerCase().includes(q) || h.title.toLowerCase().includes(q))
  }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Core" }, { label: "Approvals", href: "/approvals/inbox" }, { label: "History" }]} />

      <ActionBar
        title="Approval History"
        status={`Last 30 days · ${hist.length} actions · ${hist.filter(h => h.status === "approved").length} approved · ${hist.filter(h => h.status === "rejected").length} rejected`}
        leading={
          <div className="flex items-center gap-1.5 ml-2">
            {(["inbox", "rules", "history"] as const).map(t => (
              <button key={t}
                onClick={() => push(t === "inbox" ? "/approvals/inbox" : `/approvals/${t}`)}
                className={cn(
                  "h-7 px-3 rounded-md text-[11px] font-semibold transition-colors",
                  t === "history" ? "bg-brand text-white" : "bg-cream text-ink-soft hover:bg-cream-deep"
                )}>
                {t === "inbox" ? "Inbox" : t === "rules" ? "Rules" : "History"}
              </button>
            ))}
          </div>
        }
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search…" className="w-[260px]" />}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        filters={
          <>
            <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")}>All</FilterChip>
            <FilterChip active={statusFilter === "approved"} onClick={() => setStatusFilter("approved")}>Approved</FilterChip>
            <FilterChip active={statusFilter === "rejected"} onClick={() => setStatusFilter("rejected")} tone="danger">Rejected</FilterChip>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1180px] mx-auto p-6">
          <div className="bg-surface border border-divider rounded-lg overflow-hidden">
            <div className="grid grid-cols-[140px_140px_1.6fr_140px_140px_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
              <span>Document ID</span><span>Type</span><span>Title</span><span>By</span><span>When</span><span className="text-right">Amount</span>
            </div>
            {filtered.map(h => (
              <div key={h.id} className="grid grid-cols-[140px_140px_1.6fr_140px_140px_120px] px-4 py-2.5 items-center border-t border-divider-soft hover:bg-cream/40">
                <span className="font-mono text-[11px] font-bold text-brand-mid">{h.id}</span>
                <span className="text-[11.5px] text-ink-soft">{h.type}</span>
                <span className="text-[12px] text-ink truncate">{h.title}</span>
                <span className="text-[11px] text-ink-soft">{h.who}</span>
                <span className="text-[11px] text-ink-mute">{h.when}</span>
                <span className="text-right">
                  <Badge variant={h.status === "approved" ? "success" : "danger"}>{h.status === "approved" ? "✓ Approved" : "✗ Rejected"}</Badge>
                  <div className="font-mono text-[10.5px] text-ink-mute mt-0.5">{h.amount}</div>
                </span>
              </div>
            ))}
          </div>
        </div>
      </div>
    </>
  )
}

// SANDBOX
;(globalThis as any).ApprovalsHistory = ApprovalsHistory

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/support/tickets.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/support/tickets.tsx
 * ----------------------------------------------------------------------------
 * Support — Tickets Inbox. Master list + right-side detail sheet with
 * conversation thread, AI suggestion panel, customer context, SLA timer.
 * This is the heart of the Support module.
 * ============================================================================
 */

function SupportTickets() {
  const tickets = (SUPPORT_TICKETS as any[])
  const queues  = (SUPPORT_QUEUES  as any[])
  const agents  = (SUPPORT_AGENTS  as any[])

  const [status, setStatus]     = React.useState<string>("open")
  const [queue,  setQueue]      = React.useState<string>("all")
  const [priority, setPriority] = React.useState<string>("all")
  const [channel,  setChannel]  = React.useState<string>("all")
  const [search,   setSearch]   = React.useState<string>("")
  const [selectedId, setSelectedId] = React.useState<string | null>(null)

  const buckets = {
    "open":     (t: any) => ["new", "open", "pending"].includes(t.status),
    "all":      () => true,
    "breached": (t: any) => t.slaState === "breached",
    "mine":     (t: any) => t.assignee?.initials === "SM",   /* "me" = Sofia for the demo */
    "resolved": (t: any) => ["resolved", "closed"].includes(t.status),
  } as Record<string, (t: any) => boolean>

  let list = tickets.filter(buckets[status] ?? buckets["all"])
  if (queue !== "all")    list = list.filter(t => t.queue === queue)
  if (priority !== "all") list = list.filter(t => t.priority === priority)
  if (channel !== "all")  list = list.filter(t => t.channel === channel)
  if (search.trim()) {
    const q = search.trim().toLowerCase()
    list = list.filter(t => t.subject.toLowerCase().includes(q) || t.customer.name.toLowerCase().includes(q) || t.id.toLowerCase().includes(q))
  }

  /* Sort: breached → at-risk → on-track; within tier by createdAt desc */
  list = [...list].sort((a, b) => {
    const order = { breached: 0, "at-risk": 1, "on-track": 2 } as any
    const d = order[a.slaState] - order[b.slaState]
    if (d !== 0) return d
    return b.createdAt.localeCompare(a.createdAt)
  })

  const selected = selectedId ? (TKT_BY_ID as any)[selectedId] : null

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Support" }, { label: "Tickets" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download" size="sm">Export</Button>
            <Button variant="ghost" leadingIcon="settings" size="sm">Views</Button>
            <Button variant="primary" leadingIcon="plus" size="sm">New ticket</Button>
          </>
        }
      />

      <ActionBar
        title="Tickets"
        status={`${tickets.filter(buckets["open"]).length} open · ${SUPPORT_KPIS.breached} breached · ${SUPPORT_KPIS.atRisk} at-risk · ${SUPPORT_KPIS.resolvedToday} resolved today`}
        primary={
          <div className="relative">
            <Icon name="search" size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-ink-faint" />
            <input
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Search subject, customer, TKT-…"
              className="w-[280px] h-7 pl-7 pr-2 text-[11.5px] bg-surface border border-divider rounded-md focus:outline-none focus:border-brand"
            />
          </div>
        }
        filters={
          <>
            <FilterChip active={status === "open"}     onClick={() => setStatus("open")}>Open</FilterChip>
            <FilterChip active={status === "breached"} onClick={() => setStatus("breached")}>Breached</FilterChip>
            <FilterChip active={status === "mine"}     onClick={() => setStatus("mine")}>Assigned to me</FilterChip>
            <FilterChip active={status === "resolved"} onClick={() => setStatus("resolved")}>Resolved</FilterChip>
            <FilterChip active={status === "all"}      onClick={() => setStatus("all")}>All</FilterChip>

            <span className="w-px h-5 bg-divider mx-1" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mr-1">Queue</span>
            <select value={queue} onChange={e => setQueue(e.target.value)} className="h-7 text-[11.5px] bg-surface border border-divider rounded-md px-2 focus:outline-none focus:border-brand">
              <option value="all">All queues</option>
              {queues.map((q: any) => <option key={q.id} value={q.id}>{q.label}</option>)}
            </select>

            <span className="w-px h-5 bg-divider mx-1" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mr-1">Priority</span>
            <FilterChip active={priority === "all"} onClick={() => setPriority("all")}>All</FilterChip>
            <FilterChip active={priority === "P1"}  onClick={() => setPriority("P1")}>P1</FilterChip>
            <FilterChip active={priority === "P2"}  onClick={() => setPriority("P2")}>P2</FilterChip>
            <FilterChip active={priority === "P3"}  onClick={() => setPriority("P3")}>P3</FilterChip>

            <span className="w-px h-5 bg-divider mx-1" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mr-1">Channel</span>
            <FilterChip active={channel === "all"}      onClick={() => setChannel("all")}>All</FilterChip>
            <FilterChip active={channel === "email"}    onClick={() => setChannel("email")}>Email</FilterChip>
            <FilterChip active={channel === "chat"}     onClick={() => setChannel("chat")}>Chat</FilterChip>
            <FilterChip active={channel === "phone"}    onClick={() => setChannel("phone")}>Phone</FilterChip>
            <FilterChip active={channel === "portal"}   onClick={() => setChannel("portal")}>Portal</FilterChip>
            <FilterChip active={channel === "whatsapp"} onClick={() => setChannel("whatsapp")}>WA</FilterChip>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          {/* AI insight strip */}
          <AICallout
            title="3 tickets share the same root cause — bulk-merge?"
            body="Cenora AI noticed POS-T4 outages at Metro Retail (TKT-1418), Globe (TKT-1401), and PNB are all post-firmware 2.4.0. Suggested action: bulk-link to RCA-2026-0418, push KB-POS-021 to the inbox, and proactively reach out to 12 more affected sites before they call in."
            matches={[
              { code: "TKT-2026-1418", name: "Metro Retail · POS outage · P1",          pct: 100 },
              { code: "RCA-2026-0418", name: "Root-cause analysis · firmware 2.4.0",     pct: 98  },
              { code: "KB-POS-021",     name: "Article · POS-T4 firmware rollback",       pct: 96  },
            ]}
            dismissLabel="Triage manually"
            proceedLabel="Apply bulk action"
          />

          {/* Inbox list */}
          <Card>
            <div className="grid grid-cols-[42px_120px_2.2fr_140px_120px_70px_120px_120px_42px] px-3 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-b border-divider">
              <span></span>
              <span>Ticket</span>
              <span>Subject · customer</span>
              <span>Queue</span>
              <span>Assignee</span>
              <span>P</span>
              <span>SLA</span>
              <span>Updated</span>
              <span></span>
            </div>
            {list.length === 0 && (
              <div className="px-4 py-12 text-center text-ink-mute text-[12px]">No tickets match these filters.</div>
            )}
            {list.map((t: any) => (
              <TicketRow key={t.id} t={t} active={selectedId === t.id} onClick={() => setSelectedId(t.id)} />
            ))}
          </Card>
        </div>
      </div>

      {selected && (
        <TicketDetailSheet
          t={selected}
          onClose={() => setSelectedId(null)}
        />
      )}
    </>
  )
}

/* ─── Row ─────────────────────────────────────────────────────────── */
function TicketRow({ t, active, onClick }: any) {
  const slaTone: Record<string, string> = { "on-track": "success", "at-risk": "warn", "breached": "danger" }
  const senTone: Record<string, string> = { positive: "success", neutral: "ink-mute", negative: "danger" }
  return (
    <button
      onClick={onClick}
      className={cn(
        "w-full grid grid-cols-[42px_120px_2.2fr_140px_120px_70px_120px_120px_42px] px-3 py-2.5 items-center border-b border-divider-soft hover:bg-cream/40 text-left text-[11.5px] transition-colors",
        active && "bg-brand-soft/40 hover:bg-brand-soft/40",
      )}>
      {/* Channel + unread dot */}
      <div className="flex items-center gap-2">
        {["new"].includes(t.status) && <span className="size-1.5 rounded-full bg-brand shrink-0" />}
        {!["new"].includes(t.status) && <span className="size-1.5 shrink-0" />}
        <Icon name={(CH_ICON as any)[t.channel]} size={13} className="text-ink-mute" />
      </div>

      <div className="font-mono text-[10.5px]">
        <div className="font-bold text-brand-mid">{t.id}</div>
        <div className="text-ink-faint text-[9.5px]">{t.createdAt}</div>
      </div>

      <div className="min-w-0 pr-2">
        <div className="text-ink font-semibold truncate flex items-center gap-1.5">
          {t.subject}
          {t.sentiment === "negative" && <span title="negative sentiment" className="size-1.5 rounded-full bg-danger shrink-0" />}
        </div>
        <div className="flex items-center gap-1.5 mt-0.5 text-[10.5px] text-ink-mute">
          <Avatar initials={t.customer.initials} tone={t.customer.tone} size="xs" />
          <span className="truncate text-ink-soft">{t.customer.name}</span>
          <span className="text-ink-faint">·</span>
          <span className="text-ink-faint">{t.customer.tier}</span>
          {t.tags?.[0] && (<><span className="text-ink-faint">·</span>
            <span className="text-ink-faint font-mono">#{t.tags[0]}</span></>)}
        </div>
      </div>

      <div className="text-[10.5px]">
        <Badge variant="neutral">{(SUPPORT_QUEUE_BY_ID as any)[t.queue]?.label.split(" ")[0] ?? t.queue}</Badge>
      </div>

      <div className="flex items-center gap-1.5 min-w-0">
        {t.assignee ? (
          <>
            <Avatar initials={t.assignee.initials} tone={t.assignee.tone} size="xs" />
            <span className="text-ink-soft truncate text-[11px]">{t.assignee.name}</span>
          </>
        ) : (
          <span className="text-ink-faint italic text-[11px]">Unassigned</span>
        )}
      </div>

      <div>
        {t.priority === "P1" ? <Badge variant="danger">P1</Badge> : t.priority === "P2" ? <Badge variant="warn">P2</Badge> : <Badge variant="neutral">P3</Badge>}
      </div>

      <div className={cn(
        "text-[10.5px] font-mono",
        t.slaState === "breached" ? "text-danger font-bold" : t.slaState === "at-risk" ? "text-warn" : "text-ink-soft"
      )}>
        {t.slaState === "breached" && <Icon name="alert-triangle" size={11} className="inline mr-1" />}
        {t.resolveDueIn}
      </div>

      <div className="text-[10.5px] text-ink-mute">{t.lastUpdated.split(" · ")[1] ?? t.lastUpdated}</div>

      <Icon name="chevron-right" size={12} className="text-ink-faint justify-self-end" />
    </button>
  )
}

/* ─── Detail drawer (custom — wider than the standard Sheet) ──────── */
function TicketDetailSheet({ t, onClose }: { t: any; onClose: () => void }) {
  const [reply, setReply] = React.useState("")
  const [tab, setTab] = React.useState<"thread" | "context">("thread")
  const queue = (SUPPORT_QUEUE_BY_ID as any)[t.queue]

  React.useEffect(() => {
    const onKey = (e: KeyboardEvent) => { if (e.key === "Escape") onClose() }
    document.addEventListener("keydown", onKey)
    document.body.style.overflow = "hidden"
    return () => {
      document.removeEventListener("keydown", onKey)
      document.body.style.overflow = ""
    }
  }, [onClose])

  const aiDraft = `Hi ${t.customer.contact?.split(" ")[0] ?? "there"} — thanks for reaching out.\n\nWe have ${t.status === "new" ? "received your request and are looking into it now" : "an update for you"}. Based on the symptoms you described, this looks like ${t.tags?.[0] ?? "the issue"} — I have linked ${(SUPPORT_KB as any[]).find(k => k.id === (t.queue === "technical" ? "KB-POS-021" : "KB-BILL-007"))?.id} and started a fix track. I will follow up within the next ${queue?.firstResponseTargetMin ?? 60} minutes with confirmation.\n\nBest,\n${t.assignee?.name ?? "the Support team"}`

  return (
    <>
      <div className="fixed inset-0 z-[60] bg-ink/40 backdrop-blur-[2px] animate-fade-in" onClick={onClose} />
      <div role="dialog" aria-modal="true"
        className="fixed top-0 right-0 bottom-0 z-[61] w-[760px] max-w-[94vw] bg-surface border-l border-divider shadow-lg flex flex-col animate-slide-in-right">
      {/* Header */}
      <div className="px-5 py-4 border-b border-divider flex items-start justify-between gap-3">
        <div className="min-w-0 flex-1">
          <div className="flex items-center gap-2 mb-1">
            <span className="font-mono text-[10px] text-ink-mute">{t.id}</span>
            <Icon name={(CH_ICON as any)[t.channel]} size={11} className="text-ink-faint" />
            <span className="text-[10px] text-ink-faint">{(CH_LABEL as any)[t.channel]}</span>
            <span className="text-ink-faint">·</span>
            <span className="text-[10px] text-ink-faint">{t.entity}</span>
          </div>
          <h2 className="font-serif text-xl text-ink leading-tight truncate">{t.subject}</h2>
          <div className="flex items-center gap-2 mt-2 flex-wrap">
            {t.priority === "P1" ? <Badge variant="danger">P1</Badge> : t.priority === "P2" ? <Badge variant="warn">P2</Badge> : <Badge variant="neutral">P3</Badge>}
            <Badge variant="info">{queue?.label}</Badge>
            <Badge variant={t.status === "new" ? "info" : t.status === "open" ? "warn" : t.status === "pending" ? "neutral" : t.status === "resolved" ? "success" : "neutral"} dot>{t.status}</Badge>
            {t.slaState === "breached" && <Badge variant="danger" dot>SLA breached</Badge>}
            {t.slaState === "at-risk" && <Badge variant="warn" dot>SLA at risk</Badge>}
            {t.sentiment === "negative" && <Badge variant="danger">Negative sentiment</Badge>}
            {t.sentiment === "positive" && <Badge variant="success">Positive sentiment</Badge>}
          </div>
        </div>
        <button onClick={onClose} aria-label="Close panel" className="h-7 w-7 rounded-md text-ink-mute hover:bg-cream hover:text-ink inline-flex items-center justify-center transition-colors shrink-0">
          <Icon name="x" size={16} />
        </button>
      </div>

      <div className="flex-1 overflow-y-auto p-5">
        {/* SLA strip */}
        <div className="grid grid-cols-4 gap-2 mb-4">
          <SLAChip label="First response" value={t.firstResponse ?? "—"} tone={t.firstResponse ? "success" : "neutral"} />
          <SLAChip label="Resolve due"    value={t.resolveDueIn} tone={t.slaState === "breached" ? "danger" : t.slaState === "at-risk" ? "warn" : "success"} />
          <SLAChip label="Queue target"   value={`${queue?.firstResponseTargetMin}m / ${queue?.resolveTargetHours}h`} tone="neutral" />
          <SLAChip label="Customer tier"  value={t.customer.tier} tone="neutral" />
        </div>

        {/* Tabs */}
        <div className="flex items-center gap-1 mb-3 border-b border-divider-soft">
          <SheetTab label="Conversation" active={tab === "thread"}  onClick={() => setTab("thread")} count={t.messages.length} />
          <SheetTab label="Context"      active={tab === "context"} onClick={() => setTab("context")} />
          <div className="ml-auto flex items-center gap-1 pb-1">
            <Button variant="ghost" size="sm" leadingIcon="link">Link record</Button>
            <Button variant="ghost" size="sm" leadingIcon="tag">Tag</Button>
          </div>
        </div>

        {tab === "thread" && (
          <>
            {/* AI suggestion card */}
            {!["resolved", "closed"].includes(t.status) && (
              <div className="bg-brand-soft/40 border border-brand/20 rounded-lg p-3 mb-4">
                <div className="flex items-center gap-2 mb-2">
                  <Icon name="sparkle" size={13} className="text-brand" />
                  <span className="text-[11px] font-bold text-brand uppercase tracking-wider">Cenora AI · suggested reply</span>
                  <Badge variant="info">{t.queue === "technical" ? "97%" : "91%"} confidence</Badge>
                  <Button variant="ghost" size="sm" leadingIcon="refresh" className="ml-auto">Regenerate</Button>
                </div>
                <pre className="font-sans whitespace-pre-wrap text-[12px] text-ink-soft leading-relaxed">{aiDraft}</pre>
                <div className="flex items-center gap-2 mt-2 pt-2 border-t border-brand/15">
                  <span className="text-[10px] text-ink-mute">Cites:</span>
                  <button className="text-[10.5px] font-mono text-brand-mid hover:underline">{t.queue === "technical" ? "KB-POS-021" : "KB-BILL-007"}</button>
                  {t.linkedDocs?.slice(0, 2).map((d: any) => (
                    <button key={d.id} className="text-[10.5px] font-mono text-brand-mid hover:underline">{d.id}</button>
                  ))}
                  <Button variant="primary" size="sm" leadingIcon="send" className="ml-auto" onClick={() => setReply(aiDraft)}>Use draft</Button>
                </div>
              </div>
            )}

            {/* Message thread */}
            <div className="space-y-3">
              {t.messages.map((m: any, i: number) => (
                <MessageBubble key={i} m={m} />
              ))}
            </div>

            {/* Reply composer */}
            {!["resolved", "closed"].includes(t.status) && (
              <div className="mt-4 bg-surface border border-divider rounded-lg overflow-hidden">
                <div className="flex items-center gap-1 px-3 py-2 bg-cream/50 border-b border-divider-soft text-[10.5px] text-ink-mute">
                  <button className="px-2 py-0.5 rounded text-ink font-semibold bg-surface border border-divider">Reply</button>
                  <button className="px-2 py-0.5 rounded text-ink-soft hover:bg-surface/60">Internal note</button>
                  <button className="px-2 py-0.5 rounded text-ink-soft hover:bg-surface/60">Forward</button>
                  <span className="ml-auto text-ink-faint">via {(CH_LABEL as any)[t.channel]}</span>
                </div>
                <textarea
                  value={reply}
                  onChange={e => setReply(e.target.value)}
                  placeholder="Write a reply or paste the AI draft…"
                  rows={4}
                  className="w-full px-3 py-2.5 text-[12px] bg-surface border-0 focus:outline-none resize-none font-sans"
                />
                <div className="flex items-center gap-1 px-3 py-2 border-t border-divider-soft">
                  <Button variant="ghost" size="sm" leadingIcon="paperclip">Attach</Button>
                  <Button variant="ghost" size="sm" leadingIcon="bookmark">Insert macro</Button>
                  <Button variant="ghost" size="sm" leadingIcon="link">Cite KB</Button>
                  <div className="ml-auto flex items-center gap-1">
                    <Button variant="ghost" size="sm">Save draft</Button>
                    <Button variant="primary" size="sm" leadingIcon="send">Send + Resolve</Button>
                  </div>
                </div>
              </div>
            )}
          </>
        )}

        {tab === "context" && (
          <div className="space-y-4">
            <ContextSection title="Customer">
              <div className="flex items-center gap-3 px-3 py-2.5 bg-surface border border-divider rounded-md">
                <Avatar initials={t.customer.initials} tone={t.customer.tone} size="md" />
                <div className="flex-1 min-w-0">
                  <div className="text-[13px] font-semibold text-ink">{t.customer.name}</div>
                  <div className="text-[10.5px] text-ink-mute">{t.customer.contact} · {t.customer.tier}</div>
                </div>
                <Button variant="ghost" size="sm" leadingIcon="external">Open</Button>
              </div>
              <div className="grid grid-cols-3 gap-2 mt-2 text-[11px]">
                <MiniStat label="Open tickets" value="3" />
                <MiniStat label="MTD spend" value="$182k" />
                <MiniStat label="CSAT (90d)" value="95.2%" tone="success" />
              </div>
            </ContextSection>

            {t.linkedDocs && t.linkedDocs.length > 0 && (
              <ContextSection title="Linked records">
                <div className="space-y-1.5">
                  {t.linkedDocs.map((d: any) => (
                    <button key={d.id} className="w-full bg-surface border border-divider rounded-md px-3 py-2 text-left hover:border-divider-strong flex items-center gap-2">
                      <Icon name={d.type === "PO" ? "doc" : d.type === "IR" ? "download" : d.type === "INV" ? "receipt" : d.type === "RMA" ? "refresh" : d.type === "FJ" ? "truck" : "link"} size={12} className="text-ink-mute" />
                      <span className="text-[10.5px] uppercase tracking-wider text-ink-soft">{d.type}</span>
                      <span className="font-mono font-bold text-brand-mid text-[11.5px]">{d.id}</span>
                      <Icon name="arrow-right" size={11} className="text-ink-faint ml-auto" />
                    </button>
                  ))}
                </div>
              </ContextSection>
            )}

            <ContextSection title="Properties">
              <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
                <PropRow label="Status"    value={t.status} />
                <PropRow label="Priority"  value={t.priority} />
                <PropRow label="Queue"     value={queue?.label} />
                <PropRow label="Channel"   value={(CH_LABEL as any)[t.channel]} />
                <PropRow label="Entity"    value={t.entity} />
                <PropRow label="Assignee"  value={t.assignee?.name ?? "Unassigned"} />
                <PropRow label="Created"   value={t.createdAt} />
                <PropRow label="Updated"   value={t.lastUpdated} />
              </div>
            </ContextSection>

            <ContextSection title="Tags">
              <div className="flex flex-wrap gap-1.5">
                {t.tags.map((tag: string) => (
                  <span key={tag} className="px-2 py-0.5 bg-cream border border-divider rounded-full text-[10.5px] font-mono text-ink-soft">#{tag}</span>
                ))}
              </div>
            </ContextSection>
          </div>
        )}
      </div>

      <div className="px-5 py-3.5 border-t border-divider flex items-center gap-2 bg-cream">
        <Button variant="ghost" leadingIcon="user">Reassign</Button>
        <Button variant="ghost" leadingIcon="clock">Snooze</Button>
        <div className="ml-auto flex items-center gap-2">
          {!["resolved", "closed"].includes(t.status) ? (
            <>
              <Button variant="ghost" leadingIcon="pause">Set to pending</Button>
              <Button variant="primary" leadingIcon="check">Resolve</Button>
            </>
          ) : (
            <Button variant="primary" leadingIcon="refresh">Reopen</Button>
          )}
        </div>
      </div>
      </div>
    </>
  )
}

/* ─── Atoms ──────────────────────────────────────────────────────── */
function SLAChip({ label, value, tone }: { label: string; value: string; tone: "success" | "warn" | "danger" | "neutral" }) {
  const tones: any = {
    success: "text-success bg-success-soft/40 border-success/20",
    warn:    "text-warn bg-warn-soft/40 border-warn/20",
    danger:  "text-danger bg-danger-soft/40 border-danger/20",
    neutral: "text-ink-soft bg-cream border-divider-soft",
  }
  return (
    <div className={cn("border rounded-md px-2.5 py-1.5", tones[tone])}>
      <div className="text-[9px] uppercase tracking-wider font-bold opacity-70">{label}</div>
      <div className="text-[12px] font-mono font-semibold mt-0.5">{value}</div>
    </div>
  )
}

function SheetTab({ label, active, onClick, count }: any) {
  return (
    <button onClick={onClick}
      className={cn(
        "px-3 py-2 text-[11.5px] font-semibold border-b-2 transition-colors -mb-px",
        active ? "border-brand text-ink" : "border-transparent text-ink-mute hover:text-ink-soft"
      )}>
      {label}
      {count != null && <span className={cn("ml-1.5 font-mono", active ? "text-brand-mid" : "text-ink-faint")}>{count}</span>}
    </button>
  )
}

function MessageBubble({ m }: { m: any }) {
  const isCustomer = m.role === "customer"
  const isAI       = m.role === "ai"
  const isAgent    = m.role === "agent"
  const isInternal = m.internal === true

  return (
    <div className={cn("flex gap-3", isCustomer ? "" : "flex-row-reverse")}>
      <Avatar initials={m.initials} tone={m.tone} size="sm" />
      <div className={cn("flex-1 min-w-0 max-w-[88%]", isCustomer ? "" : "items-end")}>
        <div className={cn("flex items-baseline gap-2 mb-1", isCustomer ? "" : "justify-end")}>
          <span className="text-[11.5px] font-semibold text-ink">{m.from}</span>
          {isAI && <Badge variant="info">AI · internal</Badge>}
          {isInternal && !isAI && <Badge variant="warn">Internal note</Badge>}
          <span className="text-[10px] text-ink-faint">{m.time}</span>
        </div>
        <div className={cn(
          "px-3 py-2.5 rounded-lg text-[12.5px] leading-relaxed whitespace-pre-wrap",
          isInternal && isAI       ? "bg-info-soft/40 border border-info/20 text-ink-soft" :
          isInternal               ? "bg-warn-soft/40 border border-warn/30 text-ink-soft" :
          isCustomer               ? "bg-cream border border-divider text-ink"             :
                                    "bg-brand-soft/30 border border-brand/15 text-ink"
        )}>{m.body}</div>
      </div>
    </div>
  )
}

function ContextSection({ title, children }: any) {
  return (
    <section>
      <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">{title}</div>
      {children}
    </section>
  )
}

function MiniStat({ label, value, tone }: any) {
  return (
    <div className="bg-surface border border-divider rounded-md px-2.5 py-2 text-center">
      <div className="text-[9px] uppercase tracking-wider text-ink-mute font-bold">{label}</div>
      <div className={cn("text-[13px] font-mono font-semibold mt-0.5", tone === "success" && "text-success")}>{value}</div>
    </div>
  )
}

function PropRow({ label, value }: any) {
  return (
    <div className="flex items-center gap-3 px-3 py-2">
      <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute w-20 shrink-0">{label}</span>
      <span className="text-ink-soft flex-1">{value}</span>
    </div>
  )
}

// SANDBOX
;(globalThis as any).SupportTickets = SupportTickets

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/support/dashboard.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/support/dashboard.tsx
 * ----------------------------------------------------------------------------
 * Support — Dashboard. Hero KPIs, inbound volume trend, agent leaderboard,
 * queue load, AI deflection summary, recent ticket strip.
 * ============================================================================
 */

function SupportDashboard() {
  const { push } = useRouter()
  const k = SUPPORT_KPIS as any
  const trend  = SUPPORT_TREND_30D as number[]
  const queues = SUPPORT_QUEUES   as any[]
  const agents = (SUPPORT_AGENTS  as any[]).filter(a => a.initials !== "AI")
  const tickets = SUPPORT_TICKETS as any[]

  const recent = [...tickets]
    .filter(t => !["closed"].includes(t.status))
    .sort((a, b) => b.lastUpdated.localeCompare(a.lastUpdated))
    .slice(0, 5)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Support" }, { label: "Dashboard" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download" size="sm">Export</Button>
            <Button variant="primary" leadingIcon="arrow-right" size="sm" onClick={() => push("/support/tickets")}>Open inbox</Button>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          {/* Hero KPIs */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Open tickets" value={k.openTickets} sub={`${k.newToday} new · ${k.resolvedToday} resolved today`} accent="brand" />
            <KpiTile label="Median first response" value={k.firstResponseMedian} sub={`${k.firstResponseTrend} vs 7-day`} delta={k.firstResponseTrend} deltaDir={k.firstResponseTrendDir} accent="warn" />
            <KpiTile label="CSAT · 30d" value={`${k.csat}%`} sub="positive responses" delta={k.csatTrend} deltaDir={k.csatTrendDir} accent="success" />
            <KpiTile label="SLA compliance" value={`${k.slaCompliance}%`} sub={`${k.breached} breached · ${k.atRisk} at-risk now`} delta={k.slaTrend} deltaDir={k.slaTrendDir} accent="accent" />
          </div>

          <AICallout
            title="Cenora AI deflected 38 tickets today — 28.5% of inbound"
            body="High-confidence auto-resolves (≥92%) covered portal address changes, invoice-VAT lookups, and order-status checks. 4 conversations escalated back to a human after the customer flagged the answer as incomplete — review those before tomorrow's deflection threshold tune."
            matches={[
              { code: "KB-ACCT-014", name: "Article · Add shipping address — 142 deflections", pct: 98 },
              { code: "KB-BILL-007", name: "Article · VAT/EWT on PH invoices — 188 deflections", pct: 96 },
              { code: "KB-RET-002",  name: "Article · Open a warranty RMA — 91 deflections",      pct: 94 },
            ]}
            dismissLabel="Review escalations"
            proceedLabel="Tune thresholds"
          />

          {/* Volume + queue cards row */}
          <div className="grid grid-cols-[1.6fr_1fr] gap-4">
            <Card>
              <CardHeader>
                <CardTitle>Inbound volume · last 30 days</CardTitle>
                <div className="flex items-center gap-3 text-[10.5px] text-ink-mute">
                  <LegendDot color="brand" label="Inbound" />
                  <LegendDot color="success" label="AI deflected" />
                  <LegendDot color="warn" label="Breached" />
                </div>
              </CardHeader>
              <div className="p-4">
                <VolumeChart series={trend} />
                <div className="grid grid-cols-3 gap-3 mt-3 pt-3 border-t border-divider-soft text-[11px]">
                  <Sparkstat label="30d inbound" value={trend.reduce((s, n) => s + n, 0).toLocaleString()} sub="tickets" />
                  <Sparkstat label="30d deflected by AI" value="612" sub="≈21% of volume" tone="success" />
                  <Sparkstat label="30d breached" value="38" sub="2.0% of volume" tone="danger" />
                </div>
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>Queue load · right now</CardTitle>
                <button onClick={() => push("/support/queues")} className="text-[10.5px] text-brand-mid hover:underline">Open board →</button>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {queues.map(q => (
                  <button key={q.id} onClick={() => push("/support/queues")} className="w-full px-4 py-2.5 flex items-center gap-3 hover:bg-cream/40 text-left transition-colors">
                    <div className="flex-1 min-w-0">
                      <div className="text-[12px] font-semibold text-ink truncate">{q.label}</div>
                      <div className="text-[10px] text-ink-mute">{q.open} open · {q.pending} pending{q.breached ? <> · <span className="text-danger font-semibold">{q.breached} breached</span></> : null}</div>
                    </div>
                    <div className="text-right">
                      <div className={cn("text-[12px] font-mono font-bold", q.compliancePct >= 96 ? "text-success" : q.compliancePct >= 92 ? "text-warn" : "text-danger")}>
                        {q.compliancePct.toFixed(1)}%
                      </div>
                      <div className="text-[9px] text-ink-faint uppercase tracking-wider">SLA</div>
                    </div>
                  </button>
                ))}
              </div>
            </Card>
          </div>

          {/* Agent leaderboard + recent strip */}
          <div className="grid grid-cols-[1fr_1.4fr] gap-4">
            <Card>
              <CardHeader>
                <CardTitle>Agent leaderboard · today</CardTitle>
                <span className="text-[10.5px] text-ink-mute">{(SUPPORT_AGENTS as any[]).reduce((s, a) => s + a.todayResolved, 0)} resolved</span>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {[...agents].sort((a, b) => b.todayResolved - a.todayResolved).map((a, i) => (
                  <div key={a.initials} className="px-4 py-2.5 flex items-center gap-3">
                    <span className="font-mono text-[10.5px] font-bold text-ink-faint w-4">{i + 1}</span>
                    <Avatar initials={a.initials} tone={a.tone} size="sm" />
                    <div className="flex-1 min-w-0">
                      <div className="text-[12px] font-semibold text-ink truncate">{a.name}</div>
                      <div className="text-[10px] text-ink-mute truncate">{a.role}</div>
                    </div>
                    <div className="text-right">
                      <div className="text-[13px] font-mono font-bold text-ink">{a.todayResolved}</div>
                      <div className="text-[9px] text-ink-faint uppercase tracking-wider">resolved</div>
                    </div>
                    <div className="text-right">
                      <div className={cn("text-[11px] font-mono font-semibold", a.csatRolling >= 95 ? "text-success" : "text-ink-soft")}>
                        {a.csatRolling.toFixed(1)}%
                      </div>
                      <div className="text-[9px] text-ink-faint uppercase tracking-wider">CSAT</div>
                    </div>
                    <span className={cn(
                      "size-1.5 rounded-full ml-1",
                      a.status === "online" ? "bg-success" : a.status === "away" ? "bg-warn" : "bg-ink-faint"
                    )} />
                  </div>
                ))}
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>Recent activity</CardTitle>
                <button onClick={() => push("/support/tickets")} className="text-[10.5px] text-brand-mid hover:underline">All tickets →</button>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {recent.map(t => (
                  <button key={t.id} onClick={() => push("/support/tickets")} className="w-full px-4 py-2.5 flex items-center gap-3 hover:bg-cream/40 text-left transition-colors">
                    <Icon name={(CH_ICON as any)[t.channel]} size={13} className="text-ink-mute shrink-0" />
                    <Avatar initials={t.customer.initials} tone={t.customer.tone} size="xs" />
                    <div className="flex-1 min-w-0">
                      <div className="text-[12px] text-ink font-semibold truncate">{t.subject}</div>
                      <div className="text-[10px] text-ink-mute">
                        <span className="font-mono font-bold text-brand-mid">{t.id}</span> · {t.customer.name} · {t.lastUpdated.split(" · ")[1] ?? t.lastUpdated}
                      </div>
                    </div>
                    {t.priority === "P1" ? <Badge variant="danger">P1</Badge> : t.priority === "P2" ? <Badge variant="warn">P2</Badge> : <Badge variant="neutral">P3</Badge>}
                    {t.slaState === "breached" ? <Badge variant="danger" dot>Breached</Badge> : null}
                  </button>
                ))}
              </div>
            </Card>
          </div>

        </div>
      </div>
    </>
  )
}

/* ─── Bar/area chart ───────────────────────────────────────────────── */
function VolumeChart({ series }: { series: number[] }) {
  const max = Math.max(...series)
  const w = 880, h = 140
  const step = w / (series.length - 1)
  const pts = series.map((v, i) => [i * step, h - (v / max) * (h - 12) - 2] as [number, number])
  const linePath = pts.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(1)},${y.toFixed(1)}`).join(" ")
  const areaPath = `${linePath} L${w},${h} L0,${h} Z`

  return (
    <svg viewBox={`0 0 ${w} ${h + 18}`} className="w-full h-[150px]" preserveAspectRatio="none">
      <defs>
        <linearGradient id="areaFill" x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor="var(--brand)" stopOpacity="0.22" />
          <stop offset="100%" stopColor="var(--brand)" stopOpacity="0" />
        </linearGradient>
      </defs>
      {[0.25, 0.5, 0.75].map(f => (
        <line key={f} x1="0" x2={w} y1={h * f} y2={h * f} stroke="var(--divider-soft)" strokeWidth="1" />
      ))}
      <path d={areaPath} fill="url(#areaFill)" />
      <path d={linePath} fill="none" stroke="var(--brand)" strokeWidth="1.5" />
      {pts.map(([x, y], i) => (
        <circle key={i} cx={x} cy={y} r={i === pts.length - 1 ? 3 : 1.5} fill={i === pts.length - 1 ? "var(--accent-deep)" : "var(--brand)"} />
      ))}
      {/* X-axis labels — weeks */}
      {[0, 7, 14, 21, 28].map(i => (
        <text key={i} x={i * step} y={h + 12} textAnchor="middle" fontSize="9" fill="var(--ink-faint)" fontFamily="Arial">
          {i === 0 ? "30d ago" : i === 28 ? "today" : `−${30 - i}d`}
        </text>
      ))}
    </svg>
  )
}

function LegendDot({ color, label }: { color: string; label: string }) {
  const c: any = { brand: "bg-brand", success: "bg-success", warn: "bg-warn", danger: "bg-danger" }
  return (
    <span className="inline-flex items-center gap-1.5">
      <span className={cn("size-2 rounded-full", c[color] ?? "bg-ink")} />
      <span>{label}</span>
    </span>
  )
}

function Sparkstat({ label, value, sub, tone }: any) {
  const tones: any = { success: "text-success", danger: "text-danger", warn: "text-warn" }
  return (
    <div>
      <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">{label}</div>
      <div className={cn("font-serif text-[22px] leading-none mt-1.5", tones[tone] ?? "text-ink")}>{value}</div>
      <div className="text-[10px] text-ink-mute mt-1">{sub}</div>
    </div>
  )
}

// SANDBOX
;(globalThis as any).SupportDashboard = SupportDashboard

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/support/queues.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/support/queues.tsx
 * ----------------------------------------------------------------------------
 * Support — Queue Board. Each queue rendered as a column with its open
 * tickets stacked by priority. Hover a card to see SLA timer.
 * ============================================================================
 */

function SupportQueueBoard() {
  const { push } = useRouter()
  const queues  = SUPPORT_QUEUES   as any[]
  const tickets = SUPPORT_TICKETS  as any[]
  const [priority, setPriority] = React.useState<string>("all")

  let active = tickets.filter(t => !["resolved", "closed"].includes(t.status))
  if (priority !== "all") active = active.filter(t => t.priority === priority)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Support" }, { label: "Queues" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="settings" size="sm">Edit queues</Button>
            <Button variant="primary" leadingIcon="arrow-right" size="sm" onClick={() => push("/support/tickets")}>Open inbox</Button>
          </>
        }
      />
      <ActionBar
        title="Queue board"
        status={`${queues.length} queues · ${active.length} open tickets · ${active.filter(t => t.slaState === "breached").length} breached`}
        filters={
          <>
            <FilterChip active={priority === "all"} onClick={() => setPriority("all")}>All priorities</FilterChip>
            <FilterChip active={priority === "P1"}  onClick={() => setPriority("P1")}>P1 only</FilterChip>
            <FilterChip active={priority === "P2"}  onClick={() => setPriority("P2")}>P2 only</FilterChip>
            <FilterChip active={priority === "P3"}  onClick={() => setPriority("P3")}>P3 only</FilterChip>
          </>
        }
      />

      <div className="flex-1 overflow-x-auto overflow-y-auto bg-app">
        <div className="min-w-[1500px] p-6">
          <div className="grid grid-cols-6 gap-3">
            {queues.map(q => {
              const items = active.filter(t => t.queue === q.id)
              const sorted = items.sort((a, b) => {
                const order = { breached: 0, "at-risk": 1, "on-track": 2 } as any
                const d = order[a.slaState] - order[b.slaState]
                if (d !== 0) return d
                return a.priority.localeCompare(b.priority)
              })
              const breached = items.filter(t => t.slaState === "breached").length
              return (
                <div key={q.id} className="flex flex-col bg-cream/50 border border-divider rounded-lg overflow-hidden">
                  <div className="px-3 py-2.5 bg-surface border-b border-divider">
                    <div className="flex items-center gap-2">
                      <span className="text-[11.5px] font-bold text-ink truncate flex-1">{q.label}</span>
                      <Badge variant={breached > 0 ? "danger" : "neutral"}>{items.length}</Badge>
                    </div>
                    <div className="text-[10px] text-ink-mute mt-1 leading-tight">{q.blurb}</div>
                    <div className="flex items-center gap-3 mt-1.5 text-[10px]">
                      <span className="text-ink-faint font-mono">{q.firstResponseTargetMin}m / {q.resolveTargetHours}h</span>
                      <span className={cn("ml-auto font-mono font-bold", q.compliancePct >= 96 ? "text-success" : q.compliancePct >= 92 ? "text-warn" : "text-danger")}>{q.compliancePct.toFixed(1)}%</span>
                    </div>
                  </div>
                  <div className="flex-1 p-2 space-y-2 min-h-[420px]">
                    {sorted.length === 0 && (
                      <div className="text-center text-[10.5px] text-ink-faint italic py-8">No open tickets</div>
                    )}
                    {sorted.map(t => (
                      <QueueCard key={t.id} t={t} onClick={() => push("/support/tickets")} />
                    ))}
                  </div>
                </div>
              )
            })}
          </div>
        </div>
      </div>
    </>
  )
}

function QueueCard({ t, onClick }: any) {
  return (
    <button
      onClick={onClick}
      className={cn(
        "w-full bg-surface border rounded-md px-2.5 py-2 text-left transition-all hover:shadow-sm",
        t.slaState === "breached" ? "border-danger/30 ring-1 ring-danger/15" :
        t.slaState === "at-risk"  ? "border-warn/30" : "border-divider hover:border-divider-strong"
      )}>
      <div className="flex items-center gap-1.5 mb-1">
        {t.priority === "P1" ? <Badge variant="danger">P1</Badge> : t.priority === "P2" ? <Badge variant="warn">P2</Badge> : <Badge variant="neutral">P3</Badge>}
        <Icon name={(CH_ICON as any)[t.channel]} size={10} className="text-ink-faint" />
        <span className="font-mono text-[9.5px] text-ink-faint ml-auto">{t.id.slice(-4)}</span>
      </div>
      <div className="text-[11px] font-semibold text-ink leading-snug line-clamp-2">{t.subject}</div>
      <div className="flex items-center gap-1.5 mt-1.5">
        <Avatar initials={t.customer.initials} tone={t.customer.tone} size="xs" />
        <span className="text-[10px] text-ink-mute truncate flex-1">{t.customer.name}</span>
      </div>
      <div className="flex items-center gap-1.5 mt-1.5 pt-1.5 border-t border-divider-soft">
        {t.assignee ? (
          <Avatar initials={t.assignee.initials} tone={t.assignee.tone} size="xs" />
        ) : (
          <span className="size-4 rounded-full bg-cream border border-dashed border-ink-faint inline-flex items-center justify-center">
            <Icon name="user" size={8} className="text-ink-faint" />
          </span>
        )}
        <span className={cn(
          "text-[9.5px] font-mono ml-auto",
          t.slaState === "breached" ? "text-danger font-bold" : t.slaState === "at-risk" ? "text-warn font-semibold" : "text-ink-faint"
        )}>{t.resolveDueIn}</span>
      </div>
    </button>
  )
}

// SANDBOX
;(globalThis as any).SupportQueueBoard = SupportQueueBoard

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/support/kb.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/support/kb.tsx
 * ----------------------------------------------------------------------------
 * Support — Knowledge Base. Articles with deflection stats. Click to view
 * the article in a side panel.
 * ============================================================================
 */

function SupportKnowledgeBase() {
  const kb = SUPPORT_KB as any[]
  const [selected, setSelected] = React.useState<string | null>(null)
  const [search, setSearch] = React.useState("")

  let list = kb
  if (search.trim()) {
    const q = search.trim().toLowerCase()
    list = list.filter(a => a.title.toLowerCase().includes(q) || a.id.toLowerCase().includes(q) || a.category.toLowerCase().includes(q))
  }

  const totalDeflections = kb.reduce((s, a) => s + a.deflections30d, 0)
  const sel = selected ? kb.find(a => a.id === selected) : null

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Support" }, { label: "Knowledge Base" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download" size="sm">Export</Button>
            <Button variant="primary" leadingIcon="plus" size="sm">New article</Button>
          </>
        }
      />

      <ActionBar
        title="Knowledge Base"
        status={`${kb.length} published articles · ${totalDeflections.toLocaleString()} deflections in 30 days`}
        primary={
          <div className="relative">
            <Icon name="search" size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-ink-faint" />
            <input
              value={search}
              onChange={e => setSearch(e.target.value)}
              placeholder="Search articles…"
              className="w-[260px] h-7 pl-7 pr-2 text-[11.5px] bg-surface border border-divider rounded-md focus:outline-none focus:border-brand"
            />
          </div>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          {/* KPI row */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Published articles" value={kb.length} sub="across 6 categories" accent="brand" />
            <KpiTile label="Total views · 30d" value={kb.reduce((s, a) => s + a.views30d, 0).toLocaleString()} sub="customer-facing portal" accent="info" />
            <KpiTile label="AI deflections · 30d" value={totalDeflections.toLocaleString()} sub="≈21% of inbound" accent="success" />
            <KpiTile label="Avg helpful rating" value={`${(kb.reduce((s, a) => s + a.helpfulPct, 0) / kb.length).toFixed(1)}%`} sub="thumbs-up share" accent="accent" />
          </div>

          <div className="grid grid-cols-[1.5fr_1fr] gap-4 items-start">
            <Card>
              <CardHeader>
                <CardTitle>Articles</CardTitle>
                <span className="text-[10.5px] text-ink-mute">{list.length} shown</span>
              </CardHeader>
              <div className="grid grid-cols-[120px_2.5fr_140px_90px_90px_120px_42px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-b border-divider">
                <span>Article</span>
                <span>Title · category</span>
                <span>Owner</span>
                <span>Views</span>
                <span>Deflect.</span>
                <span>Helpful</span>
                <span></span>
              </div>
              {list.length === 0 && <div className="px-4 py-10 text-center text-[12px] text-ink-mute">No articles match.</div>}
              {list.map(a => (
                <button key={a.id} onClick={() => setSelected(a.id)}
                  className={cn(
                    "w-full grid grid-cols-[120px_2.5fr_140px_90px_90px_120px_42px] px-4 py-2.5 items-center text-[11.5px] border-b border-divider-soft hover:bg-cream/40 text-left transition-colors",
                    selected === a.id && "bg-brand-soft/40 hover:bg-brand-soft/40",
                  )}>
                  <span className="font-mono text-[10.5px] font-bold text-brand-mid">{a.id}</span>
                  <div>
                    <div className="text-ink font-semibold">{a.title}</div>
                    <div className="text-[10px] text-ink-mute mt-0.5">{a.category} · Updated {a.updated}</div>
                  </div>
                  <div className="flex items-center gap-2">
                    <Avatar initials={a.owner.initials} tone={a.owner.tone} size="xs" />
                    <span className="text-ink-soft truncate text-[11px]">{a.owner.name}</span>
                  </div>
                  <span className="font-mono text-ink-soft">{a.views30d.toLocaleString()}</span>
                  <span className="font-mono text-success font-semibold">{a.deflections30d}</span>
                  <div className="flex items-center gap-2">
                    <div className="flex-1 h-1.5 bg-cream-deep rounded-full overflow-hidden">
                      <div className={cn("h-full rounded-full", a.helpfulPct >= 90 ? "bg-success" : a.helpfulPct >= 80 ? "bg-warn" : "bg-danger")} style={{ width: `${a.helpfulPct}%` }} />
                    </div>
                    <span className="text-[10px] font-mono text-ink-soft">{a.helpfulPct}%</span>
                  </div>
                  <Icon name="chevron-right" size={12} className="text-ink-faint justify-self-end" />
                </button>
              ))}
            </Card>

            {/* Article preview */}
            <Card>
              <CardHeader>
                <CardTitle>{sel ? "Article preview" : "Macros & saved replies"}</CardTitle>
                {sel && <button onClick={() => setSelected(null)} className="text-[10.5px] text-ink-mute hover:text-ink">Close</button>}
              </CardHeader>
              {sel ? (
                <div className="p-4">
                  <div className="font-mono text-[10.5px] text-ink-mute">{sel.id}</div>
                  <h3 className="font-serif text-xl text-ink mt-1 leading-tight">{sel.title}</h3>
                  <div className="text-[11px] text-ink-mute mt-1.5 flex items-center gap-2">
                    <Avatar initials={sel.owner.initials} tone={sel.owner.tone} size="xs" />
                    <span>{sel.owner.name}</span>
                    <span>·</span><span>Updated {sel.updated}</span>
                  </div>
                  <div className="mt-3 grid grid-cols-3 gap-2">
                    <MiniKPI label="Views · 30d" value={sel.views30d.toLocaleString()} />
                    <MiniKPI label="Deflections" value={sel.deflections30d} tone="success" />
                    <MiniKPI label="Helpful" value={`${sel.helpfulPct}%`} />
                  </div>
                  <div className="mt-4 prose text-[12.5px] text-ink-soft leading-relaxed">
                    <p className="text-ink font-semibold">Overview</p>
                    <p>This article walks through resolving the issue end-to-end. Customers can self-serve via the portal, and Cenora AI cites this article for matched tickets.</p>
                    <p className="text-ink font-semibold mt-3">Quick steps</p>
                    <ol className="list-decimal pl-5 space-y-1">
                      <li>Identify the affected accounts and entities.</li>
                      <li>Apply the documented fix (linked below).</li>
                      <li>Confirm with the customer and request a CSAT response.</li>
                    </ol>
                  </div>
                  <div className="mt-3 flex items-center gap-2 pt-3 border-t border-divider-soft">
                    <Button variant="ghost" size="sm" leadingIcon="edit">Edit</Button>
                    <Button variant="ghost" size="sm" leadingIcon="copy">Insert as macro</Button>
                    <Button variant="primary" size="sm" leadingIcon="external" className="ml-auto">Open full article</Button>
                  </div>
                </div>
              ) : (
                <div className="divide-y divide-divider-soft">
                  {[
                    { name: "First-response acknowledgement",     uses: 412, last: "2m ago" },
                    { name: "Refund issued — please allow 3 BD",  uses: 188, last: "1h ago" },
                    { name: "Delivery delayed — customs paperwork", uses: 142, last: "3h ago" },
                    { name: "Escalate to Tier 2 (POS)",            uses: 96,  last: "today" },
                    { name: "Schedule a callback",                 uses: 84,  last: "today" },
                  ].map(m => (
                    <div key={m.name} className="px-4 py-2.5 flex items-center gap-3">
                      <Icon name="bookmark" size={12} className="text-ink-mute" />
                      <div className="flex-1 min-w-0">
                        <div className="text-[12px] text-ink font-semibold truncate">{m.name}</div>
                        <div className="text-[10px] text-ink-mute">Last used {m.last}</div>
                      </div>
                      <div className="text-right">
                        <div className="text-[12px] font-mono font-bold text-ink">{m.uses}</div>
                        <div className="text-[9px] text-ink-faint uppercase tracking-wider">uses</div>
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </Card>
          </div>

        </div>
      </div>
    </>
  )
}

function MiniKPI({ label, value, tone }: any) {
  const tones: any = { success: "text-success", danger: "text-danger" }
  return (
    <div className="bg-cream border border-divider-soft rounded-md px-2 py-1.5 text-center">
      <div className="text-[9px] uppercase tracking-wider text-ink-mute font-bold">{label}</div>
      <div className={cn("text-[14px] font-mono font-bold mt-0.5", tones[tone] ?? "text-ink")}>{value}</div>
    </div>
  )
}

// SANDBOX
;(globalThis as any).SupportKnowledgeBase = SupportKnowledgeBase

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/support/csat.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/support/csat.tsx
 * ----------------------------------------------------------------------------
 * Support — CSAT & Analytics. Star distribution, NPS, per-agent CSAT,
 * recent customer comments.
 * ============================================================================
 */

function SupportCSAT() {
  const dist  = SUPPORT_CSAT_DIST as number[]   /* % at 1..5 stars */
  const agents = (SUPPORT_AGENTS as any[]).filter(a => a.initials !== "AI")
  const k = SUPPORT_KPIS as any

  /* Synthesised NPS based on the distribution: promoters (5) − detractors (1+2) */
  const nps = (dist[4]) - (dist[0] + dist[1])

  /* Recent comments — synthesised from resolved tickets in the mock */
  const comments = [
    { id: "TKT-2026-1401", customer: { name: "Globe Telecom",    initials: "GT", tone: "amber" }, agent: { initials: "CB", tone: "blue" },  rating: 5, sentiment: "positive", text: "Carlos stayed on with us until past midnight to fix our DNS forwarding. Above and beyond.",       when: "Apr 16" },
    { id: "TKT-2026-1408", customer: { name: "Maybank PH",       initials: "MB", tone: "blue"  }, agent: { initials: "SM", tone: "amber" }, rating: 5, sentiment: "positive", text: "Smooth signatory update. Fastest KYC change I have ever processed with a vendor.",              when: "Apr 17" },
    { id: "TKT-2026-1404", customer: { name: "Filinvest Land",   initials: "FL", tone: "teal"  }, agent: { initials: "AI", tone: "ink"   }, rating: 5, sentiment: "positive", text: "AI answered in seconds — exactly what I needed.",                                                when: "Apr 18" },
    { id: "TKT-2026-1402", customer: { name: "Metro Retail",     initials: "MR", tone: "ink"   }, agent: { initials: "OP", tone: "amber" }, rating: 3, sentiment: "neutral",  text: "Backup vehicle did arrive but the no-show is a pattern this quarter.",                          when: "Apr 16" },
    { id: "TKT-2026-1411", customer: { name: "Reliance Indust.", initials: "RI", tone: "blue"  }, agent: { initials: "NT", tone: "teal"  }, rating: 4, sentiment: "positive", text: "Customs delay was not your fault — appreciated the transparency.",                              when: "Apr 18" },
  ]

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Support" }, { label: "CSAT & Analytics" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download" size="sm">Export</Button>
            <Button variant="ghost" leadingIcon="settings" size="sm">Edit survey</Button>
          </>
        }
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1500px] mx-auto p-6 space-y-4">

          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="CSAT · 30d"          value={`${k.csat}%`} sub="positive responses (4-5★)" delta={k.csatTrend} deltaDir={k.csatTrendDir} accent="success" />
            <KpiTile label="NPS · 30d"           value={nps}          sub={`${dist[4]}% promoters · ${dist[0] + dist[1]}% detractors`}              accent="brand" />
            <KpiTile label="Survey response rate" value="42.6%"       sub="3,420 sent · 1,457 returned"                                              accent="info" />
            <KpiTile label="Avg resolution"      value="6h 14m"       sub="−18 min vs 30d ago" delta="−18m" deltaDir="up"                            accent="accent" />
          </div>

          <div className="grid grid-cols-[1.2fr_1fr] gap-4">
            <Card>
              <CardHeader>
                <CardTitle>Rating distribution · last 30 days</CardTitle>
                <span className="text-[10.5px] text-ink-mute">1,457 responses</span>
              </CardHeader>
              <div className="p-5 space-y-2.5">
                {dist.slice().reverse().map((pct, i) => {
                  const stars = 5 - i
                  const tone = stars >= 4 ? "bg-success" : stars === 3 ? "bg-warn" : "bg-danger"
                  return (
                    <div key={stars} className="flex items-center gap-3">
                      <span className="text-[11.5px] text-ink-soft w-12">{"★".repeat(stars)}<span className="text-ink-faint">{"☆".repeat(5 - stars)}</span></span>
                      <div className="flex-1 h-5 bg-cream-deep/60 rounded">
                        <div className={cn("h-full rounded transition-all", tone)} style={{ width: `${pct}%` }} />
                      </div>
                      <span className="font-mono text-[11.5px] text-ink-soft w-10 text-right">{pct}%</span>
                      <span className="font-mono text-[10px] text-ink-faint w-14 text-right">{Math.round(1457 * pct / 100)}</span>
                    </div>
                  )
                })}
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>Agent CSAT · rolling 30d</CardTitle>
              </CardHeader>
              <div className="divide-y divide-divider-soft">
                {[...agents].sort((a, b) => b.csatRolling - a.csatRolling).map(a => (
                  <div key={a.initials} className="px-4 py-2.5 flex items-center gap-3">
                    <Avatar initials={a.initials} tone={a.tone} size="sm" />
                    <div className="flex-1 min-w-0">
                      <div className="text-[12px] font-semibold text-ink truncate">{a.name}</div>
                      <div className="text-[10px] text-ink-mute truncate">{a.role}</div>
                    </div>
                    <div className="w-28">
                      <div className="h-1.5 bg-cream-deep rounded-full overflow-hidden">
                        <div className={cn("h-full rounded-full", a.csatRolling >= 95 ? "bg-success" : a.csatRolling >= 90 ? "bg-warn" : "bg-danger")} style={{ width: `${a.csatRolling}%` }} />
                      </div>
                    </div>
                    <div className={cn("text-[12px] font-mono font-bold w-14 text-right", a.csatRolling >= 95 ? "text-success" : "text-ink-soft")}>
                      {a.csatRolling.toFixed(1)}%
                    </div>
                  </div>
                ))}
              </div>
            </Card>
          </div>

          <Card>
            <CardHeader>
              <CardTitle>Recent customer comments</CardTitle>
              <span className="text-[10.5px] text-ink-mute">Top 5 most recent</span>
            </CardHeader>
            <div className="divide-y divide-divider-soft">
              {comments.map(c => (
                <div key={c.id} className="px-4 py-3 flex items-start gap-3">
                  <Avatar initials={c.customer.initials} tone={c.customer.tone} size="sm" />
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2 mb-1">
                      <span className="text-[12px] font-semibold text-ink">{c.customer.name}</span>
                      <span className="text-[10px] text-ink-faint">·</span>
                      <span className="text-[10px] text-ink-faint">{c.when}</span>
                      <span className="text-[10px] text-ink-faint">·</span>
                      <span className="font-mono text-[10px] text-brand-mid">{c.id}</span>
                      <span className="ml-auto text-warn text-[12px] leading-none">{"★".repeat(c.rating)}<span className="text-ink-faint">{"☆".repeat(5 - c.rating)}</span></span>
                    </div>
                    <div className="text-[12.5px] text-ink-soft leading-relaxed">"{c.text}"</div>
                    <div className="flex items-center gap-1.5 mt-1.5">
                      <Avatar initials={c.agent.initials} tone={c.agent.tone} size="xs" />
                      <span className="text-[10px] text-ink-mute">assigned</span>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </Card>

        </div>
      </div>
    </>
  )
}

// SANDBOX
;(globalThis as any).SupportCSAT = SupportCSAT

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/comms/comms.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/comms/comms.tsx
 * ----------------------------------------------------------------------------
 * Cenora Communications module — Teams-like, reworked for ERP context.
 *
 * Four exports:
 *
 *   <CommsModule />     Full-page module — 3-pane shell (rail / list / conversation)
 *                       Sections: Activity · Chat · Channels · Calls · Meetings
 *   <CommsPanel />      Global slide-out (coexists with AI panel — auto-offsets
 *                       when AI is also open). Dockable right / left / bottom /
 *                       floating. Quick-access chat from anywhere.
 *   <CommsActiveCall /> Full-screen active-call mock (video tiles, captions,
 *                       AI live-summary, raise-hand, transcript).
 *   <DocChatPill />     Embeddable per-document chat (PO form, IRQ approval,
 *                       Opp drawer, etc). Floating button → inline thread panel.
 *
 * Reads: COMMS_USERS, COMMS_GROUPS, COMMS_MESSAGES, COMMS_DMS, COMMS_DM_MESSAGES,
 *        COMMS_CALL_HISTORY, COMMS_MEETINGS, ACTIVE_CALL_PARTICIPANTS,
 *        ACTIVE_CALL_TRANSCRIPT, commsUser, commsMessages, commsDMMessages
 * ============================================================================
 */

declare const COMMS_USERS: any[]
declare const COMMS_GROUPS: any[]
declare const COMMS_DMS: any[]
declare const COMMS_CALL_HISTORY: any[]
declare const COMMS_MEETINGS: any[]
declare const ACTIVE_CALL_PARTICIPANTS: any[]
declare const ACTIVE_CALL_TRANSCRIPT: any[]
declare function commsUser(id: string): any
declare function commsMessages(id: string): any[]
declare function commsDMMessages(id: string): any[]

/* ─── PRESENCE DOT ──────────────────────────────────────────────────── */
const PRESENCE_COLOR: Record<string, string> = {
  available: "bg-success",
  busy:      "bg-danger",
  dnd:       "bg-danger",
  focus:     "bg-info",
  away:      "bg-warn",
  offline:   "bg-ink-mute",
}
const PRESENCE_LABEL: Record<string, string> = {
  available: "Available", busy: "Busy", dnd: "Do not disturb", focus: "Focus mode", away: "Away", offline: "Offline",
}

function PresenceDot({ status, size = 8, ring = true }: { status: string; size?: number; ring?: boolean }) {
  return (
    <span
      title={PRESENCE_LABEL[status]}
      className={cn(
        "inline-block rounded-full shrink-0",
        PRESENCE_COLOR[status] ?? "bg-ink-mute",
        ring && "ring-2 ring-surface",
        status === "focus" && "ring-info-soft"
      )}
      style={{ width: size, height: size }}
    />
  )
}

function UserAvatar({ user, size = 32, showPresence = true }: { user: any; size?: number; showPresence?: boolean }) {
  if (!user) return null
  return (
    <div className="relative shrink-0" style={{ width: size, height: size }}>
      <Avatar initials={user.initials} tone={user.tone ?? "sand"} size={size <= 24 ? "sm" : size <= 36 ? "md" : "lg"} />
      {showPresence && (
        <span className="absolute -bottom-0.5 -right-0.5">
          <PresenceDot status={user.presence ?? "available"} size={size <= 24 ? 7 : 9} />
        </span>
      )}
    </div>
  )
}

/* ─── DOCTYPE LINK BADGE ────────────────────────────────────────────── */
const DOCTYPE_META: Record<string, { icon: string; label: string; tone: string }> = {
  po:          { icon: "doc",          label: "PO",          tone: "text-brand bg-brand-soft" },
  irq:         { icon: "tag",          label: "IRQ",         tone: "text-warn bg-warn-soft" },
  bill:        { icon: "receipt",      label: "Bill",        tone: "text-success bg-success-soft" },
  wo:          { icon: "list",         label: "WO",          tone: "text-info bg-info-soft" },
  "field-job": { icon: "truck",        label: "Job",         tone: "text-accent-deep bg-accent-soft" },
  "bank-recon":{ icon: "scan",         label: "Bank rec",    tone: "text-info bg-info-soft" },
  opp:         { icon: "trending-up",  label: "Opp",         tone: "text-success bg-success-soft" },
  ar:          { icon: "wallet",       label: "AR",          tone: "text-danger bg-danger-soft" },
}
const DOCTYPE_ROUTE: Record<string, string> = {
  po: "/procurement/po",
  irq: "/products/irq",
  bill: "/finance/ap",
  wo: "/manufacturing/work-orders",
  "field-job": "/field/dispatch",
  "bank-recon": "/finance/bank-recon",
  opp: "/crm/opportunities",
  ar: "/finance/ar",
}
function DocLinkBadge({ doc }: { doc: { kind: string; id: string; label: string } }) {
  const { push } = useRouter()
  const meta = DOCTYPE_META[doc.kind] ?? { icon: "link", label: doc.kind, tone: "text-ink-mute bg-cream-deep" }
  const dest = DOCTYPE_ROUTE[doc.kind]
  return (
    <button
      onClick={() => dest ? push(dest) : (window as any).cenoraToast?.(`Open ${doc.label}`, { sub: `No ${meta.label} workspace in this build.` })}
      className={cn("inline-flex items-center gap-1.5 h-6 px-2 rounded-md border border-divider hover:border-brand-mid transition-colors text-[11px] font-semibold", meta.tone)}>
      <Icon name={meta.icon as any} size={11} />
      <span>{doc.label}</span>
      <Icon name="external" size={10} className="opacity-60" />
    </button>
  )
}

/* ─── REACTIONS / ATTACHMENTS ───────────────────────────────────────── */
function ReactionStrip({ reactions }: { reactions: { emoji: string; count: number; mine?: boolean }[] }) {
  if (!reactions?.length) return null
  return (
    <div className="flex flex-wrap gap-1 mt-1.5">
      {reactions.map((r, i) => (
        <button key={i}
          className={cn(
            "inline-flex items-center gap-1 h-5 px-1.5 rounded-full border text-[10.5px] font-semibold transition-colors",
            r.mine ? "bg-brand-soft border-brand-mid text-brand" : "bg-surface border-divider text-ink-soft hover:border-brand-mid"
          )}>
          <span>{r.emoji}</span>
          <span>{r.count}</span>
        </button>
      ))}
    </div>
  )
}

function AttachmentChip({ a }: { a: { kind: string; name: string; sub?: string } }) {
  return (
    <div className="mt-2 flex items-center gap-2 p-2 rounded-md border border-divider bg-cream hover:border-brand-mid transition-colors max-w-[320px]">
      <span className="size-7 rounded bg-brand-soft text-brand flex items-center justify-center shrink-0">
        <Icon name={a.kind === "image" ? "image" : "doc"} size={13} />
      </span>
      <div className="flex-1 min-w-0">
        <div className="text-[11.5px] font-semibold text-ink truncate">{a.name}</div>
        {a.sub && <div className="text-[10px] text-ink-mute truncate">{a.sub}</div>}
      </div>
      <button className="size-6 inline-flex items-center justify-center text-ink-mute hover:text-ink hover:bg-cream-deep rounded">
        <Icon name="download" size={11} />
      </button>
    </div>
  )
}

/* ─── MESSAGE BUBBLE ────────────────────────────────────────────────── */
function CommsMessage({ msg, dense = false }: { msg: any; dense?: boolean }) {
  const user = commsUser(msg.authorId)
  const isAI = msg.authorId === "u-bot-ai"
  const isSummary = !!msg.aiSummary

  return (
    <div className={cn(
      "group flex gap-2.5 px-4 py-2 hover:bg-cream/60 transition-colors",
      msg.pinned && "bg-warn-soft/30",
      isSummary && "bg-accent-soft/40 border-l-2 border-accent",
    )}>
      <UserAvatar user={user} size={dense ? 28 : 36} showPresence={!isAI} />
      <div className="flex-1 min-w-0">
        <div className="flex items-baseline gap-1.5">
          <span className={cn("font-semibold text-[12.5px]", isAI ? "text-accent-deep" : "text-ink")}>
            {user.name}
            {isAI && <span className="ml-1 text-[9px] font-bold uppercase tracking-wider bg-accent-soft text-accent-deep px-1 py-px rounded">AI</span>}
          </span>
          {!isAI && <span className="text-[10px] text-ink-mute">{user.role}</span>}
          <span className="text-[10px] text-ink-mute ml-auto">{msg.time}</span>
        </div>
        {msg.pinned && (
          <div className="text-[9.5px] font-bold uppercase tracking-wider text-warn mb-0.5 inline-flex items-center gap-1">
            <Icon name="bookmark" size={9} /> Pinned
          </div>
        )}
        <div className={cn(
          "text-[13px] leading-relaxed whitespace-pre-wrap mt-0.5",
          isAI ? "text-ink-soft" : "text-ink"
        )} dangerouslySetInnerHTML={{ __html: msg.text.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>").replace(/@(\w+)/g, '<span class="bg-info-soft text-info font-semibold px-1 rounded">@$1</span>') }} />
        {msg.linkedDoc && <div className="mt-2"><DocLinkBadge doc={msg.linkedDoc} /></div>}
        {msg.attachments?.map((a: any, i: number) => <AttachmentChip key={i} a={a} />)}
        <ReactionStrip reactions={msg.reactions ?? []} />
      </div>
      <div className="opacity-0 group-hover:opacity-100 flex items-start gap-0.5 transition-opacity">
        <button title="React" className="size-7 rounded-md text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center">😊</button>
        <button title="Reply" className="size-7 rounded-md text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center"><Icon name="message" size={12} /></button>
        <button title="More" className="size-7 rounded-md text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center"><Icon name="more" size={12} /></button>
      </div>
    </div>
  )
}

/* ─── COMPOSER ──────────────────────────────────────────────────────── */
function MessageComposer({ placeholder, compact = false }: { placeholder: string; compact?: boolean }) {
  const [value, setValue] = React.useState("")
  return (
    <div className={cn("border-t border-divider bg-surface p-3", compact && "p-2")}>
      <div className="border border-divider rounded-lg bg-cream/40 focus-within:border-brand-mid transition-colors">
        <textarea
          value={value}
          onChange={e => setValue(e.target.value)}
          placeholder={placeholder}
          rows={compact ? 1 : 2}
          className="w-full bg-transparent outline-none resize-none px-3 py-2 text-[13px] placeholder:text-ink-mute"
        />
        <div className="flex items-center gap-0.5 px-2 pb-1.5">
          <ComposerBtn icon="paperclip" title="Attach" />
          <ComposerBtn icon="link" title="Link a Cenora record" />
          <ComposerBtn icon="image" title="GIF / image" />
          <ComposerBtn icon="sparkle" title="Ask @cenora" tone="accent" />
          <ComposerBtn icon="mic" title="Voice memo" />
          <div className="ml-auto flex items-center gap-1">
            <ComposerBtn icon="phone" title="Voice call" />
            <ComposerBtn icon="video" title="Video call" />
            <button
              disabled={!value.trim()}
              onClick={() => { (window as any).cenoraToast?.("Sent"); setValue("") }}
              className={cn(
                "h-7 px-3 rounded-md text-[11px] font-bold inline-flex items-center gap-1.5 transition-colors",
                value.trim() ? "bg-brand text-white hover:bg-brand-hover" : "bg-cream-deep text-ink-mute cursor-not-allowed"
              )}>
              Send
              <Icon name="send" size={11} strokeWidth={2.5} />
            </button>
          </div>
        </div>
      </div>
    </div>
  )
}

function ComposerBtn({ icon, title, tone = "default" }: { icon: string; title: string; tone?: "default" | "accent" }) {
  return (
    <button
      title={title}
      onClick={() => (window as any).cenoraToast?.(title)}
      className={cn(
        "size-7 rounded-md inline-flex items-center justify-center transition-colors",
        tone === "accent" ? "text-accent-deep hover:bg-accent-soft" : "text-ink-mute hover:bg-cream-deep hover:text-ink"
      )}>
      <Icon name={icon as any} size={13} />
    </button>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * FULL-PAGE MODULE — <CommsModule />
 * ════════════════════════════════════════════════════════════════════════ */
interface CommsViewState {
  section: "activity" | "chat" | "channels" | "calls" | "meetings"
  channelId?: string
  dmId?: string
  callTab?: "all" | "missed" | "recorded"
  meetingTab?: "upcoming" | "live" | "past"
}

function CommsModule() {
  const shell = useShell()
  const [v, setV] = React.useState<CommsViewState>({ section: "channels", channelId: "sc-po-overdue" })
  const [tweaks] = React.useState(() => readCommsTweaks())

  return (
    <>
      <TopBar breadcrumb={[{ label: "Communications" }]} />
      <ActionBar
        title="Communications"
        status={<>
          <strong className="text-ink">{COMMS_GROUPS.length} groups</strong> · {countAllChannels()} channels · {COMMS_DMS.length} direct chats
          <span className="mx-1.5">·</span>
          <span className="inline-flex items-center gap-1">
            <PresenceDot status={shell.role.id === "executive" ? "focus" : "available"} size={7} ring={false} />
            You are <strong className="text-ink ml-1">{shell.role.id === "executive" ? "in Focus mode" : "available"}</strong>
          </span>
        </>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New chat</Button>}
        secondary={<>
          <Button variant="ghost" leadingIcon="video" size="sm" onClick={() => (window as any).__cenora_open_active_call?.()}>Start meeting</Button>
          <Button variant="ghost" leadingIcon="users" size="sm">New group</Button>
        </>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Summarize my day</Button>}
      />

      <div className="flex-1 overflow-hidden bg-app flex">
        {/* RAIL */}
        <CommsRail value={v} onChange={setV} compact={tweaks.rail === "compact"} />
        {/* LIST */}
        <CommsList value={v} onChange={setV} />
        {/* CONVERSATION */}
        <div className="flex-1 min-w-0 flex flex-col bg-surface border-l border-divider overflow-hidden">
          <CommsConversation value={v} flat={tweaks.feed === "flat"} />
        </div>
      </div>
    </>
  )
}

function countAllChannels() {
  let n = 0
  for (const g of COMMS_GROUPS) {
    for (const c of g.channels) {
      n++
      if (c.subChannels) n += c.subChannels.length
    }
  }
  return n
}

/* ─── LEFT RAIL ─────────────────────────────────────────────────────── */
function CommsRail({ value, onChange, compact }: { value: CommsViewState; onChange: (v: CommsViewState) => void; compact: boolean }) {
  const sections: { key: CommsViewState["section"]; icon: string; label: string; badge?: number }[] = [
    { key: "activity", icon: "activity", label: "Activity", badge: 7 },
    { key: "chat",     icon: "message",  label: "Chat",     badge: COMMS_DMS.reduce((s, d) => s + (d.unread ?? 0), 0) },
    { key: "channels", icon: "layers",   label: "Channels", badge: COMMS_GROUPS.reduce((s, g) => s + g.channels.reduce((c: number, x: any) => c + (x.unread ?? 0) + (x.subChannels ?? []).reduce((y: number, z: any) => y + (z.unread ?? 0), 0), 0), 0) },
    { key: "calls",    icon: "phone",    label: "Calls",    badge: COMMS_CALL_HISTORY.filter(c => c.direction === "missed").length },
    { key: "meetings", icon: "video",    label: "Meetings" },
  ]

  return (
    <aside className={cn("shrink-0 bg-brand text-white/70 flex flex-col items-center py-3 gap-1", compact ? "w-[64px]" : "w-[80px]")}>
      {sections.map(s => {
        const active = value.section === s.key
        return (
          <button key={s.key} onClick={() => onChange({ section: s.key })}
            className={cn(
              "w-[56px] py-2 rounded-md flex flex-col items-center gap-1 transition-colors relative",
              active ? "bg-white/15 text-white" : "hover:bg-white/8 hover:text-white"
            )}>
            <Icon name={s.icon as any} size={18} />
            {!compact && <span className="text-[10px] font-semibold">{s.label}</span>}
            {s.badge != null && s.badge > 0 && (
              <span className="absolute top-1 right-2 min-w-[16px] h-[16px] px-1 rounded-full bg-accent text-brand text-[9px] font-bold flex items-center justify-center">
                {s.badge}
              </span>
            )}
          </button>
        )
      })}
      <div className="flex-1" />
      <button title="New chat or call"
        onClick={() => (window as any).cenoraToast?.("New chat")}
        className="size-10 rounded-full bg-accent text-brand hover:bg-accent/90 flex items-center justify-center mb-1">
        <Icon name="plus" size={18} strokeWidth={2.5} />
      </button>
    </aside>
  )
}

/* ─── MIDDLE LIST ───────────────────────────────────────────────────── */
function CommsList({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  if (value.section === "channels") return <ChannelsList value={value} onChange={onChange} />
  if (value.section === "chat")     return <ChatsList value={value} onChange={onChange} />
  if (value.section === "calls")    return <CallsList value={value} onChange={onChange} />
  if (value.section === "meetings") return <MeetingsList value={value} onChange={onChange} />
  return <ActivityList />
}

/* Channels (3-deep tree: groups → channels → sub-channels) */
function ChannelsList({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  const [expanded, setExpanded] = React.useState<Record<string, boolean>>(() => ({
    "g-procurement": true, "g-finance": true, "g-hvph": true,
    "c-proc-pos": true, "c-fin-close": true,
  }))
  const toggle = (id: string) => setExpanded(e => ({ ...e, [id]: !e[id] }))

  return (
    <aside className="w-[300px] shrink-0 bg-cream border-l border-divider overflow-y-auto">
      <div className="px-3 py-3 sticky top-0 bg-cream z-10 border-b border-divider">
        <div className="flex items-center justify-between mb-2">
          <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">Groups &amp; Channels</div>
          <button title="New group"
            onClick={() => (window as any).cenoraToast?.("New group")}
            className="size-6 rounded text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center">
            <Icon name="plus" size={12} />
          </button>
        </div>
        <SearchBox placeholder="Search channels…" />
      </div>
      <div className="py-1">
        {COMMS_GROUPS.map(g => {
          const open = !!expanded[g.id]
          const groupUnread = g.channels.reduce((s: number, c: any) => s + (c.unread ?? 0) + (c.subChannels ?? []).reduce((y: number, z: any) => y + (z.unread ?? 0), 0), 0)
          return (
            <div key={g.id}>
              <button onClick={() => toggle(g.id)}
                className="w-full flex items-center gap-2 px-3 py-2 hover:bg-cream-deep group">
                <Icon name={open ? "chevron-down" : "chevron-right"} size={10} className="text-ink-mute" strokeWidth={2.5} />
                <span className={cn("size-6 rounded text-white text-[10px] font-bold inline-flex items-center justify-center shrink-0", g.color)}>{g.initials}</span>
                <div className="flex-1 min-w-0 text-left">
                  <div className="text-[12px] font-semibold text-ink truncate">{g.name}</div>
                  <div className="text-[10px] text-ink-mute truncate">{g.members} members · {g.kind}</div>
                </div>
                {groupUnread > 0 && (
                  <span className="bg-brand text-white text-[10px] font-bold px-1.5 py-px rounded-full">{groupUnread}</span>
                )}
              </button>
              {open && (
                <div className="pb-1">
                  {g.channels.map((c: any) => {
                    const subOpen = !!expanded[c.id]
                    const selected = value.channelId === c.id || c.subChannels?.some((s: any) => s.id === value.channelId)
                    return (
                      <div key={c.id}>
                        <div className={cn(
                          "flex items-center group cursor-pointer pl-8 pr-2 py-1.5 hover:bg-cream-deep",
                          selected && !c.subChannels?.length && "bg-brand-soft",
                        )}>
                          {c.subChannels?.length ? (
                            <button onClick={() => toggle(c.id)} className="size-4 inline-flex items-center justify-center text-ink-mute mr-1">
                              <Icon name={subOpen ? "chevron-down" : "chevron-right"} size={9} strokeWidth={2.5} />
                            </button>
                          ) : <span className="w-5" />}
                          <button onClick={() => onChange({ section: "channels", channelId: c.id })}
                            className="flex items-center gap-1.5 flex-1 min-w-0 text-left">
                            <span className="text-ink-mute font-mono text-[12px] leading-none">#</span>
                            <span className={cn("text-[12px] truncate", selected ? "text-brand font-semibold" : "text-ink-soft", c.unread && "font-semibold text-ink")}>{c.name}</span>
                            {c.private && <Icon name="lock" size={9} className="text-ink-mute ml-auto" />}
                          </button>
                          {c.unread != null && c.unread > 0 && (
                            <span className="bg-brand text-white text-[9.5px] font-bold px-1.5 py-px rounded-full ml-1.5">{c.unread}</span>
                          )}
                        </div>
                        {subOpen && c.subChannels && (
                          <div>
                            {c.subChannels.map((s: any) => {
                              const sActive = value.channelId === s.id
                              return (
                                <button key={s.id}
                                  onClick={() => onChange({ section: "channels", channelId: s.id })}
                                  className={cn(
                                    "w-full flex items-center gap-1.5 pl-[52px] pr-2 py-1.5 text-left hover:bg-cream-deep",
                                    sActive && "bg-brand-soft"
                                  )}>
                                  <span className="text-ink-faint text-[10px]">└</span>
                                  <span className="text-ink-faint font-mono text-[11px] leading-none">#</span>
                                  <span className={cn(
                                    "text-[11.5px] truncate flex-1",
                                    sActive ? "text-brand font-semibold" : "text-ink-mute",
                                    s.unread && "font-semibold text-ink"
                                  )}>{s.name}</span>
                                  {s.unread != null && s.unread > 0 && (
                                    <span className="bg-brand text-white text-[9px] font-bold px-1.5 py-px rounded-full">{s.unread}</span>
                                  )}
                                </button>
                              )
                            })}
                            <button
                              onClick={() => (window as any).cenoraToast?.("Add sub-channel")}
                              className="w-full pl-[52px] py-1 text-left text-[10.5px] text-ink-mute hover:text-brand inline-flex items-center gap-1">
                              <Icon name="plus" size={9} /> Add sub-channel
                            </button>
                          </div>
                        )}
                      </div>
                    )
                  })}
                  <button
                    onClick={() => (window as any).cenoraToast?.("New channel")}
                    className="w-full pl-8 py-1.5 text-left text-[11px] text-ink-mute hover:text-brand inline-flex items-center gap-1.5">
                    <Icon name="plus" size={10} /> New channel
                  </button>
                </div>
              )}
            </div>
          )
        })}
      </div>
    </aside>
  )
}

/* DM list */
function ChatsList({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  return (
    <aside className="w-[300px] shrink-0 bg-cream border-l border-divider overflow-y-auto">
      <div className="px-3 py-3 sticky top-0 bg-cream z-10 border-b border-divider">
        <div className="flex items-center justify-between mb-2">
          <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">Direct Messages</div>
          <button title="New chat" onClick={() => (window as any).cenoraToast?.("New chat")} className="size-6 rounded text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center">
            <Icon name="plus" size={12} />
          </button>
        </div>
        <SearchBox placeholder="Search people, chats…" />
      </div>
      <div className="py-1">
        {COMMS_DMS.map(dm => {
          const ppl = dm.participantIds.map(commsUser)
          const active = value.dmId === dm.id
          const isGroup = ppl.length > 1
          const display = isGroup ? ppl.map((p: any) => p.name.split(" ")[0]).join(", ") : ppl[0].name
          return (
            <button key={dm.id}
              onClick={() => onChange({ section: "chat", dmId: dm.id })}
              className={cn(
                "w-full flex items-center gap-2.5 px-3 py-2 text-left hover:bg-cream-deep border-l-2 transition-colors",
                active ? "bg-brand-soft border-brand" : "border-transparent"
              )}>
              {isGroup ? (
                <div className="size-9 rounded-md bg-info-soft text-info inline-flex items-center justify-center shrink-0">
                  <Icon name="users" size={14} />
                </div>
              ) : <UserAvatar user={ppl[0]} size={36} />}
              <div className="flex-1 min-w-0">
                <div className="flex items-center gap-1.5">
                  <div className={cn("text-[12.5px] truncate", active ? "text-brand font-semibold" : dm.unread ? "text-ink font-semibold" : "text-ink-soft")}>
                    {display}
                  </div>
                  {dm.pinned && <Icon name="bookmark" size={9} className="text-warn shrink-0" />}
                  {dm.muted && <Icon name="bell" size={9} className="text-ink-faint shrink-0" />}
                </div>
                <div className="flex items-center gap-1.5 text-[10.5px] text-ink-mute mt-0.5">
                  <span className="truncate flex-1">{dm.lastMsgPreview}</span>
                  <span className="shrink-0">{dm.lastMsgTime}</span>
                </div>
              </div>
              {dm.unread != null && dm.unread > 0 && (
                <span className="bg-brand text-white text-[9.5px] font-bold px-1.5 py-px rounded-full shrink-0">{dm.unread}</span>
              )}
            </button>
          )
        })}
      </div>
    </aside>
  )
}

/* Call history list */
function CallsList({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  const [tab, setTab] = React.useState<"all" | "missed" | "recorded">("all")
  const items = COMMS_CALL_HISTORY.filter(c =>
    tab === "missed" ? c.direction === "missed" : tab === "recorded" ? c.recording : true
  )
  return (
    <aside className="w-[340px] shrink-0 bg-cream border-l border-divider overflow-y-auto">
      <div className="px-3 py-3 sticky top-0 bg-cream z-10 border-b border-divider">
        <div className="flex items-center justify-between mb-2">
          <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">Recent Calls</div>
        </div>
        <div className="inline-flex h-7 rounded-md bg-surface border border-divider p-0.5 w-full">
          {(["all", "missed", "recorded"] as const).map(t => (
            <button key={t} onClick={() => setTab(t)}
              className={cn(
                "flex-1 px-2 rounded-[4px] text-[11px] font-semibold transition-colors",
                tab === t ? "bg-brand text-white" : "text-ink-mute hover:text-ink"
              )}>
              {t[0].toUpperCase() + t.slice(1)}
            </button>
          ))}
        </div>
      </div>
      <div className="py-1">
        {items.map(c => {
          const others = c.participantIds.map(commsUser)
          const display = others.length > 1 ? `${others[0].name} +${others.length - 1}` : others[0].name
          const dirIcon = c.direction === "in" ? "arrow-down" : c.direction === "out" ? "arrow-up" : "x"
          const dirTone = c.direction === "missed" ? "text-danger" : c.direction === "in" ? "text-success" : "text-info"
          return (
            <button key={c.id}
              onClick={() => (window as any).cenoraToast?.(`Call back ${display}`, { sub: `${c.kind} · ${c.startedAt}` })}
              className="w-full flex items-center gap-2.5 px-3 py-2 text-left hover:bg-cream-deep border-b border-divider/40">
              <div className="relative shrink-0">
                <UserAvatar user={others[0]} size={36} />
                <span className={cn("absolute -bottom-1 -right-1 size-4 rounded-full bg-surface flex items-center justify-center", dirTone)}>
                  <Icon name={dirIcon as any} size={9} strokeWidth={2.5} />
                </span>
              </div>
              <div className="flex-1 min-w-0">
                <div className={cn("text-[12.5px] font-semibold truncate", c.direction === "missed" && "text-danger")}>{display}</div>
                <div className="text-[10.5px] text-ink-mute truncate">
                  {c.topic ?? (c.kind === "meeting" ? "Meeting" : c.kind === "video" ? "Video call" : "Voice call")}
                </div>
                <div className="text-[10px] text-ink-mute mt-0.5 flex items-center gap-1.5">
                  <span>{c.startedAt}</span>
                  {c.duration && <span>· {c.duration}</span>}
                  {c.recording && <span className="text-info-soft inline-flex items-center gap-0.5"><span className="size-1 bg-danger rounded-full" /> REC</span>}
                </div>
              </div>
              <div className="flex items-center gap-0.5 shrink-0">
                <button title="Call back" onClick={(e) => { e.stopPropagation(); (window as any).cenoraToast?.("Calling…") }} className="size-7 rounded-md text-ink-mute hover:bg-brand hover:text-white inline-flex items-center justify-center">
                  <Icon name={c.kind === "video" || c.kind === "meeting" ? "video" : "phone"} size={12} />
                </button>
              </div>
            </button>
          )
        })}
      </div>
    </aside>
  )
}

/* Meetings list */
function MeetingsList({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  const [tab, setTab] = React.useState<"upcoming" | "live" | "past">("upcoming")
  const items = tab === "live" ? COMMS_MEETINGS.filter(m => m.isLive) : tab === "upcoming" ? COMMS_MEETINGS : []
  return (
    <aside className="w-[340px] shrink-0 bg-cream border-l border-divider overflow-y-auto">
      <div className="px-3 py-3 sticky top-0 bg-cream z-10 border-b border-divider">
        <div className="flex items-center justify-between mb-2">
          <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">Meetings</div>
          <button onClick={() => (window as any).cenoraToast?.("New meeting")} className="size-6 rounded text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center">
            <Icon name="plus" size={12} />
          </button>
        </div>
        <div className="inline-flex h-7 rounded-md bg-surface border border-divider p-0.5 w-full">
          {(["upcoming", "live", "past"] as const).map(t => (
            <button key={t} onClick={() => setTab(t)}
              className={cn(
                "flex-1 px-2 rounded-[4px] text-[11px] font-semibold transition-colors",
                tab === t ? "bg-brand text-white" : "text-ink-mute hover:text-ink"
              )}>
              {t[0].toUpperCase() + t.slice(1)}
            </button>
          ))}
        </div>
      </div>
      <div className="py-1 space-y-1">
        {items.map(m => (
          <div key={m.id} className={cn(
            "mx-2 p-3 rounded-md border bg-surface",
            m.isLive ? "border-success/40 ring-1 ring-success-soft" : "border-divider"
          )}>
            <div className="flex items-start gap-2">
              <div className="flex-1 min-w-0">
                {m.isLive && (
                  <div className="text-[9.5px] font-bold uppercase tracking-wider text-success mb-1 inline-flex items-center gap-1">
                    <span className="size-1.5 rounded-full bg-success animate-pulse" /> Live now
                  </div>
                )}
                <div className="text-[13px] font-semibold text-ink leading-tight">{m.title}</div>
                <div className="text-[10.5px] text-ink-mute mt-1">{m.when} · {m.durationMin}m{m.recurring ? ` · ${m.recurring}` : ""}</div>
                <div className="flex items-center gap-1 mt-2">
                  {m.participantIds.slice(0, 4).map((id: string, i: number) => {
                    const u = commsUser(id)
                    return <UserAvatar key={i} user={u} size={20} showPresence={false} />
                  })}
                  {m.participantIds.length > 4 && (
                    <span className="text-[10px] text-ink-mute ml-1">+{m.participantIds.length - 4}</span>
                  )}
                </div>
                {m.agenda && (
                  <div className="mt-2 text-[10.5px] text-ink-soft">
                    Agenda: {m.agenda.slice(0, 2).join(" · ")}{m.agenda.length > 2 ? " …" : ""}
                  </div>
                )}
              </div>
            </div>
            <div className="flex items-center gap-1 mt-2.5">
              {m.joinable ? (
                <button onClick={() => (window as any).__cenora_open_active_call?.()}
                  className="flex-1 h-7 rounded-md bg-brand text-white text-[11px] font-bold inline-flex items-center justify-center gap-1.5 hover:bg-brand-hover">
                  <Icon name="video" size={11} /> Join {m.isLive ? "now" : "in advance"}
                </button>
              ) : (
                <button onClick={() => (window as any).cenoraToast?.("Reminder set")}
                  className="flex-1 h-7 rounded-md bg-cream-deep text-ink-soft text-[11px] font-bold inline-flex items-center justify-center gap-1.5 hover:bg-divider">
                  <Icon name="bell" size={10} /> Remind me
                </button>
              )}
              <button title="Open" className="size-7 rounded-md text-ink-mute hover:bg-cream-deep hover:text-ink inline-flex items-center justify-center">
                <Icon name="external" size={11} />
              </button>
            </div>
          </div>
        ))}
        {items.length === 0 && (
          <div className="px-4 py-8 text-center text-[12px] text-ink-mute">
            No {tab} meetings.
          </div>
        )}
      </div>
    </aside>
  )
}

function ActivityList() {
  return (
    <aside className="w-[300px] shrink-0 bg-cream border-l border-divider overflow-y-auto">
      <div className="px-3 py-3 sticky top-0 bg-cream z-10 border-b border-divider">
        <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">Activity</div>
      </div>
      <div className="py-1">
        {[
          { who: "u-maria",  when: "12m", text: "mentioned you in #month-end-close > april-2026", icon: "message", tone: "info" },
          { who: "u-bot-ai", when: "20m", text: "summarized #overdue-pos — 6 messages",            icon: "sparkle", tone: "accent-deep" },
          { who: "u-daniel", when: "1h",  text: "replied to your thread in #shop-floor-live",     icon: "message", tone: "info" },
          { who: "u-anil",   when: "2h",  text: "missed call · video",                             icon: "phone",   tone: "danger" },
          { who: "u-ling",   when: "3h",  text: "shared a file in #vendor-issues",                 icon: "paperclip", tone: "info" },
          { who: "u-sofia",  when: "4h",  text: "added you to #culture-club",                      icon: "users",   tone: "success" },
          { who: "u-tom",    when: "yesterday", text: "started a meeting · 'SAP cutover Week 2 kickoff'", icon: "video", tone: "info" },
        ].map((a, i) => {
          const u = commsUser(a.who)
          return (
            <button key={i}
              className="w-full flex items-start gap-2.5 px-3 py-2 text-left hover:bg-cream-deep border-b border-divider/30">
              <UserAvatar user={u} size={32} />
              <div className="flex-1 min-w-0">
                <div className="text-[12px] text-ink">
                  <strong className="font-semibold">{u.name}</strong>{" "}
                  <span className="text-ink-soft">{a.text}</span>
                </div>
                <div className="text-[10px] text-ink-mute mt-0.5 inline-flex items-center gap-1">
                  <Icon name={a.icon as any} size={9} className={`text-${a.tone}`} />
                  <span>{a.when} ago</span>
                </div>
              </div>
            </button>
          )
        })}
      </div>
    </aside>
  )
}

/* ─── RIGHT PANE — Conversation ────────────────────────────────────── */
function CommsConversation({ value, flat }: { value: CommsViewState; flat: boolean }) {
  if (value.section === "channels" && value.channelId) {
    return <ChannelConversation channelId={value.channelId} flat={flat} />
  }
  if (value.section === "chat" && value.dmId) {
    return <DMConversation dmId={value.dmId} />
  }
  if (value.section === "calls" || value.section === "meetings" || value.section === "activity") {
    return <EmptyConvo section={value.section} />
  }
  return <EmptyConvo section="channels" />
}

function findChannel(channelId: string): { group: any; channel: any; sub?: any } | null {
  for (const g of COMMS_GROUPS) {
    for (const c of g.channels) {
      if (c.id === channelId) return { group: g, channel: c }
      const sub = c.subChannels?.find((s: any) => s.id === channelId)
      if (sub) return { group: g, channel: c, sub }
    }
  }
  return null
}

function ChannelConversation({ channelId, flat }: { channelId: string; flat: boolean }) {
  const found = findChannel(channelId)
  if (!found) return <EmptyConvo section="channels" />
  const { group, channel, sub } = found
  const msgs = commsMessages(channelId)
  const displayName = sub ? `${channel.name} › ${sub.name}` : channel.name

  return (
    <>
      {/* Header */}
      <header className="shrink-0 px-4 py-2.5 border-b border-divider bg-surface flex items-center gap-3">
        <span className={cn("size-8 rounded text-white text-[10px] font-bold inline-flex items-center justify-center", group.color)}>{group.initials}</span>
        <div className="flex-1 min-w-0">
          <div className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">{group.name}</div>
          <div className="font-serif text-[18px] text-ink leading-tight">
            <span className="text-ink-mute">#</span>{displayName}
          </div>
        </div>
        <div className="flex items-center gap-1">
          <PaneIconBtn icon="users" label="Members" sub={`${group.members}`} />
          <PaneIconBtn icon="bookmark" label="Pinned" />
          <PaneIconBtn icon="search" label="Search" />
          <PaneIconBtn icon="sparkle" label="Summarize" tone="accent" />
          <button className="h-7 px-2.5 rounded-md bg-brand text-white text-[11px] font-bold inline-flex items-center gap-1.5 ml-1 hover:bg-brand-hover"
            onClick={() => (window as any).__cenora_open_active_call?.()}>
            <Icon name="video" size={11} /> Meet
          </button>
        </div>
      </header>

      {/* Channel charter / about */}
      {channel.linkedDoctype && (
        <div className="shrink-0 px-4 py-1.5 bg-accent-soft border-b border-accent/20 text-[10.5px] text-accent-deep flex items-center gap-2">
          <Icon name="link" size={11} />
          <span>Auto-linked to <strong>{DOCTYPE_META[channel.linkedDoctype]?.label ?? channel.linkedDoctype}</strong> records — messages can pin to specific documents.</span>
        </div>
      )}

      {/* Messages */}
      <div className={cn("flex-1 overflow-y-auto", flat ? "divide-y divide-divider/30" : "")}>
        <div className="px-4 py-3 text-[10.5px] text-ink-mute text-center">
          Beginning of <span className="font-mono">#{displayName}</span> · created by {commsUser("u-tom").name}
        </div>
        {msgs.map(m => <CommsMessage key={m.id} msg={m} />)}
      </div>

      <MessageComposer placeholder={`Message #${displayName}`} />
    </>
  )
}

function DMConversation({ dmId }: { dmId: string }) {
  const dm = COMMS_DMS.find(d => d.id === dmId)
  if (!dm) return <EmptyConvo section="chat" />
  const ppl = dm.participantIds.map(commsUser)
  const msgs = commsDMMessages(dmId)
  const display = ppl.length > 1 ? ppl.map((p: any) => p.name).join(", ") : ppl[0].name

  return (
    <>
      <header className="shrink-0 px-4 py-2.5 border-b border-divider bg-surface flex items-center gap-3">
        {ppl.length > 1
          ? <div className="size-9 rounded bg-info-soft text-info inline-flex items-center justify-center"><Icon name="users" size={14} /></div>
          : <UserAvatar user={ppl[0]} size={36} />}
        <div className="flex-1 min-w-0">
          <div className="font-serif text-[18px] text-ink leading-tight truncate">{display}</div>
          {ppl.length === 1 && (
            <div className="text-[10.5px] text-ink-mute flex items-center gap-1.5">
              <PresenceDot status={ppl[0].presence} size={6} ring={false} />
              <span>{PRESENCE_LABEL[ppl[0].presence]}</span>
              {ppl[0].statusMsg && <span className="italic">· {ppl[0].statusMsg}</span>}
            </div>
          )}
        </div>
        <div className="flex items-center gap-1">
          <PaneIconBtn icon="phone" label="Voice call" onClick={() => (window as any).cenoraToast?.("Calling…")} />
          <PaneIconBtn icon="video" label="Video call" onClick={() => (window as any).__cenora_open_active_call?.()} />
          <PaneIconBtn icon="more" label="More" />
        </div>
      </header>
      <div className="flex-1 overflow-y-auto">
        {msgs.map(m => <CommsMessage key={m.id} msg={m} />)}
      </div>
      <MessageComposer placeholder={`Message ${display}`} />
    </>
  )
}

function EmptyConvo({ section }: { section: CommsViewState["section"] }) {
  const meta: Record<CommsViewState["section"], { icon: string; title: string; sub: string }> = {
    activity: { icon: "activity", title: "Activity feed",  sub: "Pick an item from the list to dive in." },
    chat:     { icon: "message",  title: "Direct messages", sub: "Pick a person or group from the left." },
    channels: { icon: "layers",   title: "Channels",        sub: "Pick a group → channel → sub-channel on the left." },
    calls:    { icon: "phone",    title: "Call history",    sub: "Recent voice, video, and meetings calls. Pick one to view details." },
    meetings: { icon: "video",    title: "Meetings",        sub: "Upcoming and live meetings. Join from the panel on the left." },
  }
  const m = meta[section]
  return (
    <div className="flex-1 flex items-center justify-center bg-app">
      <div className="text-center max-w-sm px-6">
        <div className="size-14 rounded-full bg-brand-soft text-brand inline-flex items-center justify-center mb-3">
          <Icon name={m.icon as any} size={22} />
        </div>
        <div className="font-serif text-[22px] text-ink">{m.title}</div>
        <div className="text-[12px] text-ink-mute mt-1">{m.sub}</div>
      </div>
    </div>
  )
}

function PaneIconBtn({ icon, label, sub, onClick, tone = "default" }: { icon: string; label: string; sub?: string; onClick?: () => void; tone?: "default" | "accent" }) {
  return (
    <button title={label} onClick={onClick ?? (() => (window as any).cenoraToast?.(label))}
      className={cn(
        "h-7 px-2 rounded-md inline-flex items-center gap-1 text-[11px] transition-colors",
        tone === "accent" ? "text-accent-deep hover:bg-accent-soft" : "text-ink-mute hover:bg-cream hover:text-ink"
      )}>
      <Icon name={icon as any} size={12} />
      {sub && <span className="font-mono text-[10px]">{sub}</span>}
    </button>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * TWEAKS bridge (compact rail / flat feed)
 * ════════════════════════════════════════════════════════════════════════ */
function readCommsTweaks() {
  try {
    const raw = localStorage.getItem("cenora.comms.tweaks")
    if (raw) return JSON.parse(raw)
  } catch {}
  return { rail: "roomy", feed: "threaded" }
}

/* ════════════════════════════════════════════════════════════════════════
 * GLOBAL DOCKABLE PANEL — <CommsPanel />
 *
 * Listens on `shell.commsOpen` (added in app-shell). When AI panel is also
 * open and the dock is "right", we automatically shift left to avoid overlap.
 * Dock options: right, left, bottom, floating, popout (mock notice).
 * ════════════════════════════════════════════════════════════════════════ */
function CommsPanel() {
  const shell = useShell() as any
  const commsOpen = shell.commsOpen as boolean
  const closeComms = shell.closeComms as () => void
  const aiOpen = shell.aiOpen as boolean

  /* Coordinated dock — same system as AI / Notifications so all panels tile on a
   * shared edge instead of overlapping (helpers exported from bundle-1). */
  const { dock, setDock, pos, onDragStart, offset, width, startResize, resetWidth, pinned, togglePin } = (window as any).usePanelDock("chat", "right")

  const [v, setV] = React.useState<CommsViewState>({ section: "chat", dmId: "dm-maria" })

  if (!commsOpen) return null

  const ds = (window as any).panelDockStyle(dock, pos, width, 560, offset)
  const styleClass: string = ds.cls
  const styleObj: React.CSSProperties = ds.style
  const PanelResizeHandle = (window as any).PanelResizeHandle

  return (
    <>
      {/* No backdrop — panel is non-blocking by default */}
      <aside
        className={cn(
          "fixed z-50 bg-surface flex flex-col overflow-hidden shadow-lg animate-fade-in",
          styleClass
        )}
        style={styleObj}>
        <PanelResizeHandle dock={dock} onResizeStart={startResize} onReset={resetWidth} />
        {/* Header — also the drag handle in floating mode */}
        <header
          onMouseDown={onDragStart}
          className={cn(
            "h-11 shrink-0 bg-brand text-white px-3 flex items-center gap-2",
            dock === "floating" && "cursor-move"
          )}>
          <span className="size-7 rounded-md bg-white/15 flex items-center justify-center">
            <Icon name="message" size={13} strokeWidth={2.5} />
          </span>
          <div className="flex-1 min-w-0">
            <div className="text-[12.5px] font-semibold leading-none">Communications</div>
            <div className="text-[10px] text-white/55 mt-0.5">Cenora chat · calls · meetings</div>
          </div>
          {/* Dock controls */}
          <div className="flex items-center gap-0.5 mr-1">
            {(dock === "left" || dock === "right") && (
              <DockBtn icon="pin" active={!!pinned} onClick={togglePin} title={pinned ? "Unpin — let content slide underneath" : "Pin — push the page aside so nothing is hidden"} />
            )}
            <DockBtn icon="chevron-left"  active={dock === "left"}     onClick={() => setDock("left")}     title={dock === "left" ? "Swap position with the next left-docked panel" : "Dock left"} />
            <DockBtn icon="chevron-right" active={dock === "right"}    onClick={() => setDock("right")}    title={dock === "right" ? "Swap position with the next right-docked panel" : "Dock right"} />
            <DockBtn icon="layout"        active={dock === "floating"} onClick={() => setDock("floating")} title="Float" />
          </div>
          <button onClick={closeComms} className="size-7 rounded-md hover:bg-white/15 inline-flex items-center justify-center text-white/70 hover:text-white">
            <Icon name="x" size={13} />
          </button>
        </header>

        {/* Mini 2-pane: tabs row + content */}
        <PanelBody value={v} onChange={setV} bottomDock={dock === "bottom"} />
      </aside>
    </>
  )
}

function DockBtn({ icon, active, onClick, title }: { icon: string; active: boolean; onClick: () => void; title: string }) {
  return (
    <button title={title} onClick={onClick}
      className={cn(
        "size-7 rounded-md inline-flex items-center justify-center transition-colors",
        active ? "bg-white/20 text-white" : "text-white/60 hover:bg-white/10 hover:text-white"
      )}>
      <Icon name={icon as any} size={11} />
    </button>
  )
}

function PanelBody({ value, onChange, bottomDock }: { value: CommsViewState; onChange: (v: CommsViewState) => void; bottomDock: boolean }) {
  return (
    <>
      {/* Tabs */}
      <div className="shrink-0 border-b border-divider flex items-center px-1.5 bg-cream">
        {([
          { k: "chat",     l: "Chat",     i: "message", n: COMMS_DMS.reduce((s, d) => s + (d.unread ?? 0), 0) },
          { k: "channels", l: "Channels", i: "layers",  n: 11 },
          { k: "calls",    l: "Calls",    i: "phone",   n: COMMS_CALL_HISTORY.filter(c => c.direction === "missed").length },
          { k: "meetings", l: "Meetings", i: "video" },
          { k: "activity", l: "Activity", i: "activity", n: 7 },
        ] as const).map(t => {
          const active = value.section === t.k
          return (
            <button key={t.k} onClick={() => onChange({ section: t.k as any })}
              className={cn(
                "h-9 px-2.5 inline-flex items-center gap-1.5 text-[11px] font-semibold border-b-2 transition-colors",
                active ? "border-brand text-brand" : "border-transparent text-ink-mute hover:text-ink"
              )}>
              <Icon name={t.i as any} size={12} />
              {t.l}
              {(t as any).n != null && (t as any).n > 0 && (
                <span className={cn("text-[9px] font-bold px-1 rounded", active ? "bg-brand text-white" : "bg-cream-deep text-ink-soft")}>{(t as any).n}</span>
              )}
            </button>
          )
        })}
      </div>

      {/* Body — 2 columns when bottom-docked, otherwise stacked */}
      <div className={cn("flex-1 overflow-hidden", bottomDock ? "flex" : "flex flex-col")}>
        <div className={cn(bottomDock ? "w-[260px] shrink-0 border-r border-divider overflow-y-auto" : "shrink-0 border-b border-divider max-h-[280px] overflow-y-auto")}>
          <PanelListInner value={value} onChange={onChange} />
        </div>
        <div className="flex-1 min-w-0 flex flex-col overflow-hidden">
          <PanelConvoInner value={value} />
        </div>
      </div>
    </>
  )
}

function PanelListInner({ value, onChange }: { value: CommsViewState; onChange: (v: CommsViewState) => void }) {
  if (value.section === "chat") {
    return (
      <div className="py-1">
        {COMMS_DMS.slice(0, 6).map(dm => {
          const u = commsUser(dm.participantIds[0])
          const active = value.dmId === dm.id
          return (
            <button key={dm.id} onClick={() => onChange({ section: "chat", dmId: dm.id })}
              className={cn("w-full flex items-center gap-2 px-3 py-1.5 text-left hover:bg-cream-deep", active && "bg-brand-soft")}>
              <UserAvatar user={u} size={28} />
              <div className="flex-1 min-w-0">
                <div className={cn("text-[12px] truncate", active ? "text-brand font-semibold" : dm.unread ? "text-ink font-semibold" : "text-ink-soft")}>{u.name}</div>
                <div className="text-[10px] text-ink-mute truncate">{dm.lastMsgPreview}</div>
              </div>
              {dm.unread != null && dm.unread > 0 && <span className="bg-brand text-white text-[9px] font-bold px-1.5 py-px rounded-full">{dm.unread}</span>}
            </button>
          )
        })}
      </div>
    )
  }
  if (value.section === "channels") {
    /* Show "starred" / recent channels in the panel */
    const items = [
      { id: "sc-po-overdue",   group: "Procurement", name: "overdue-pos", unread: 2 },
      { id: "sc-close-apr26",  group: "Finance",     name: "april-2026",  unread: 3 },
      { id: "sc-wo-shopfloor", group: "Operations",  name: "shop-floor-live", unread: 2 },
      { id: "c-proc-irq",      group: "Procurement", name: "item-requests", unread: 2 },
      { id: "c-hvph-announce", group: "HVPH",        name: "announcements", unread: 2 },
    ]
    return (
      <div className="py-1">
        {items.map(i => {
          const active = value.channelId === i.id
          return (
            <button key={i.id} onClick={() => onChange({ section: "channels", channelId: i.id })}
              className={cn("w-full flex items-center gap-2 px-3 py-1.5 text-left hover:bg-cream-deep", active && "bg-brand-soft")}>
              <span className="text-ink-mute font-mono text-[12px]">#</span>
              <div className="flex-1 min-w-0">
                <div className={cn("text-[12px] truncate", active ? "text-brand font-semibold" : "text-ink-soft")}>{i.name}</div>
                <div className="text-[10px] text-ink-mute">{i.group}</div>
              </div>
              {i.unread > 0 && <span className="bg-brand text-white text-[9px] font-bold px-1.5 py-px rounded-full">{i.unread}</span>}
            </button>
          )
        })}
      </div>
    )
  }
  if (value.section === "calls") return <CallsList value={value} onChange={onChange} />
  if (value.section === "meetings") return <MeetingsList value={value} onChange={onChange} />
  return <ActivityList />
}

function PanelConvoInner({ value }: { value: CommsViewState }) {
  if (value.section === "chat" && value.dmId) {
    const dm = COMMS_DMS.find(d => d.id === value.dmId)
    if (!dm) return <EmptyConvo section="chat" />
    const ppl = dm.participantIds.map(commsUser)
    return (
      <>
        <div className="shrink-0 px-3 py-2 border-b border-divider flex items-center gap-2 bg-surface">
          <UserAvatar user={ppl[0]} size={26} />
          <div className="flex-1 min-w-0">
            <div className="text-[12.5px] font-semibold text-ink truncate">{ppl[0].name}</div>
            <div className="text-[10px] text-ink-mute truncate">{ppl[0].role}</div>
          </div>
          <PaneIconBtn icon="phone" label="Voice" />
          <PaneIconBtn icon="video" label="Video" onClick={() => (window as any).__cenora_open_active_call?.()} />
        </div>
        <div className="flex-1 overflow-y-auto">
          {commsDMMessages(value.dmId).map(m => <CommsMessage key={m.id} msg={m} dense />)}
        </div>
        <MessageComposer placeholder={`Reply to ${ppl[0].name}`} compact />
      </>
    )
  }
  if (value.section === "channels" && value.channelId) {
    const found = findChannel(value.channelId)
    if (!found) return <EmptyConvo section="channels" />
    return (
      <>
        <div className="shrink-0 px-3 py-2 border-b border-divider bg-surface">
          <div className="text-[10px] text-ink-mute uppercase tracking-wider">{found.group.name}</div>
          <div className="text-[14px] font-serif text-ink leading-tight">
            <span className="text-ink-mute">#</span>{found.sub?.name ?? found.channel.name}
          </div>
        </div>
        <div className="flex-1 overflow-y-auto">
          {commsMessages(value.channelId).map(m => <CommsMessage key={m.id} msg={m} dense />)}
        </div>
        <MessageComposer placeholder={`Message #${found.sub?.name ?? found.channel.name}`} compact />
      </>
    )
  }
  return <EmptyConvo section={value.section} />
}

/* ════════════════════════════════════════════════════════════════════════
 * ACTIVE CALL OVERLAY — <CommsActiveCall />
 *
 * Driven by window.__cenora_active_call (true/false). The CommsPanel and
 * other UI flip this via window.__cenora_open_active_call().
 * ════════════════════════════════════════════════════════════════════════ */
function CommsActiveCall() {
  const [open, setOpen] = React.useState(false)
  React.useEffect(() => {
    (window as any).__cenora_open_active_call = () => setOpen(true)
    return () => { delete (window as any).__cenora_open_active_call }
  }, [])

  const [muted, setMuted] = React.useState(false)
  const [video, setVideo] = React.useState(true)
  const [hand, setHand] = React.useState(false)
  const [share, setShare] = React.useState(false)
  const [captions, setCaptions] = React.useState(true)
  const [timer, setTimer] = React.useState("00:00")

  React.useEffect(() => {
    if (!open) { setTimer("00:00"); return }
    const start = Date.now()
    const t = setInterval(() => {
      const s = Math.floor((Date.now() - start) / 1000)
      const m = Math.floor(s / 60).toString().padStart(2, "0")
      const ss = (s % 60).toString().padStart(2, "0")
      setTimer(`${m}:${ss}`)
    }, 1000)
    return () => clearInterval(t)
  }, [open])

  if (!open) return null

  return (
    <div className="fixed inset-0 z-[80] bg-ink/95 flex flex-col">
      {/* Header */}
      <header className="h-12 shrink-0 bg-ink/40 backdrop-blur border-b border-white/10 px-4 flex items-center gap-3 text-white">
        <div className="flex items-center gap-2">
          <span className="size-2 rounded-full bg-success animate-pulse" />
          <span className="text-[12px] font-semibold">Apr close · daily sync</span>
        </div>
        <span className="text-[11px] text-white/50">·</span>
        <span className="font-mono text-[12px] text-white/70">{timer}</span>
        <span className="text-[11px] text-white/50">·</span>
        <span className="text-[11px] text-white/70">{ACTIVE_CALL_PARTICIPANTS.length} participants</span>
        <span className="text-[11px] text-white/50">·</span>
        <span className="text-[11px] text-danger inline-flex items-center gap-1"><span className="size-1.5 rounded-full bg-danger" /> Recording</span>
        <div className="flex-1" />
        <button onClick={() => setOpen(false)} className="h-7 px-3 rounded-md bg-white/10 hover:bg-white/20 text-[11px] font-semibold inline-flex items-center gap-1.5">
          <Icon name="external" size={11} /> Pop out
        </button>
        <button onClick={() => setOpen(false)} className="size-7 inline-flex items-center justify-center rounded-md hover:bg-white/10">
          <Icon name="x" size={14} />
        </button>
      </header>

      {/* Stage */}
      <div className="flex-1 flex overflow-hidden">
        {/* Video grid */}
        <div className="flex-1 p-4 flex flex-col">
          <div className="flex-1 grid grid-cols-3 gap-3 content-center">
            {ACTIVE_CALL_PARTICIPANTS.map((p, i) => {
              const u = commsUser(p.id)
              return (
                <div key={i} className={cn(
                  "relative rounded-lg overflow-hidden border-2 transition-colors aspect-video",
                  p.speaking ? "border-success" : "border-white/10",
                  p.you && "ring-1 ring-info"
                )}>
                  {p.video ? (
                    <div className={cn(
                      "absolute inset-0 flex items-center justify-center",
                      u.tone === "teal" ? "bg-brand/60" :
                      u.tone === "sand" ? "bg-accent/60" :
                      u.tone === "ink"  ? "bg-ink/80" :
                      u.tone === "ocean" ? "bg-info/60" : "bg-warn/60"
                    )}>
                      <div className="text-white text-[48px] font-serif font-bold opacity-70">{u.initials}</div>
                    </div>
                  ) : (
                    <div className="absolute inset-0 flex items-center justify-center bg-ink-soft/80">
                      <Avatar initials={u.initials} tone={u.tone ?? "sand"} size="lg" />
                    </div>
                  )}
                  <div className="absolute bottom-2 left-2 right-2 flex items-center gap-1.5">
                    <span className="bg-ink/70 backdrop-blur text-white text-[11px] px-2 py-0.5 rounded font-medium flex-1 truncate">
                      {u.name}{p.you && " (You)"}
                    </span>
                    {p.hand && <span className="bg-warn text-ink text-[11px] px-1.5 rounded">✋</span>}
                    {p.muted && <span className="bg-ink/70 size-6 rounded inline-flex items-center justify-center text-white"><Icon name="mic" size={11} /></span>}
                  </div>
                </div>
              )
            })}
          </div>

          {/* Captions */}
          {captions && (
            <div className="mx-auto mt-3 max-w-[640px] px-4 py-2 bg-ink/70 backdrop-blur text-white rounded-md text-[13px] text-center">
              <span className="text-accent font-semibold">Maya R.:</span>{" "}
              <span>"HVHK accruals — I posted JE-2026-04-0142 and 0143 — rent and utilities."</span>
            </div>
          )}
        </div>

        {/* Side: transcript + AI */}
        <aside className="w-[340px] shrink-0 bg-ink/40 backdrop-blur border-l border-white/10 flex flex-col text-white">
          <header className="h-10 shrink-0 px-3 border-b border-white/10 flex items-center gap-2">
            <Icon name="sparkle" size={13} className="text-accent" />
            <span className="text-[12px] font-semibold">Live transcript &amp; AI</span>
          </header>
          <div className="flex-1 overflow-y-auto p-3 space-y-2">
            {ACTIVE_CALL_TRANSCRIPT.map((t, i) => {
              const u = commsUser(t.who)
              const isAI = t.who === "u-bot-ai"
              return (
                <div key={i} className={cn(
                  "p-2 rounded-md",
                  isAI ? "bg-accent-soft/20 border border-accent/30" : "bg-white/5"
                )}>
                  <div className="flex items-center gap-1.5 text-[10px] text-white/60 mb-0.5">
                    <span className={cn("font-semibold", isAI ? "text-accent" : "text-white")}>{u.name}</span>
                    <span className="font-mono">{t.time}</span>
                  </div>
                  <div className={cn("text-[12px] leading-snug", isAI ? "text-accent-soft" : "text-white/90")}>{t.text}</div>
                </div>
              )
            })}
          </div>
          <div className="shrink-0 border-t border-white/10 p-2">
            <button className="w-full h-7 rounded-md bg-accent text-ink text-[11px] font-bold inline-flex items-center justify-center gap-1.5 hover:bg-accent/90">
              <Icon name="sparkle" size={11} /> Summarize so far
            </button>
          </div>
        </aside>
      </div>

      {/* Controls */}
      <footer className="h-20 shrink-0 bg-ink border-t border-white/10 flex items-center justify-center gap-2">
        <CallCtl active={!muted} onIcon="mic" offIcon="mic" label={muted ? "Unmute" : "Mute"} onClick={() => setMuted(m => !m)} />
        <CallCtl active={video}  onIcon="video" offIcon="video" label={video ? "Stop video" : "Start video"} onClick={() => setVideo(v => !v)} />
        <CallCtl active={share}  onIcon="monitor" offIcon="monitor" label={share ? "Stop sharing" : "Share screen"} onClick={() => setShare(s => !s)} />
        <CallCtl active={hand}   onIcon="thumbs-up" offIcon="thumbs-up" label={hand ? "Lower hand" : "Raise hand"} onClick={() => setHand(h => !h)} />
        <CallCtl active={captions} onIcon="message" offIcon="message" label={captions ? "Hide captions" : "Show captions"} onClick={() => setCaptions(c => !c)} />
        <CallCtl active={false}  onIcon="users" offIcon="users" label="People" onClick={() => (window as any).cenoraToast?.("People")} />
        <CallCtl active={false}  onIcon="more"  offIcon="more"  label="More" onClick={() => (window as any).cenoraToast?.("More")} />
        <button onClick={() => setOpen(false)}
          className="h-11 px-5 rounded-full bg-danger text-white text-[12px] font-bold inline-flex items-center gap-2 hover:bg-danger/90 ml-2">
          <Icon name="phone" size={13} className="rotate-[135deg]" />
          Leave
        </button>
      </footer>
    </div>
  )
}

function CallCtl({ active, onIcon, offIcon, label, onClick }: { active: boolean; onIcon: string; offIcon: string; label: string; onClick: () => void }) {
  return (
    <button title={label} onClick={onClick}
      className={cn(
        "size-11 rounded-full inline-flex items-center justify-center transition-colors",
        active ? "bg-white/15 text-white hover:bg-white/25" : "bg-danger/30 text-danger hover:bg-danger/40"
      )}>
      <Icon name={(active ? onIcon : offIcon) as any} size={16} />
    </button>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * EMBEDDABLE DOC CHAT — <DocChatPill />
 *
 * Drop into a document surface (PO, IRQ, Opp, Work Order…) to surface a
 * floating "💬 N" pill in the bottom-right. Click to expand into an
 * inline thread anchored to the doc.
 *
 *   <DocChatPill docType="po" docId="PO-HVPH-1089" channel="sc-po-overdue" />
 * ════════════════════════════════════════════════════════════════════════ */
function DocChatPill({ docType, docId, channel, anchor = "bottom-right" }: {
  docType: string
  docId: string
  channel?: string  /* optional channel id with seed messages */
  anchor?: "bottom-right" | "inline"
}) {
  const [open, setOpen] = React.useState(false)
  const seed = channel ? commsMessages(channel) : []
  const count = seed.length

  if (anchor === "inline") {
    return (
      <DocChatInline docType={docType} docId={docId} messages={seed} />
    )
  }

  return (
    <>
      <button
        onClick={() => setOpen(true)}
        className="fixed bottom-6 right-6 z-40 h-11 pl-3 pr-4 rounded-full bg-brand text-white font-semibold text-[12px] shadow-lg hover:bg-brand-hover inline-flex items-center gap-2">
        <span className="size-7 rounded-full bg-white/15 inline-flex items-center justify-center -ml-1">
          <Icon name="message" size={13} />
        </span>
        Discussion
        {count > 0 && <span className="bg-accent text-brand size-5 rounded-full text-[10px] font-bold inline-flex items-center justify-center">{count}</span>}
      </button>

      {open && (
        <div className="fixed inset-0 z-[60] bg-ink/30 flex items-end justify-end p-4" onClick={() => setOpen(false)}>
          <aside className="bg-surface border border-divider rounded-lg shadow-xl w-[420px] h-[560px] flex flex-col overflow-hidden" onClick={e => e.stopPropagation()}>
            <header className="h-11 shrink-0 px-3 bg-brand text-white flex items-center gap-2">
              <span className="size-7 rounded-md bg-white/15 inline-flex items-center justify-center">
                <Icon name="message" size={13} />
              </span>
              <div className="flex-1 min-w-0">
                <div className="text-[12.5px] font-semibold leading-none">Discussion</div>
                <div className="text-[10px] text-white/55 mt-0.5 font-mono">{docId}</div>
              </div>
              <button onClick={() => setOpen(false)} className="size-7 rounded-md hover:bg-white/15 inline-flex items-center justify-center"><Icon name="x" size={13} /></button>
            </header>
            <div className="shrink-0 px-3 py-1.5 bg-accent-soft border-b border-accent/20 text-[10.5px] text-accent-deep flex items-center gap-1.5">
              <Icon name="link" size={11} />
              <span>Posts here also appear in <strong>#{channel ?? "linked-channel"}</strong></span>
            </div>
            <div className="flex-1 overflow-y-auto">
              {seed.length === 0 ? (
                <div className="px-4 py-8 text-center text-[12px] text-ink-mute">No discussion yet. Start a thread below.</div>
              ) : seed.map(m => <CommsMessage key={m.id} msg={m} dense />)}
            </div>
            <MessageComposer placeholder={`Discuss ${docId}`} compact />
          </aside>
        </div>
      )}
    </>
  )
}

function DocChatInline({ docType, docId, messages }: { docType: string; docId: string; messages: any[] }) {
  return (
    <Card>
      <CardHeader>
        <div>
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Discussion</div>
          <div className="font-serif text-[16px] text-ink mt-0.5">{messages.length} messages · linked to <span className="font-mono text-[14px]">{docId}</span></div>
        </div>
        <Button variant="ghost" size="sm" leadingIcon="external">Open in channel</Button>
      </CardHeader>
      <div className="max-h-[320px] overflow-y-auto bg-cream/40">
        {messages.length === 0 ? (
          <div className="px-4 py-8 text-center text-[12px] text-ink-mute">No discussion yet.</div>
        ) : messages.map((m: any) => <CommsMessage key={m.id} msg={m} dense />)}
      </div>
      <MessageComposer placeholder={`Discuss ${docId} with the team`} compact />
    </Card>
  )
}

// SANDBOX
Object.assign(globalThis as any, {
  CommsModule, CommsPanel, CommsActiveCall, DocChatPill, DocChatInline,
  PresenceDot, UserAvatar, DocLinkBadge,
})

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/app/routes.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/app/(app)/routes.tsx
 * ----------------------------------------------------------------------------
 * Sandbox-only file. Maps a route string to its page component.
 *
 * Routes follow the v2 sidebar layout (Products/Procurement/Warehouse/CRM/
 * Multi-Entity/etc renamed). Legacy /catalogue, /sales, /inventory paths are
 * kept as aliases so deep links from older builds still work.
 * ============================================================================
 */

/* ─── Activity feed (Core) ──────────────────────────────────────────────
 * Cross-module event stream. Shares its data source with the notifications
 * bell (the global NOTIFS list defined in notifications-panel.tsx) — the bell
 * is the compact slideout, this is the full-page view. Same items, same
 * action buttons (Approve → Approvals, Reply → Chat, etc.). */
function ActivityFeed() {
  const { push } = useRouter()
  const SRC: any[] = (globalThis as any).NOTIFS || []
  const CAT: any = (globalThis as any).NOTIF_CAT || {}
  const [items, setItems] = React.useState<any[]>(SRC)
  const [tab, setTab] = React.useState<"all" | "approval" | "mention" | "system">("all")

  const filtered = tab === "all" ? items
    : tab === "mention"  ? items.filter(i => i.category === "mention")
    : tab === "approval" ? items.filter(i => i.category === "approval")
    : items.filter(i => i.category === "system" || i.category === "task" || i.category === "alert")

  const bucket = (w: string) => {
    const s = (w || "").toLowerCase()
    if (/yesterday/.test(s)) return "Yesterday"
    if (/\d\s*d\s*ago|week|month/.test(s)) return "Earlier"
    return "Today"
  }
  const groups = ["Today", "Yesterday", "Earlier"].filter(g => filtered.some(i => bucket(i.when) === g))
  const markRead = (id: string) => setItems(items.map(i => i.id === id ? { ...i, unread: false } : i))
  const unread = items.filter(i => i.unread).length

  const chips: { key: typeof tab; label: string }[] = [
    { key: "all", label: "All" },
    { key: "approval", label: "Approvals" },
    { key: "mention", label: "Mentions" },
    { key: "system", label: "System" },
  ]

  return (
    <>
      <TopBar breadcrumb={[{ label: "Activity" }]} />
      <ActionBar
        title="Activity"
        status={`Cross-module event stream · same source as notifications · ${unread} unread`}
        primary={<Button variant="ghost" leadingIcon="check" onClick={() => setItems(items.map(i => ({ ...i, unread: false })))}>Mark all read</Button>}
        secondary={<Button variant="ghost" leadingIcon="download">Export log</Button>}
        filters={
          <div className="flex items-center gap-1.5 flex-wrap">
            {chips.map(c => {
              const active = tab === c.key
              const count = c.key === "all" ? items.length : filtered === items ? 0 : (
                c.key === "approval" ? items.filter(i => i.category === "approval").length
                : c.key === "mention" ? items.filter(i => i.category === "mention").length
                : items.filter(i => i.category === "system" || i.category === "task" || i.category === "alert").length
              )
              return (
                <button key={c.key} onClick={() => setTab(c.key)}
                  className={cn(
                    "h-7 px-3 rounded-full text-[11.5px] font-semibold border inline-flex items-center gap-1.5 transition-colors",
                    active ? "bg-brand text-white border-brand" : "bg-surface text-ink-soft border-divider hover:border-brand-mid hover:text-brand"
                  )}>
                  {c.label}
                  {count > 0 && <span className={cn("text-[9.5px] tabular-nums", active ? "opacity-80" : "opacity-60")}>{count}</span>}
                </button>
              )
            })}
          </div>
        }
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[820px] mx-auto px-6 py-6">
          {groups.map(group => {
            const rows = filtered.filter(i => bucket(i.when) === group)
            return (
              <div key={group} className="mb-7">
                <div className="text-[10px] font-bold uppercase tracking-[0.18em] text-ink-mute mb-2.5 px-1">{group}</div>
                <div className="bg-surface border border-divider rounded-lg overflow-hidden">
                  {rows.map((n, idx) => {
                    const cat = CAT[n.category] || CAT.system || { icon: "info", bg: "bg-cream-deep", fg: "text-ink-mute" }
                    return (
                      <div key={n.id}
                        className={cn(
                          "px-4 py-3 flex items-start gap-3",
                          idx < rows.length - 1 && "border-b border-divider-soft",
                          n.unread && "bg-brand-soft/20"
                        )}>
                        {n.whoInitials ? (
                          <Avatar initials={n.whoInitials} tone="sand" size="md" />
                        ) : (
                          <span className={cn("size-8 rounded-lg inline-flex items-center justify-center shrink-0", cat.bg)}>
                            <Icon name={cat.icon as any} size={15} className={cat.fg} />
                          </span>
                        )}
                        <div className="flex-1 min-w-0">
                          <div className="flex items-start justify-between gap-2">
                            <div className="text-[12.5px] text-ink font-semibold leading-snug">{n.title}</div>
                            {n.unread && <span className="size-1.5 rounded-full bg-brand mt-1.5 shrink-0" />}
                          </div>
                          {n.body && <div className="text-[11.5px] text-ink-soft mt-1 leading-snug">{n.body}</div>}
                          <div className="text-[10.5px] text-ink-mute mt-1.5">
                            {n.who && <span>{n.who} · </span>}{n.when}{n.ref && <span className="font-mono ml-1">· {n.ref}</span>}
                          </div>
                          {n.actions && n.actions.length > 0 && (
                            <div className="flex items-center gap-1.5 mt-2 flex-wrap">
                              {n.actions.map((a: any, i: number) => (
                                <button key={i}
                                  onClick={() => { markRead(n.id); if (a.href) push(a.href) }}
                                  className={cn(
                                    "h-7 px-2.5 rounded text-[11px] font-semibold transition-colors",
                                    a.tone === "primary" ? "bg-brand text-white hover:bg-brand-hover"
                                    : a.tone === "danger"  ? "bg-danger-soft text-danger hover:bg-danger hover:text-white"
                                    : "bg-cream text-ink-soft hover:bg-cream-deep border border-divider"
                                  )}>
                                  {a.label}
                                </button>
                              ))}
                            </div>
                          )}
                        </div>
                      </div>
                    )
                  })}
                </div>
              </div>
            )
          })}
        </div>
      </div>
    </>
  )
}

function renderRoute(route: string): React.ReactElement {
  /* Activity (Core) — cross-module event stream */
  if (route.startsWith("/home/activity")) return <ActivityFeed />

  /* Home */
  if (route === "/" || route === "/home" || route.startsWith("/home/")) return <Home />

  /* Approvals (Core) */
  if (route === "/approvals" || route === "/approvals/inbox") return <ApprovalsInbox />
  if (route === "/approvals/rules")    return <ApprovalsRules />
  if (route === "/approvals/history")  return <ApprovalsHistory />

  /* Products (renamed from Catalogue) */
  if (route === "/products/irq" || route === "/catalogue/irq")                  return <IRQKanban />
  if (route === "/products/irq/new" || route === "/catalogue/irq/new")          return <IRQForm />
  if (route.startsWith("/products/irq/") || route.startsWith("/catalogue/irq/")) return <IRQKanban />
  if (route === "/products/list" || route === "/catalogue/products")            return <Products />
  if (route === "/products/categories" || route === "/catalogue/categories")    return <Categories />
  if (route === "/products/attributes" || route === "/catalogue/attributes")    return <Attributes />

  /* Procurement */
  if (route === "/procurement/order-requests" || route === "/procurement/pr") return <PRList />
  if (route === "/procurement/requisitions")  return <Requisitions />
  if (route === "/procurement/po")            return <POList />
  if (route === "/procurement/po/new")        return <POForm />
  if (route.startsWith("/procurement/po/"))   return <POForm poId={route.slice("/procurement/po/".length)} />
  if (route === "/procurement/vendors")       return <VendorCards />
  if (route.startsWith("/procurement/vendors/")) return <VendorCards />
  /* Item Receipts moved to Warehouse but legacy route still resolves */
  if (route === "/procurement/ir")            return <ItemReceipt />
  if (route.startsWith("/procurement/ir/"))   return <ItemReceipt irId={route.slice("/procurement/ir/".length)} />

  /* Finance */
  if (route === "/finance")                 return <FinanceDashboard />
  if (route === "/finance/gl")              return <GeneralLedger />
  if (route === "/finance/ap")              return <AccountsPayable />
  if (route === "/finance/ar")              return <AccountsReceivable />
  if (route === "/finance/cash")            return <CashBank />
  if (route === "/finance/bank-recon")      return <BankRecon />
  if (route === "/finance/closings" || route === "/finance/month-end") return <MonthEndClose />

  /* Subscriptions & Billing (NEW v4 — Finance family) */
  if (route === "/billing")               return <BillingDashboard />
  if (route === "/billing/plans")         return <BillingPlans />
  if (route === "/billing/subscriptions") return <BillingSubscriptions />
  if (route.startsWith("/billing/subscriptions/")) return <BillingSubscriptions subId={route.slice("/billing/subscriptions/".length)} />
  if (route === "/billing/invoices")      return <BillingInvoices />
  if (route === "/billing/revenue")       return <BillingRevenue />

  /* ═══ V4 net-new modules ═══ */
  /* E-commerce Storefront */
  if (route === "/commerce")              return <CommerceDashboard />
  if (route === "/commerce/storefront")   return <CommerceStorefront />
  if (route === "/commerce/orders")       return <CommerceOrders />
  if (route === "/commerce/promotions")   return <CommercePromotions />
  if (route === "/commerce/b2b")          return <CommerceB2B />
  /* Point of Sale */
  if (route === "/pos")                   return <POSDashboard />
  if (route === "/pos/register")          return <POSRegister />
  if (route === "/pos/shifts")            return <POSShifts />
  if (route === "/pos/loyalty")           return <POSLoyalty />
  /* Bookings & Reservations */
  if (route === "/bookings")              return <BookingsDashboard />
  if (route === "/bookings/calendar")     return <BookingsCalendar />
  if (route === "/bookings/types")        return <BookingTypes />
  if (route === "/bookings/public")       return <BookingsPublic />
  if (route === "/bookings/list")         return <BookingsList />
  /* Quality Management (QMS) */
  if (route === "/quality")               return <QualityDashboard />
  if (route === "/quality/ncr")           return <QualityNCR />
  if (route === "/quality/capa")          return <QualityCAPA />
  if (route === "/quality/inspections")   return <QualityInspections />
  if (route === "/quality/audits")        return <QualityAudits />
  /* Health & Safety (EHS) */
  if (route === "/ehs")                   return <EHSDashboard />
  if (route === "/ehs/incidents")         return <EHSIncidents />
  if (route === "/ehs/inspections")       return <EHSInspections />
  if (route === "/ehs/permits")           return <EHSPermits />
  if (route === "/ehs/actions")           return <EHSActions />
  /* V4 expansions */
  if (route === "/assets/intangible/amortisation") return <AssetsIntangible view="amortisation" />
  if (route === "/assets/intangible")     return <AssetsIntangible view="register" />
  if (route === "/time/shifts")           return <ShiftScheduling />
  if (route === "/facilities/leases")     return <FacilitiesLeases />
  if (route === "/hr/expenses")           return <HRExpenses />
  if (route === "/admin/org-scope")       return <AdminOrgScope />

  /* Warehouse (renamed from Inventory) */
  if (route === "/warehouse" || route === "/inventory")                   return <StockDashboard />
  if (route === "/warehouse/receive" || route === "/inventory/receive")   return <WarehouseReceive />
  if (route === "/warehouse/ship" || route === "/inventory/ship")         return <WarehouseShip />
  if (route === "/warehouse/transfers" || route === "/inventory/transfers") return <WarehouseTransfers />
  if (route === "/warehouse/rma" || route === "/inventory/rma")           return <WarehouseRMA />
  if (route === "/warehouse/adjustments" || route === "/inventory/adjustments") return <WarehouseAdjustments />
  if (route === "/warehouse/cycle-count" || route === "/inventory/cycle-count") return <CycleCount />

  /* Logistics (new module) */
  if (route === "/logistics")                 return <LogisticsDashboard />
  if (route === "/logistics/transport")       return <TransportOrders />
  if (route === "/logistics/routes")          return <RoutePlanning />
  if (route === "/logistics/fleet")           return <FleetDrivers />
  if (route === "/logistics/gps")             return <GPSTracking />

  /* HRIS */
  if (route === "/hr/dashboard" || route === "/hr/hire-to-retire") return <HireToRetire />
  if (route === "/hr/directory")              return <EmployeeDirectory />
  if (route === "/hr/org-chart")              return <OrgChart />
  if (route === "/hr/recruitment")            return <HRRecruitment />
  if (route === "/hr/performance")            return <HRPerformance />
  if (route === "/hr/training")               return <HRTraining />
  if (route === "/hr/payroll")                return <HRPayroll />
  if (route === "/hr/attendance")             return <HRAttendance />
  if (route === "/hr/cases")                  return <HRCases />

  /* CRM (renamed from Sales · CRM) */
  if (route === "/crm" || route === "/crm/opportunities" || route === "/sales/pipeline") return <SalesPipeline />
  if (route === "/crm/customers" || route === "/sales/customers")    return <SalesCustomers />
  if (route === "/crm/activities" || route === "/sales/activities")  return <CRMActivities />
  if (route === "/crm/quotes" || route === "/sales/quotes")          return <SalesQuotes />
  if (route === "/crm/orders" || route === "/sales/orders")          return <SalesOrders />
  if (route === "/sales/invoices")                                    return <SalesInvoices />

  /* Reports */
  if (route === "/reports/library")            return <ReportLibrary />
  if (route === "/reports/ask-ai")             return <AskAIReport />

  /* Multi-Entity module (V3 — broken out from core) */
  if (route === "/multi-entity" || route === "/multi-entity/consolidated" || route === "/entities/consolidated") return <Consolidated />
  if (route === "/multi-entity/navigator")    return <SubsidiaryNavigator />
  if (route === "/multi-entity/intercompany") return <IntercompanyTxns />
  if (route === "/multi-entity/eliminations") return <Eliminations />
  if (route === "/multi-entity/transfers")    return <InterEntityTransfers />
  if (route === "/multi-entity/rules")        return <ConsolidationRules />

  /* Communications module (V3) */
  if (route === "/comms" || route.startsWith("/comms/")) return <CommsModule />

  /* Marketplace */
  if (route === "/marketplace")                return <Marketplace />

  /* Manufacturing */
  if (route === "/manufacturing")                  return <MfgDashboard />
  if (route === "/manufacturing/planning")         return <ProductionPlanning />
  if (route === "/manufacturing/work-orders")      return <WorkOrders />
  if (route === "/manufacturing/bom")              return <BillOfMaterials />
  if (route === "/manufacturing/shop-floor")       return <ShopFloor />
  if (route === "/manufacturing/quality")          return <QualityControl />
  if (route === "/manufacturing/routings")         return <ManufacturingRoutings />

  /* Project Management (new module) */
  if (route === "/pm" || route === "/pm/projects") return <PMProjects />
  if (route === "/pm/tasks")     return <PMTasks />
  if (route === "/pm/schedule")  return <PMSchedule />
  if (route === "/pm/resources") return <PMResources />

  /* Project Accounting */
  if (route === "/pa/portfolio" || route === "/projects/portfolio") return <ProjectPortfolio />
  if (route === "/pa/time" || route === "/projects/time")           return <ProjectTimesheets />
  if (route === "/pa/billing" || route === "/projects/wip")         return <ProjectWIP />

  /* Fixed Assets */
  if (route === "/assets/register")            return <AssetRegister />
  if (route === "/assets/depreciation")        return <AssetDepreciation />
  if (route === "/assets/disposals")           return <AssetDisposals />

  /* Tax & Compliance */
  if (route === "/tax/calendar")               return <ComplianceCalendar />
  if (route === "/tax/filings")                return <TaxFilings />
  if (route === "/tax/ph-bir")                 return <PHBir />

  /* Field Service */
  if (route === "/field/dispatch")             return <DispatchBoard />
  if (route === "/field/crew")                 return <CrewMobile />
  if (route === "/field/sla")                  return <FieldSLA />

  /* === IMPORTED MODULES (merge v3.3) — Marketing / Tickets / Customer Service / IT / Facilities ===
   * Customer Service supersedes the legacy Support module. The two richer Support
   * screens are retained and reused for KB + CSAT (see /tickets/kb, /tickets/csat,
   * /support/kb, /support/csat below). SupportDashboard/Tickets/QueueBoard are no
   * longer routed (kept defined for back-compat / lift-out). */

  /* Marketing */
  if (route === "/marketing")            return <MarketingDashboard />
  if (route === "/marketing/email")      return <MarketingEmail />
  if (route === "/marketing/social")     return <MarketingSocial />
  if (route === "/marketing/campaigns")  return <MarketingCampaigns />
  if (route === "/marketing/pages")      return <MarketingPages />
  if (route === "/marketing/analytics")  return <MarketingAnalytics />
  if (route === "/marketing/contacts")   return <MarketingContacts />

  /* Tickets platform — KB & CSAT use the richer Support screens kept from this base */
  if (route === "/tickets")              return <TicketsDashboard />
  if (route === "/inbox" || route === "/tickets/inbox") return <TicketsInbox />
  if (route === "/tickets/types")        return <TicketTypes />
  if (route === "/tickets/sla")          return <TicketSLA />
  if (route === "/tickets/automations")  return <TicketAutomations />
  if (route === "/tickets/kb")           return <SupportKnowledgeBase />
  if (route === "/tickets/csat")         return <SupportCSAT />
  if (route === "/tickets/q/customer-support") return <CustomerSupportQueue />
  if (route === "/tickets/q/it")         return <ITTicketsQueue />
  if (route === "/tickets/q/facilities") return <FacilitiesTicketsQueue />
  if (route === "/tickets/q/hr")         return <HRCasesQueue />
  if (route === "/tickets/q/project")    return <ProjectIssuesQueue />
  if (route === "/tickets/q/asset")      return <AssetRequestsQueue />

  /* Customer Service (replaces legacy /support; rich KB/CSAT retained) */
  if (route === "/customer-service" || route === "/support" || route === "/support/dashboard") return <CSDashboard />
  if (route === "/customer-service/calls")   return <CSVoice />
  if (route === "/customer-service/inbox")   return <CSInbox />
  if (route === "/customer-service/queues" || route === "/support/queues")  return <CSQueues />
  if (route === "/customer-service/agents")  return <CSAgents />
  if (route === "/customer-service/reports") return <CSReports />
  if (route === "/customer-service/tickets" || route === "/support/tickets") return <CustomerSupportQueue />
  if (route.startsWith("/customer-service/tickets/")) return <CustomerSupportQueue />
  if (route === "/support/kb")               return <SupportKnowledgeBase />
  if (route === "/support/csat")             return <SupportCSAT />

  /* IT */
  if (route === "/it")          return <ITDashboard />
  if (route === "/it/tickets")  return <ITTicketsQueue />
  if (route === "/it/devices")  return <ITDevices />
  if (route === "/it/software") return <ITSoftware />
  if (route === "/it/access")   return <ITAccess />

  /* Facilities */
  if (route === "/facilities")             return <FacDashboard />
  if (route === "/facilities/tickets")     return <FacilitiesTicketsQueue />
  if (route === "/facilities/assets")      return <FacAssets />
  if (route === "/facilities/work-orders") return <FacWorkOrders />
  if (route === "/facilities/space")       return <FacSpace />
  if (route === "/facilities/vendors")     return <FacVendors />

  /* Capital module (NEW v3.2 — investor module) */
  if (route === "/capital/launch")    return <LaunchModule />
  if (route === "/capital/investors") return <InvestorHubModule />

  /* Usage Governance (v4.0 — Admin → Usage & Billing / AI Governance) */
  if (route === "/admin/usage" || route === "/admin/billing")    return <UsageBilling />
  if (route === "/admin/ai-governance" || route === "/admin/governance") return <AIGovernance />

  /* Admin / Settings module (Tenant-B — v3.3) */
  if (route === "/admin")              return <AdminDashboard />
  if (route === "/admin/companies")    return <AdminCompanies />
  if (route === "/admin/branches")     return <AdminBranches />
  if (route === "/admin/users")        return <AdminUsersRoles />
  if (route === "/admin/workflows")    return <AdminApprovalWorkflows />
  if (route === "/admin/numbering")    return <AdminNumbering />
  if (route === "/admin/currencies")   return <AdminCurrencies />
  if (route === "/admin/taxes")        return <AdminTaxes />
  if (route === "/admin/integrations") return <AdminIntegrations />
  if (route === "/admin/audit")        return <AdminAuditLogs />

  /* Settings Center (full-page route; section after the slash) */
  if (route === "/settings" || route.startsWith("/settings/")) {
    const sec = route.startsWith("/settings/") ? route.slice("/settings/".length) : "profile"
    return (
      <>
        <TopBar breadcrumb={[{ label: "Settings" }]} />
        <div className="flex-1 min-h-0"><SettingsCenter initialSection={sec} /></div>
      </>
    )
  }

  /* Catch-all stub */
  return <StubPage route={route} />
}

function StubPage({ route }: { route: string }) {
  const { push } = useRouter()
  const last = route.split("/").filter(Boolean).pop() ?? "Page"
  const label = last.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase())
  return (
    <>
      <TopBar breadcrumb={route.split("/").filter(Boolean).map(seg => ({ label: seg.replace(/-/g, " ").replace(/\b\w/g, c => c.toUpperCase()) }))} />
      <div className="flex-1 flex items-center justify-center bg-app">
        <div className="max-w-md text-center px-6 py-12 bg-surface border border-divider rounded-lg">
          <div className="size-12 mx-auto rounded-full bg-cream flex items-center justify-center text-ink-mute mb-4">
            <Icon name="info" size={20} />
          </div>
          <h2 className="font-serif text-2xl text-ink mb-2">{label}</h2>
          <p className="text-xs text-ink-mute mb-4">
            This route isn't built in this prototype yet. Try the sidebar, or press <kbd className="font-mono text-[10px] bg-cream border border-divider rounded px-1 py-px">⌘K</kbd>.
          </p>
          <div className="flex items-center justify-center gap-2">
            <Button variant="ghost" size="sm" onClick={() => push("/home")}>Home</Button>
            <Button variant="primary" size="sm" onClick={() => push("/products/irq")}>Open Item Requests</Button>
          </div>
        </div>
      </div>
    </>
  )
}

// SANDBOX
;(globalThis as any).renderRoute = renderRoute
;(globalThis as any).StubPage = StubPage

})();/*__IIFE_WRAP_END__*/
