/*
 * cenora-bundle-4-screens-b.tsx — auto-generated. DO NOT EDIT.
 */

/* ═════ apps/web/components/reports/ask-ai.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/reports/ask-ai.tsx
 * ----------------------------------------------------------------------------
 * Screen 20 — Ask AI Report. Full-page workspace where the user types a
 * natural-language question and AI returns: explanation, generated SQL,
 * rendered table or chart, plus "Save to library" + "Schedule" actions.
 * ============================================================================
 */

function AskAIReport() {
  const [question, setQuestion] = React.useState("Show me top 5 vendors by YTD spend with their on-time delivery rate and any overdue POs.")
  const [provider, setProvider] = React.useState<"claude" | "gpt" | "gemini">("claude")
  const [view, setView] = React.useState<"table" | "chart" | "sql">("table")
  const [generated, setGenerated] = React.useState(true)
  const [thinking, setThinking] = React.useState(false)

  function regen() {
    setThinking(true)
    setTimeout(() => { setThinking(false); setGenerated(true) }, 900)
  }

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Reports" }, { label: "Ask AI" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download" disabled={!generated}>Export CSV</Button>
            <Button variant="ghost" leadingIcon="calendar" disabled={!generated}>Schedule</Button>
            <Button variant="primary" leadingIcon="star" disabled={!generated}>Save to library</Button>
          </>
        }
      />

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

          {/* Question card */}
          <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 flex items-center gap-1.5">
              <Icon name="sparkle" size={11} strokeWidth={2.5} />
              Cenora AI · {provider === "claude" ? "Claude 3.5 Sonnet" : provider === "gpt" ? "GPT-4o" : "Gemini 1.5 Pro"}
            </div>
            <h1 className="font-serif text-[24px] mt-2 leading-snug">Ask anything about your data.</h1>
            <p className="text-[12.5px] text-white/65 mt-1 max-w-[720px]">
              AI sees your full data graph — POs, IRs, GL, employees, inventory — and translates plain English to SQL.
              Save useful reports to your library or schedule them.
            </p>
            <div className="bg-surface text-ink rounded-md p-3 mt-4 flex items-start gap-3">
              <span className="size-8 rounded-md bg-accent-soft text-accent-deep flex items-center justify-center shrink-0 mt-px">
                <Icon name="sparkle" size={14} strokeWidth={2.5} />
              </span>
              <textarea value={question} onChange={e => setQuestion(e.target.value)} rows={2}
                className="flex-1 bg-transparent outline-none border-none resize-none text-[13px] leading-relaxed placeholder:text-ink-mute" />
              <div className="flex flex-col items-end gap-1.5 shrink-0">
                <Button variant="primary" leadingIcon={thinking ? "clock" : "arrow-right"} onClick={regen} disabled={thinking}>
                  {thinking ? "Thinking…" : generated ? "Refine" : "Generate"}
                </Button>
                <div className="text-[10px] text-ink-mute">⌘ + Enter</div>
              </div>
            </div>
            <div className="flex items-center gap-2 mt-3 text-[10.5px] text-white/65 flex-wrap">
              <span className="text-white/40">Try:</span>
              {[
                "Cash forecast for next 30 days by entity",
                "Bottom 10 SKUs by turn — last 90 days",
                "AR aging by customer over 60 days",
                "April spend by category vs March",
              ].map(p => (
                <button key={p} onClick={() => { setQuestion(p); setGenerated(false) }}
                  className="bg-white/10 hover:bg-white/20 text-white px-2.5 py-1 rounded-full text-[10.5px] transition-colors">
                  {p}
                </button>
              ))}
            </div>
          </div>

          {!generated && !thinking && (
            <Card>
              <div className="p-10 text-center">
                <span className="size-12 mx-auto rounded-full bg-cream text-ink-mute flex items-center justify-center mb-4">
                  <Icon name="sparkle" size={18} />
                </span>
                <div className="font-serif text-lg text-ink">Press <kbd className="font-mono text-[12px] bg-cream px-1.5 py-0.5 rounded border border-divider">⌘ ↵</kbd> to generate this report.</div>
                <div className="text-[12px] text-ink-mute mt-1">AI will draft the SQL, render the result, and tell you what's interesting.</div>
              </div>
            </Card>
          )}

          {generated && (
            <>
              {/* Result header */}
              <Card>
                <div className="p-4 grid grid-cols-[1fr_auto_auto] gap-4 items-center border-b border-divider">
                  <div className="flex items-start gap-3">
                    <span className="size-9 rounded-md bg-accent-soft text-accent-deep flex items-center justify-center shrink-0">
                      <Icon name="sparkle" size={15} strokeWidth={2.5} />
                    </span>
                    <div>
                      <div className="text-[13px] font-semibold text-ink">Top 5 Vendors — YTD Spend, On-Time, Overdue POs</div>
                      <div className="text-[11px] text-ink-mute mt-0.5">
                        Joined <code className="font-mono">cnr_vendor</code> ↔ <code className="font-mono">cnr_po</code>. Filtered by entity = HVPH, status &lt;&gt; cancelled. Ordered by YTD spend desc. <strong className="text-ink-soft">5 rows · 0.4s</strong>
                      </div>
                    </div>
                  </div>
                  <Badge variant="success" dot>0 errors</Badge>
                  <ViewToggle value={view} onChange={(v) => setView(v as any)} options={[
                    { value: "table", label: "Table", icon: "list" },
                    { value: "chart", label: "Chart", icon: "chart" },
                    { value: "sql",   label: "SQL",   icon: "doc"   },
                  ]} />
                </div>
                {view === "table" && <ResultTable />}
                {view === "chart" && <ResultChart />}
                {view === "sql"   && <ResultSQL />}
              </Card>

              {/* AI commentary */}
              <Card>
                <CardHeader>
                  <CardTitle><span className="text-accent-deep mr-1">✦</span> AI Notes</CardTitle>
                </CardHeader>
                <div className="p-4 text-[12.5px] text-ink-soft space-y-2 leading-relaxed">
                  <p>SteelMark dominates spend (₱184.6k YTD) but their on-time rate has dropped 14 points since Jan. Two POs are overdue, including the open Steel Rebar request from Apr 8. <strong className="text-ink">Consider rotating to Manila Iron Works for the next quarter</strong> — they're price-competitive and at 96% on-time.</p>
                  <p>Chem Supplies Corp has the worst on-time (64%) but the lowest YTD spend; consolidating with one of the higher performers may save handling overhead.</p>
                  <div className="flex flex-wrap gap-1.5 mt-3 pt-3 border-t border-divider-soft">
                    <button className="text-[11px] bg-cream border border-divider rounded-full px-2.5 py-1 hover:border-brand-mid hover:text-brand">Compare SteelMark vs Manila Iron Works</button>
                    <button className="text-[11px] bg-cream border border-divider rounded-full px-2.5 py-1 hover:border-brand-mid hover:text-brand">Drill into Apr POs by SteelMark</button>
                    <button className="text-[11px] bg-cream border border-divider rounded-full px-2.5 py-1 hover:border-brand-mid hover:text-brand">Schedule weekly to my email</button>
                  </div>
                </div>
              </Card>
            </>
          )}
        </div>
      </div>
    </>
  )
}

function ResultTable() {
  const rows = [
    { rank: 1, vendor: "SteelMark Industries",   ytd: 184600, onTime: 78, overdue: 2 },
    { rank: 2, vendor: "Rhino Cast Industries",  ytd: 142000, onTime: 91, overdue: 0 },
    { rank: 3, vendor: "Pacific Solar PH",       ytd: 96400,  onTime: 94, overdue: 0 },
    { rank: 4, vendor: "Apex Kitchen Supplies",  ytd: 42100,  onTime: 88, overdue: 0 },
    { rank: 5, vendor: "Lydia V. Arellano",      ytd: 30000,  onTime: 100, overdue: 0 },
  ]
  return (
    <table className="w-full text-[12px]">
      <thead>
        <tr className="bg-cream text-left text-[10px] font-bold uppercase tracking-wider text-ink-mute">
          <th className="px-4 py-2 text-right">#</th>
          <th>Vendor</th>
          <th className="text-right">YTD Spend</th>
          <th className="text-right">On-Time</th>
          <th className="text-right">Overdue POs</th>
        </tr>
      </thead>
      <tbody>
        {rows.map(r => (
          <tr key={r.rank} className="border-t border-divider-soft">
            <td className="px-4 py-2.5 text-right text-ink-mute font-mono">{r.rank}</td>
            <td className="font-medium text-ink">{r.vendor}</td>
            <td className="text-right font-mono font-semibold">{formatCurrency(r.ytd)}</td>
            <td className="text-right">
              <span className={cn(
                "font-mono font-bold",
                r.onTime >= 90 ? "text-success" : r.onTime >= 75 ? "text-warn" : "text-danger"
              )}>{r.onTime}%</span>
            </td>
            <td className="text-right">
              {r.overdue > 0 ? <Badge variant="danger">{r.overdue}</Badge> : <Badge variant="success">0</Badge>}
            </td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}

function ResultChart() {
  const rows = [
    { vendor: "SteelMark",   ytd: 184600, pct: 100 },
    { vendor: "Rhino Cast",  ytd: 142000, pct: 77 },
    { vendor: "Pacific Solar", ytd: 96400, pct: 52 },
    { vendor: "Apex Kitchen", ytd: 42100,  pct: 23 },
    { vendor: "Lydia A.",    ytd: 30000,  pct: 16 },
  ]
  return (
    <div className="p-4 space-y-3">
      {rows.map(r => (
        <div key={r.vendor} className="grid grid-cols-[140px_1fr_auto] gap-3 items-center text-[12px]">
          <span className="text-ink-soft truncate">{r.vendor}</span>
          <div className="h-3 rounded-full bg-cream-deep overflow-hidden">
            <div className="h-full rounded-full bg-gradient-to-r from-brand to-brand-light" style={{ width: `${r.pct}%` }} />
          </div>
          <span className="font-mono font-semibold text-ink">{formatCurrency(r.ytd)}</span>
        </div>
      ))}
    </div>
  )
}

function ResultSQL() {
  const sql = `SELECT
  v.name        AS vendor,
  SUM(po.total) AS ytd_spend,
  AVG(CASE WHEN po.received_on <= po.expected_on THEN 1 ELSE 0 END) * 100 AS on_time_pct,
  COUNT(CASE WHEN po.status = 'overdue' THEN 1 END) AS overdue_pos
FROM cnr_po AS po
JOIN cnr_vendor AS v ON v.id = po.vendor_id
WHERE po.entity_code = 'HVPH'
  AND po.status <> 'cancelled'
  AND po.po_date >= '2026-01-01'
GROUP BY v.id, v.name
ORDER BY ytd_spend DESC
LIMIT 5;`
  return (
    <pre className="p-4 text-[11.5px] font-mono leading-relaxed text-ink-soft bg-cream/50 overflow-x-auto whitespace-pre">{sql}</pre>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/entities/consolidated.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/entities/consolidated.tsx
 * ----------------------------------------------------------------------------
 * Multi-Entity view (renamed from Consolidated).
 *
 * Headline: a VISUAL org selector at top. The user picks any node in the
 * hierarchy and the KPI strip below recomputes for that scope. Three view
 * modes:
 *
 *   Tree         — collapsible hierarchical list (NetSuite-style)
 *   Org Chart    — visual node-link diagram (cards arranged by hierarchy)
 *   Map          — countries pinned with totals
 *
 * Selected scope highlights in the active view AND in the chip. KPI strip
 * below shows scope's consolidated P&L, headcount, cash, and risk.
 * ============================================================================
 */

function Consolidated() {
  const { entities, leafEntities } = useShell()
  const [view, setView]   = React.useState<"tree" | "chart" | "map">("tree")
  const [scopeId, setScopeId] = React.useState<string>("hv")

  /* Derive selected scope's leaf entities */
  const leafIds = React.useMemo(() => collectLeaves(entities, scopeId), [scopeId, entities])
  const scope = entities.find((e: any) => e.id === scopeId)
  const scopeLeaves = leafEntities.filter((e: any) => leafIds.has(e.id))

  /* Mock per-entity P&L numbers (USD-equivalent) */
  const PL = {
    hvph: { rev: 1_180_000, cogs: 820_000, opex: 168_000, hc: 412, ar: 224_000, ap: 142_000 },
    hvsg: { rev:   840_000, cogs: 480_000, opex: 180_000, hc: 168, ar: 96_000,  ap: 58_000  },
    hvhk: { rev:   320_000, cogs: 168_000, opex: 78_000,  hc: 84,  ar: 42_000,  ap: 24_000  },
    hvau: { rev:   980_000, cogs: 612_000, opex: 210_000, hc: 142, ar: 188_000, ap: 96_000  },
    hvin: { rev:   620_000, cogs: 410_000, opex: 124_000, hc: 464, ar: 88_000,  ap: 62_000  },
  } as any

  const totals = scopeLeaves.reduce((acc, e: any) => {
    const p = PL[e.id]
    if (!p) return acc
    return {
      rev: acc.rev + p.rev,
      cogs: acc.cogs + p.cogs,
      opex: acc.opex + p.opex,
      hc: acc.hc + p.hc,
      ar: acc.ar + p.ar,
      ap: acc.ap + p.ap,
    }
  }, { rev: 0, cogs: 0, opex: 0, hc: 0, ar: 0, ap: 0 })
  const gp     = totals.rev - totals.cogs
  const margin = totals.rev > 0 ? (gp / totals.rev) : 0
  const ebitda = gp - totals.opex

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Consolidated" }]} />
      <ActionBar
        title="Multi-Entity"
        status={<>
          <span>Scope: <span className="font-semibold text-ink">{scope?.name}</span></span>
          <span className="mx-1.5">·</span>
          <span>{scopeLeaves.length} entit{scopeLeaves.length === 1 ? "y" : "ies"} · {countriesOf(scopeLeaves)} countr{countriesOf(scopeLeaves) === 1 ? "y" : "ies"}</span>
        </>}
        view={
          <div className="inline-flex h-8 items-center gap-0.5 rounded-md bg-cream p-0.5 border border-divider">
            {(["tree", "chart", "map"] as const).map(v => (
              <button key={v} onClick={() => setView(v)}
                className={cn(
                  "px-3 h-7 text-[11px] font-semibold rounded-[5px] inline-flex items-center gap-1.5",
                  view === v ? "bg-surface text-ink shadow-sm" : "text-ink-mute hover:text-ink"
                )}>
                <Icon name={v === "tree" ? "list" : v === "chart" ? "shield" : "map-pin"} size={11} />
                {v === "tree" ? "Tree" : v === "chart" ? "Org chart" : "Map"}
              </button>
            ))}
          </div>
        }
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</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">

          {/* Visual selector */}
          <div className="bg-surface border border-divider rounded-lg p-4">
            {view === "tree"  && <TreeSelector  entities={entities} scopeId={scopeId} onPick={setScopeId} />}
            {view === "chart" && <OrgChartView  entities={entities} scopeId={scopeId} onPick={setScopeId} />}
            {view === "map"   && <MapView       leafEntities={leafEntities} scopeLeafIds={leafIds} onPick={setScopeId} entities={entities} />}
          </div>

          {/* KPI strip — recomputes for active scope */}
          <div className="grid grid-cols-[1.6fr_1fr_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">{scope?.name} · April 2026 (USD)</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">{formatCompact(totals.rev)}</div>
              <div className="text-[12px] text-white/70 mt-1.5">Consolidated revenue · {scopeLeaves.length} entities · post-elimination</div>
            </div>
            <KpiTile label="Gross profit"  value={formatCompact(gp)} sub={`${Math.round(margin * 100)}% margin`} accent="success" />
            <KpiTile label="EBITDA"        value={formatCompact(ebitda)} sub="After opex" accent="info" />
            <KpiTile label="Headcount"     value={totals.hc.toLocaleString()} sub="Full-time" accent="brand" />
            <KpiTile label="AR"            value={formatCompact(totals.ar)} sub="Outstanding" accent="warn" />
            <KpiTile label="AP"            value={formatCompact(totals.ap)} sub="Due" accent="danger" />
          </div>

          {/* Entity P&L table */}
          <Card>
            <CardHeader>
              <div>
                <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">P&L by entity</div>
                <div className="font-serif text-[18px] text-ink mt-0.5">April 2026 · USD-equivalent</div>
              </div>
              <Button variant="ghost" size="sm" leadingIcon="external">Drill into GL</Button>
            </CardHeader>
            <div className="grid grid-cols-[1.2fr_120px_120px_120px_120px_140px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-y border-divider">
              <span>Entity</span>
              <span className="text-right">Revenue</span>
              <span className="text-right">COGS</span>
              <span className="text-right">Opex</span>
              <span className="text-right">EBITDA</span>
              <span className="text-right">Margin</span>
            </div>
            {scopeLeaves.map(e => {
              const p = PL[e.id]
              const eGp = p.rev - p.cogs
              const eEb = eGp - p.opex
              const eMg = eGp / p.rev
              return (
                <div key={e.id} className="grid grid-cols-[1.2fr_120px_120px_120px_120px_140px] px-4 py-2.5 items-center border-t border-divider-soft hover:bg-cream/40 text-[12px]">
                  <div>
                    <div className="font-mono font-bold text-brand-mid">{e.code}</div>
                    <div className="text-[10.5px] text-ink-mute">{e.flag} {e.name}</div>
                  </div>
                  <span className="font-mono text-right">{formatCompact(p.rev)}</span>
                  <span className="font-mono text-right text-ink-mute">{formatCompact(p.cogs)}</span>
                  <span className="font-mono text-right text-ink-mute">{formatCompact(p.opex)}</span>
                  <span className={cn("font-mono text-right font-bold", eEb > 0 ? "text-success" : "text-danger")}>{formatCompact(eEb)}</span>
                  <span className="font-mono text-right">
                    <div className="inline-flex items-center gap-1.5">
                      <div className="w-16 h-1.5 bg-cream rounded-full overflow-hidden">
                        <div className="h-full bg-accent" style={{ width: `${eMg * 100 / 0.5 * 100}%` }} />
                      </div>
                      <span className="text-ink font-semibold w-10 text-right">{Math.round(eMg * 100)}%</span>
                    </div>
                  </span>
                </div>
              )
            })}
          </Card>

          {/* Group risk callouts */}
          <Card>
            <CardHeader>
              <div>
                <div className="text-[10px] uppercase tracking-wider font-bold text-accent-deep">AI · Group risk flags</div>
                <div className="font-serif text-[18px] text-ink mt-0.5">Cross-entity anomalies</div>
              </div>
            </CardHeader>
            <div className="p-4 space-y-2">
              <RiskRow severity="high"   text="HVIN AR aging spike — receivables > 60 days up 34% vs last month" entity="HVIN" />
              <RiskRow severity="med"    text="HVHK margin compression — gross margin dropped from 48% to 43%" entity="HVHK" />
              <RiskRow severity="med"    text="Intercompany imbalance between HVPH and HVSG — needs review" entity="HVPH ⇄ HVSG" />
              <RiskRow severity="low"    text="HVAU operating expenses 8% over plan in April"                  entity="HVAU" />
            </div>
          </Card>
        </div>
      </div>
    </>
  )
}

/* ─── Helpers ─────────────────────────────────────────────────────────── */
function collectLeaves(entities: any[], rootId: string): Set<string> {
  const out = new Set<string>()
  function walk(id: string) {
    const node = entities.find(e => e.id === id)
    if (!node) return
    if (node.kind === "entity") { out.add(id); return }
    for (const c of entities.filter(e => e.parentId === id)) walk(c.id)
  }
  walk(rootId)
  return out
}
function countriesOf(leaves: any[]) {
  return new Set(leaves.map(e => e.country)).size
}

/* ─── Tree selector ───────────────────────────────────────────────────── */
function TreeSelector({ entities, scopeId, onPick }: { entities: any[]; scopeId: string; onPick: (id: string) => void }) {
  function row(e: any, depth: number): React.ReactNode {
    const children = entities.filter(x => x.parentId === e.id)
    const selected = e.id === scopeId
    return (
      <React.Fragment key={e.id}>
        <button onClick={() => onPick(e.id)}
          className={cn(
            "w-full text-left flex items-center gap-2 px-2 py-1.5 rounded-md transition-colors",
            selected ? "bg-brand-soft" : "hover:bg-cream"
          )} style={{ paddingLeft: 8 + depth * 24 }}>
          {depth > 0 && <span className="text-ink-faint text-[14px] leading-none -mt-1" aria-hidden>└</span>}
          {e.kind === "group" ? (
            <span className={cn(
              "size-7 rounded inline-flex items-center justify-center shrink-0",
              selected ? "bg-brand text-white" : "bg-accent-soft text-accent-deep"
            )}>
              <Icon name={depth === 0 ? "globe" : "layers"} size={13} />
            </span>
          ) : (
            <span className={cn(
              "size-7 rounded font-mono text-[9px] font-bold flex items-center justify-center shrink-0",
              selected ? "bg-brand text-white" : "bg-cream-deep text-ink-soft"
            )}>{e.code}</span>
          )}
          <div className="flex-1 min-w-0">
            <div className="flex items-center gap-1.5">
              <span className="text-[12px]">{e.flag}</span>
              <span className={cn("text-[13px]", selected ? "font-bold text-brand" : "text-ink font-medium")}>{e.name}</span>
            </div>
            <div className="text-[10px] text-ink-mute mt-0.5">{e.kind === "group" ? e.blurb : `${e.country} · ${e.currency}`}</div>
          </div>
          {selected && <Badge variant="brand">Active scope</Badge>}
        </button>
        {children.map(c => row(c, depth + 1))}
      </React.Fragment>
    )
  }
  const roots = entities.filter(e => e.parentId === null)
  return (
    <div className="space-y-px">
      {roots.map(r => row(r, 0))}
    </div>
  )
}

/* ─── Org chart view ──────────────────────────────────────────────────── */
function OrgChartView({ entities, scopeId, onPick }: { entities: any[]; scopeId: string; onPick: (id: string) => void }) {
  const root = entities.find(e => e.parentId === null)
  if (!root) return null
  const regions = entities.filter(e => e.parentId === root.id)
  return (
    <div className="py-3">
      <div className="flex flex-col items-center">
        {/* Root */}
        <OrgNode e={root} selected={root.id === scopeId} onPick={onPick} large />
        <div className="w-px h-5 bg-divider-strong" />
        {/* Regions */}
        <div className="flex items-start gap-12">
          {regions.map(r => (
            <div key={r.id} className="flex flex-col items-center">
              <OrgNode e={r} selected={r.id === scopeId} onPick={onPick} />
              <div className="w-px h-5 bg-divider-strong" />
              <div className="flex items-start gap-3">
                {entities.filter(e => e.parentId === r.id).map(leaf => (
                  <OrgNode key={leaf.id} e={leaf} selected={leaf.id === scopeId} onPick={onPick} />
                ))}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  )
}
function OrgNode({ e, selected, onPick, large }: { e: any; selected: boolean; onPick: (id: string) => void; large?: boolean }) {
  return (
    <button onClick={() => onPick(e.id)}
      className={cn(
        "border-2 rounded-lg px-3 py-2 text-center transition-all",
        large ? "min-w-[220px]" : "min-w-[140px]",
        selected
          ? "bg-brand-soft border-brand shadow-md"
          : e.kind === "group"
            ? "bg-accent-soft/50 border-accent-soft hover:border-accent"
            : "bg-surface border-divider hover:border-divider-strong"
      )}>
      <div className={cn("text-[18px]", large && "text-[24px]")}>{e.flag}</div>
      <div className={cn(
        "font-mono font-bold mt-0.5",
        large ? "text-[12px]" : "text-[10.5px]",
        selected ? "text-brand" : "text-brand-mid"
      )}>{e.code}</div>
      <div className={cn(
        "leading-tight mt-0.5",
        large ? "text-[12px] font-semibold text-ink" : "text-[10.5px] font-medium text-ink-soft"
      )}>{large ? e.name : e.country}</div>
      {selected && <div className="mt-1.5 text-[8.5px] font-bold uppercase tracking-wider text-brand-mid">Active scope</div>}
    </button>
  )
}

/* ─── Map view ────────────────────────────────────────────────────────── */
function MapView({ leafEntities, scopeLeafIds, onPick, entities }: { leafEntities: any[]; scopeLeafIds: Set<string>; onPick: (id: string) => void; entities: any[] }) {
  return (
    <div className="relative bg-gradient-to-br from-info-soft via-cream to-brand-soft rounded-md overflow-hidden" style={{ height: 360 }}>
      <svg viewBox="0 0 1000 500" className="absolute inset-0 w-full h-full" preserveAspectRatio="xMidYMid meet">
        {/* Simulated continents */}
        <path d="M 100 200 Q 140 160 200 180 T 320 200 Q 380 220 440 200 L 480 240 L 380 280 L 200 290 Z" fill="var(--brand-soft)" stroke="var(--divider)" strokeWidth="1.5" />
        <path d="M 480 200 Q 520 180 560 200 L 580 220 L 540 260 L 500 250 Z" fill="var(--brand-soft)" stroke="var(--divider)" strokeWidth="1.5" />
        <path d="M 480 360 Q 540 340 600 360 T 720 380 Q 760 400 740 420 L 600 440 L 500 420 Z" fill="var(--brand-soft)" stroke="var(--divider)" strokeWidth="1.5" />

        {/* Pins */}
        {leafEntities.map(e => {
          const sel = scopeLeafIds.has(e.id)
          const pos = MAP_POS[e.id as keyof typeof MAP_POS] ?? { x: 500, y: 250 }
          return (
            <g key={e.id} className="cursor-pointer" onClick={() => onPick(e.id)}>
              {sel && <circle cx={pos.x} cy={pos.y} r="22" fill="var(--brand)" opacity="0.18" />}
              <circle cx={pos.x} cy={pos.y} r={sel ? 16 : 12} fill={sel ? "var(--brand)" : "white"} stroke="var(--brand)" strokeWidth="2.5" />
              <text x={pos.x} y={pos.y + 4} fontSize={sel ? 11 : 9} fontWeight="bold" textAnchor="middle" fill={sel ? "white" : "var(--brand)"}>
                {e.code.replace("HV", "")}
              </text>
              <text x={pos.x} y={pos.y + 32} fontSize="10" fontWeight="600" textAnchor="middle" fill="var(--ink)">{e.country}</text>
            </g>
          )
        })}
      </svg>

      <div className="absolute top-3 left-3 bg-surface border border-divider rounded-md p-2 shadow-sm text-[10.5px] flex items-center gap-3">
        <button onClick={() => onPick("hv")}
          className={cn(
            "px-2.5 py-1 rounded font-semibold",
            scopeLeafIds.size === leafEntities.length ? "bg-brand text-white" : "text-ink-soft hover:bg-cream"
          )}>
          🌐 All entities
        </button>
        <button onClick={() => onPick("apac")}
          className={cn(
            "px-2.5 py-1 rounded font-semibold",
            scopeLeafIds.size === 3 && !scopeLeafIds.has("hvau") ? "bg-brand text-white" : "text-ink-soft hover:bg-cream"
          )}>APAC Hub</button>
        <button onClick={() => onPick("aps")}
          className={cn(
            "px-2.5 py-1 rounded font-semibold",
            scopeLeafIds.size === 2 && scopeLeafIds.has("hvau") ? "bg-brand text-white" : "text-ink-soft hover:bg-cream"
          )}>Asia Pacific South</button>
      </div>
      <div className="absolute bottom-3 right-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: Mapbox.
      </div>
    </div>
  )
}

const MAP_POS = {
  hvph: { x: 700, y: 240 },
  hvsg: { x: 620, y: 320 },
  hvhk: { x: 700, y: 190 },
  hvau: { x: 800, y: 420 },
  hvin: { x: 520, y: 250 },
}

function RiskRow({ severity, text, entity }: { severity: "low" | "med" | "high"; text: string; entity: string }) {
  const tone = severity === "high" ? "danger" : severity === "med" ? "warn" : "neutral"
  return (
    <div className="flex items-center gap-2.5 p-2 bg-cream/40 rounded">
      <Badge variant={tone as any}>{severity.toUpperCase()}</Badge>
      <span className="text-[12px] text-ink flex-1">{text}</span>
      <span className="font-mono text-[10.5px] text-ink-mute">{entity}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/multi-entity/screens.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/multi-entity/screens.tsx
 * ----------------------------------------------------------------------------
 * Multi-Entity module — 5 sub-screens:
 *
 *   SubsidiaryNavigator    — entity tree + per-entity KPI strip + drill-down
 *   IntercompanyTxns       — cross-entity AR/AP, match/dispute
 *   Eliminations           — auto + manual elimination workbench
 *   InterEntityTransfers   — cash / inventory / fixed-asset / service-charge
 *   ConsolidationRules     — ownership, FX policy, elimination, MI, rollup
 *
 * Each component is a top-level page (own TopBar + ActionBar).
 * ============================================================================
 */

declare const IC_TXNS: any[]
declare const IC_ELIMS: any[]
declare const IE_TRANSFERS: any[]
declare const CONSOL_RULES: any[]
declare const SUBSIDIARY_KPIS: any[]

/* helpers */
const fmt0 = (n: number) => n.toLocaleString(undefined, { maximumFractionDigits: 0 })
const fmtk = (n: number) => `$${Math.round(n / 1000)}k`
const fmtm = (n: number) => `$${(n / 1_000_000).toFixed(2)}M`

/* ════════════════════════════════════════════════════════════════════════
 * SUBSIDIARY NAVIGATOR
 * ════════════════════════════════════════════════════════════════════════ */
function SubsidiaryNavigator() {
  const { entities, leafEntities, setEntity, entity: activeEntity } = useShell()
  const { push } = useRouter()
  const [selected, setSelected] = React.useState<string>(activeEntity?.id ?? "hvph")

  const sel = entities.find((e: any) => e.id === selected) ?? leafEntities[0]
  const selKpi = SUBSIDIARY_KPIS.find(k => k.entityCode === sel?.code)

  /* Group entities for rendering */
  const apac = entities.filter((e: any) => e.parentId === "apac")
  const aps  = entities.filter((e: any) => e.parentId === "aps")

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Subsidiary Navigator" }]} />
      <ActionBar
        title="Subsidiary Navigator"
        status={<>
          <span><strong className="text-ink">5 entities</strong> in 2 regions across 5 countries</span>
          <span className="mx-1.5">·</span>
          <span>April 2026 · USD presentation</span>
        </>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm" onClick={() => (window as any).cenoraToast?.("Add subsidiary", { sub: "Opens the entity onboarding wizard." })}>Add subsidiary</Button>}
        secondary={<Button variant="ghost" leadingIcon="external" size="sm" onClick={() => push("/multi-entity/consolidated")}>Consolidate</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-[1480px] mx-auto p-6 grid grid-cols-[300px_1fr] gap-5">

          {/* LEFT — entity tree */}
          <aside className="bg-surface border border-divider rounded-lg p-3 self-start">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute px-2 pb-2 border-b border-divider mb-2">Entities</div>
            <TreeNode entity={entities.find((e: any) => e.id === "hv")!} selected={selected} onSelect={setSelected} depth={0} />
            <div className="pl-3 mt-1 space-y-0.5">
              <TreeNode entity={entities.find((e: any) => e.id === "apac")!} selected={selected} onSelect={setSelected} depth={1} />
              <div className="pl-3 space-y-0.5">
                {apac.map((e: any) => <TreeNode key={e.id} entity={e} selected={selected} onSelect={setSelected} depth={2} />)}
              </div>
              <TreeNode entity={entities.find((e: any) => e.id === "aps")!} selected={selected} onSelect={setSelected} depth={1} />
              <div className="pl-3 space-y-0.5">
                {aps.map((e: any) => <TreeNode key={e.id} entity={e} selected={selected} onSelect={setSelected} depth={2} />)}
              </div>
            </div>
            <button
              onClick={() => (window as any).cenoraToast?.("Add subsidiary", { sub: "Opens the entity onboarding wizard." })}
              className="w-full mt-3 px-2 py-2 rounded-md border border-dashed border-divider text-[11px] font-semibold text-ink-mute hover:text-brand hover:border-brand-mid inline-flex items-center justify-center gap-1.5">
              <Icon name="plus" size={11} /> Add subsidiary
            </button>
          </aside>

          {/* RIGHT — selected entity detail */}
          <section className="space-y-4">
            {/* Hero card */}
            <div className="bg-surface border border-divider rounded-lg overflow-hidden">
              <div className="bg-gradient-to-br from-brand to-brand-light text-white px-6 py-5 flex items-center gap-4">
                <div className="size-14 rounded-md bg-white/15 backdrop-blur flex items-center justify-center font-mono font-bold text-[18px]">{sel?.code}</div>
                <div className="flex-1 min-w-0">
                  <div className="text-[10px] uppercase tracking-wider text-accent font-bold">{sel?.kind === "group" ? "Group · consolidation" : "Legal entity"}</div>
                  <div className="font-serif text-[28px] leading-tight mt-0.5">{sel?.flag} {sel?.name}</div>
                  <div className="text-[12px] text-white/70 mt-1">
                    {sel?.country} · {sel?.currency} · {sel?.city}
                    {selKpi && <> · <strong className="text-white/95">{selKpi.headcount.toLocaleString()}</strong> employees</>}
                  </div>
                </div>
                <Button variant="primary" size="sm" onClick={() => { setEntity(sel.id); (window as any).cenoraToast?.(`Scoped to ${sel.code}`, { variant: "success" }) }}>Switch to {sel?.code}</Button>
              </div>

              {selKpi ? (
                <div className="grid grid-cols-5 divide-x divide-divider border-t border-divider">
                  <KpiCell label="Revenue YTD"     value={fmtm(selKpi.revenueYTD)} sub={`${Math.round(selKpi.netIncomeYTD / selKpi.revenueYTD * 100)}% NI margin`} accent="brand" />
                  <KpiCell label="Net income YTD"  value={fmtm(selKpi.netIncomeYTD)} sub="Apr 2026" accent="success" />
                  <KpiCell label="AR overdue"      value={fmtk(selKpi.arOverdue)} sub="vs. terms" accent={selKpi.arOverdue > 50000 ? "danger" : "warn"} />
                  <KpiCell label="AP overdue"      value={fmtk(selKpi.apOverdue)} sub="payable" accent="info" />
                  <KpiCell label="Close progress"  value={`${selKpi.closeProgress}%`} sub={selKpi.closeStatus.replace("-", " ")} accent={selKpi.closeStatus === "blocked" ? "danger" : selKpi.closeStatus === "complete" ? "success" : "warn"} />
                </div>
              ) : (
                <div className="px-6 py-5 text-[12px] text-ink-mute">Group rollups appear in the Consolidated dashboard — drill into one of the entities below to see KPIs.</div>
              )}
            </div>

            {/* Flags + close status */}
            {selKpi && (
              <div className="grid grid-cols-2 gap-4">
                <Card>
                  <CardHeader>
                    <div>
                      <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Risk flags</div>
                      <div className="font-serif text-[18px] text-ink mt-0.5">{sel?.code} watch list</div>
                    </div>
                  </CardHeader>
                  <div className="p-3 space-y-2">
                    {selKpi.flags.length === 0 ? (
                      <div className="px-3 py-4 text-center text-[12px] text-success bg-success-soft rounded-md">
                        <Icon name="check" size={14} className="inline mr-1" />
                        No active flags — all green.
                      </div>
                    ) : selKpi.flags.map((f: string, i: number) => (
                      <div key={i} className={cn(
                        "px-3 py-2 rounded-md border flex items-center gap-2 text-[12px]",
                        selKpi.health === "concern" ? "bg-danger-soft border-danger/30 text-danger" : "bg-warn-soft border-warn/30 text-warn"
                      )}>
                        <Icon name="alert-triangle" size={12} />
                        <span className="flex-1">{f}</span>
                      </div>
                    ))}
                  </div>
                </Card>

                <Card>
                  <CardHeader>
                    <div>
                      <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Quick links</div>
                      <div className="font-serif text-[18px] text-ink mt-0.5">Jump into {sel?.code}</div>
                    </div>
                  </CardHeader>
                  <div className="grid grid-cols-2 gap-2 p-3">
                    <QuickLink icon="trending-up" label="Finance dashboard"     href="/finance" />
                    <QuickLink icon="scan"        label="Bank reconciliation"   href="/finance/bank-recon" />
                    <QuickLink icon="doc"         label="Purchase orders"       href="/procurement/po" />
                    <QuickLink icon="users"       label="Employees"             href="/hr/directory" />
                    <QuickLink icon="box"         label="Stock dashboard"       href="/warehouse" />
                    <QuickLink icon="calendar"    label="Compliance calendar"   href="/tax/calendar" />
                  </div>
                </Card>
              </div>
            )}

            {/* All-entity comparison */}
            <Card>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Compare all entities</div>
                  <div className="font-serif text-[18px] text-ink mt-0.5">YTD 2026 · USD-equivalent</div>
                </div>
                <Button variant="ghost" size="sm" leadingIcon="download">Export</Button>
              </CardHeader>
              <div className="grid grid-cols-[80px_1.6fr_120px_120px_120px_120px_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-y border-divider">
                <span>Code</span>
                <span>Entity</span>
                <span className="text-right">Revenue YTD</span>
                <span className="text-right">NI YTD</span>
                <span className="text-right">AR overdue</span>
                <span className="text-right">AP overdue</span>
                <span className="text-right">Close</span>
              </div>
              {SUBSIDIARY_KPIS.map(k => {
                const ent = entities.find((e: any) => e.code === k.entityCode)
                if (!ent) return null
                return (
                  <button key={k.entityCode}
                    onClick={() => setSelected(ent.id)}
                    className={cn(
                      "w-full grid grid-cols-[80px_1.6fr_120px_120px_120px_120px_120px] items-center px-4 py-2.5 text-left border-t border-divider hover:bg-cream-deep transition-colors",
                      selected === ent.id && "bg-brand-soft"
                    )}>
                    <span className="font-mono text-[11px] font-bold text-brand-mid">{k.entityCode}</span>
                    <span className="text-[12.5px] text-ink truncate flex items-center gap-1.5">
                      <span>{ent.flag}</span>{ent.name}
                      <Badge variant={k.health === "good" ? "success" : k.health === "watch" ? "warn" : "danger"}>{k.health}</Badge>
                    </span>
                    <span className="text-right font-mono text-[12px] font-semibold text-ink">{fmtm(k.revenueYTD)}</span>
                    <span className="text-right font-mono text-[12px] text-ink-soft">{fmtm(k.netIncomeYTD)}</span>
                    <span className={cn("text-right font-mono text-[12px]", k.arOverdue > 50000 ? "text-danger font-semibold" : "text-ink-soft")}>{fmtk(k.arOverdue)}</span>
                    <span className="text-right font-mono text-[12px] text-ink-soft">{fmtk(k.apOverdue)}</span>
                    <span className="text-right">
                      <div className="inline-flex items-center gap-1.5">
                        <div className="w-16 h-1.5 bg-cream-deep rounded-full overflow-hidden">
                          <div className={cn(
                            "h-full rounded-full",
                            k.closeStatus === "blocked" ? "bg-danger" : k.closeStatus === "complete" ? "bg-success" : "bg-warn"
                          )} style={{ width: `${k.closeProgress}%` }} />
                        </div>
                        <span className="font-mono text-[10.5px] text-ink-mute">{k.closeProgress}%</span>
                      </div>
                    </span>
                  </button>
                )
              })}
            </Card>
          </section>
        </div>
      </div>
    </>
  )
}

function TreeNode({ entity, selected, onSelect, depth }: { entity: any; selected: string; onSelect: (id: string) => void; depth: number }) {
  const active = entity.id === selected
  return (
    <button
      onClick={() => onSelect(entity.id)}
      className={cn(
        "w-full flex items-center gap-2 px-2 py-1.5 rounded-md text-left transition-colors",
        active ? "bg-brand-soft" : "hover:bg-cream"
      )}>
      {entity.kind === "group" ? (
        <Icon name={depth === 0 ? "globe" : "layers"} size={11} className={active ? "text-brand" : "text-ink-mute"} />
      ) : (
        <span className={cn("font-mono text-[8.5px] font-bold px-1 rounded", active ? "bg-brand text-white" : "bg-cream-deep text-ink-soft")}>{entity.code}</span>
      )}
      <div className="flex-1 min-w-0 flex items-center gap-1.5">
        <span className="text-[10px]">{entity.flag}</span>
        <span className={cn("text-[11.5px] truncate", active ? "text-brand font-semibold" : "text-ink-soft")}>{entity.name}</span>
      </div>
    </button>
  )
}

function KpiCell({ label, value, sub, accent }: { label: string; value: string; sub: string; accent: "brand" | "success" | "warn" | "danger" | "info" }) {
  const tone: any = {
    brand: "text-brand", success: "text-success", warn: "text-warn", danger: "text-danger", info: "text-info",
  }
  return (
    <div className="px-5 py-4">
      <div className="text-[9.5px] font-bold uppercase tracking-wider text-ink-mute">{label}</div>
      <div className={cn("font-serif text-[24px] leading-none mt-1.5", tone[accent])}>{value}</div>
      <div className="text-[10.5px] text-ink-mute mt-1">{sub}</div>
    </div>
  )
}

function QuickLink({ icon, label, href }: { icon: string; label: string; href: string }) {
  const { push } = useRouter()
  return (
    <button onClick={() => push(href)}
      className="flex items-center gap-2 px-3 py-2.5 rounded-md border border-divider hover:border-brand-mid hover:bg-brand-soft transition-colors text-left">
      <Icon name={icon as any} size={13} className="text-brand-mid shrink-0" />
      <span className="text-[12px] font-medium text-ink truncate">{label}</span>
      <Icon name="arrow-right" size={11} className="text-ink-mute ml-auto" />
    </button>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * INTERCOMPANY TRANSACTIONS
 * ════════════════════════════════════════════════════════════════════════ */
function IntercompanyTxns() {
  const [filter, setFilter] = React.useState<"all" | "matched" | "unmatched" | "disputed" | "pending">("all")
  const filtered = filter === "all" ? IC_TXNS : IC_TXNS.filter(t => t.status === filter)

  const counts = {
    all: IC_TXNS.length,
    matched: IC_TXNS.filter(t => t.status === "matched").length,
    unmatched: IC_TXNS.filter(t => t.status === "unmatched").length,
    disputed: IC_TXNS.filter(t => t.status === "disputed").length,
    pending: IC_TXNS.filter(t => t.status === "pending").length,
  }
  const totals = IC_TXNS.reduce((s, t) => s + t.amount, 0)
  const eliminable = IC_TXNS.filter(t => t.status === "matched").reduce((s, t) => s + t.amount, 0)

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Intercompany" }]} />
      <ActionBar
        title="Intercompany Transactions"
        status={<>
          <strong className="text-ink">{counts.all}</strong> txns this period · {fmtk(totals)} gross ·
          <span className="ml-1 text-success font-semibold">{fmtk(eliminable)} eliminable</span>
        </>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New IC invoice</Button>}
        secondary={<>
          <Button variant="ghost" leadingIcon="zap" size="sm">Auto-match</Button>
          <Button variant="ghost" leadingIcon="download" size="sm">Export</Button>
        </>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Reconcile with AI</Button>}
      />

      <Subheader>
        {(["all", "matched", "unmatched", "disputed", "pending"] as const).map(s => (
          <button key={s} onClick={() => setFilter(s)}
            className={cn(
              "px-3 h-7 rounded-full text-[11px] font-semibold inline-flex items-center gap-1.5 border transition-colors",
              filter === s ? "bg-brand text-white border-brand" : "bg-surface border-divider text-ink-soft hover:border-brand-mid"
            )}>
            {s[0].toUpperCase() + s.slice(1)}
            <span className={cn("font-mono text-[10px] px-1 rounded", filter === s ? "bg-white/20" : "bg-cream")}>{counts[s]}</span>
          </button>
        ))}
      </Subheader>

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1480px] mx-auto p-6">
          <Card>
            <div className="grid grid-cols-[110px_80px_80px_1.4fr_140px_120px_140px_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-b border-divider">
              <span>Date</span>
              <span>From</span>
              <span>To</span>
              <span>Description</span>
              <span>Reference</span>
              <span className="text-right">Amount</span>
              <span>Match</span>
              <span>Status</span>
            </div>
            {filtered.map(t => (
              <div key={t.id} className="grid grid-cols-[110px_80px_80px_1.4fr_140px_120px_140px_120px] items-center px-4 py-3 border-t border-divider hover:bg-cream-deep/40 transition-colors text-[12px]">
                <span className="text-ink-soft">{t.date}</span>
                <span className="font-mono text-[11px] font-bold text-brand">{t.fromEntity}</span>
                <span className="font-mono text-[11px] font-bold text-info">{t.toEntity}</span>
                <span className="text-ink">
                  <div className="font-medium">{t.description}</div>
                  {t.flagReason && <div className="text-[10.5px] text-warn mt-0.5">⚠ {t.flagReason}</div>}
                </span>
                <span className="font-mono text-[11px] text-ink-mute">{t.reference}</span>
                <span className="text-right font-mono text-[12px] font-semibold text-ink">{fmt0(t.amount)} <span className="text-[10px] text-ink-mute">{t.currency}</span></span>
                <span className="font-mono text-[10.5px] text-ink-mute">
                  {t.matchReference ? <span className="text-success">✓ {t.matchReference}</span> : "—"}
                </span>
                <span>
                  <Badge variant={t.status === "matched" ? "success" : t.status === "disputed" ? "danger" : t.status === "unmatched" ? "warn" : "neutral"}>
                    {t.status}
                  </Badge>
                </span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * ELIMINATIONS
 * ════════════════════════════════════════════════════════════════════════ */
function Eliminations() {
  const total = IC_ELIMS.reduce((s, e) => s + e.amount, 0)
  const needsReview = IC_ELIMS.filter(e => e.status === "needs-review").length

  const grouped: Record<string, any[]> = {}
  IC_ELIMS.forEach(e => { (grouped[e.category] ??= []).push(e) })
  const labels: Record<string, string> = {
    "ic-revenue":          "IC revenue / cost",
    "ic-ar-ap":            "IC AR / AP balances",
    "profit-in-inventory": "Profit in inventory",
    "investment-in-sub":   "Investment in subsidiary",
    "dividends":           "Intercompany dividends",
  }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Eliminations" }]} />
      <ActionBar
        title="Elimination Workbench"
        status={<>
          April 2026 · <strong className="text-ink">{IC_ELIMS.length} entries</strong> · {fmtm(total)} total ·
          {needsReview > 0 && <span className="ml-1 text-warn font-semibold">{needsReview} need review</span>}
        </>}
        primary={<Button variant="primary" leadingIcon="check" size="sm">Post eliminations</Button>}
        secondary={<>
          <Button variant="ghost" leadingIcon="zap" size="sm">Re-run auto</Button>
          <Button variant="ghost" leadingIcon="plus" size="sm">Manual entry</Button>
        </>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Suggest entries</Button>}
      />

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

          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Total eliminations" value={fmtm(total)}    sub="April 2026" accent="brand" />
            <KpiTile label="Auto-eliminated"    value={fmtk(IC_ELIMS.filter(e => e.status === "auto").reduce((s, e) => s + e.amount, 0))} sub="System-posted" accent="success" />
            <KpiTile label="Manual entries"     value={fmtk(IC_ELIMS.filter(e => e.status === "manual").reduce((s, e) => s + e.amount, 0))} sub="Posted by user" accent="info" />
            <KpiTile label="Needs review"       value={String(needsReview)} sub="Markup ≥ 5% threshold" accent={needsReview > 0 ? "warn" : "success"} />
          </div>

          {Object.keys(grouped).map(cat => (
            <Card key={cat}>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">{labels[cat]}</div>
                  <div className="font-serif text-[18px] text-ink mt-0.5">
                    {grouped[cat].length} entries · {fmtk(grouped[cat].reduce((s, e) => s + e.amount, 0))}
                  </div>
                </div>
              </CardHeader>
              <div className="divide-y divide-divider">
                {grouped[cat].map(e => (
                  <div key={e.id} className="grid grid-cols-[1.8fr_180px_140px_140px_120px] items-center px-4 py-3 hover:bg-cream-deep/40 text-[12px]">
                    <div>
                      <div className="text-ink font-medium">{e.label}</div>
                      <div className="text-[10.5px] text-ink-mute mt-0.5">{e.basis}</div>
                    </div>
                    <div className="font-mono text-[11px] text-brand-mid font-bold">{e.pair}</div>
                    <div className="text-right font-mono text-[13px] font-bold text-ink">{fmt0(e.amount)} <span className="text-[10px] text-ink-mute font-normal">USD</span></div>
                    <div className="text-[10.5px] text-ink-mute">
                      {e.postedBy ? <>by <span className="text-ink-soft font-medium">{e.postedBy}</span><br />{e.postedAt}</> : "—"}
                    </div>
                    <div>
                      <Badge variant={e.status === "auto" ? "info" : e.status === "manual" ? "success" : "warn"}>
                        {e.status === "auto" ? "Auto" : e.status === "manual" ? "Posted" : "Review"}
                      </Badge>
                    </div>
                  </div>
                ))}
              </div>
            </Card>
          ))}
        </div>
      </div>
    </>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * INTER-ENTITY TRANSFERS
 * ════════════════════════════════════════════════════════════════════════ */
function InterEntityTransfers() {
  const [kind, setKind] = React.useState<"all" | "cash" | "inventory" | "fixed-asset" | "service-charge">("all")
  const filtered = kind === "all" ? IE_TRANSFERS : IE_TRANSFERS.filter(t => t.kind === kind)
  const counts = {
    all: IE_TRANSFERS.length,
    cash: IE_TRANSFERS.filter(t => t.kind === "cash").length,
    inventory: IE_TRANSFERS.filter(t => t.kind === "inventory").length,
    "fixed-asset": IE_TRANSFERS.filter(t => t.kind === "fixed-asset").length,
    "service-charge": IE_TRANSFERS.filter(t => t.kind === "service-charge").length,
  }

  const kindIcon: Record<string, string> = { cash: "wallet", inventory: "box", "fixed-asset": "shield", "service-charge": "users" }
  const statusTone: any = { draft: "neutral", approved: "info", posted: "warn", settled: "success" }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Inter-Entity Transfers" }]} />
      <ActionBar
        title="Inter-Entity Transfers"
        status={<><strong className="text-ink">{counts.all}</strong> transfers this period across {IE_TRANSFERS.length} entity pairs</>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New transfer</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
      />

      <Subheader>
        {(["all", "cash", "inventory", "fixed-asset", "service-charge"] as const).map(k => (
          <button key={k} onClick={() => setKind(k)}
            className={cn(
              "px-3 h-7 rounded-full text-[11px] font-semibold inline-flex items-center gap-1.5 border transition-colors",
              kind === k ? "bg-brand text-white border-brand" : "bg-surface border-divider text-ink-soft hover:border-brand-mid"
            )}>
            {k === "all" ? "All" : k.replace("-", " ").replace(/\b\w/g, c => c.toUpperCase())}
            <span className={cn("font-mono text-[10px] px-1 rounded", kind === k ? "bg-white/20" : "bg-cream")}>{counts[k]}</span>
          </button>
        ))}
      </Subheader>

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1480px] mx-auto p-6">
          <Card>
            <div className="grid grid-cols-[110px_140px_1.6fr_220px_140px_120px_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-b border-divider">
              <span>Date</span>
              <span>Type</span>
              <span>Description</span>
              <span>From → To</span>
              <span className="text-right">Amount</span>
              <span>Approver</span>
              <span>Status</span>
            </div>
            {filtered.map(t => (
              <div key={t.id} className="grid grid-cols-[110px_140px_1.6fr_220px_140px_120px_120px] items-center px-4 py-3 border-t border-divider hover:bg-cream-deep/40 text-[12px]">
                <span className="text-ink-soft">{t.date}</span>
                <span className="inline-flex items-center gap-1.5">
                  <Icon name={kindIcon[t.kind] as any} size={12} className="text-ink-mute" />
                  <span className="text-ink-soft capitalize">{t.kind.replace("-", " ")}</span>
                </span>
                <span className="text-ink">
                  <div className="font-medium">{t.description}</div>
                  <div className="font-mono text-[10.5px] text-ink-mute mt-0.5">{t.id}</div>
                </span>
                <span className="font-mono text-[11px] inline-flex items-center gap-1.5">
                  <span className="font-bold text-brand">{t.fromEntity}</span>
                  <Icon name="arrow-right" size={10} className="text-ink-mute" />
                  <span className="font-bold text-info">{t.toEntity}</span>
                </span>
                <span className="text-right font-mono text-[12px] font-semibold text-ink">
                  {fmt0(t.amount)} <span className="text-[10px] text-ink-mute font-normal">{t.currency}</span>
                  {t.fxRateUsed && <div className="text-[9.5px] text-ink-mute font-normal mt-0.5">@ {t.fxRateUsed}</div>}
                </span>
                <span className="text-[11.5px] text-ink-soft">{t.approver ?? <span className="text-ink-mute italic">unsigned</span>}</span>
                <span><Badge variant={statusTone[t.status]}>{t.status}</Badge></span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

/* ════════════════════════════════════════════════════════════════════════
 * CONSOLIDATION RULES
 * ════════════════════════════════════════════════════════════════════════ */
function ConsolidationRules() {
  const grouped: Record<string, any[]> = {}
  CONSOL_RULES.forEach(r => { (grouped[r.category] ??= []).push(r) })
  const labels: Record<string, { title: string; icon: string; sub: string }> = {
    ownership:           { title: "Ownership %",           icon: "shield",       sub: "Who owns what — drives consolidation method" },
    "fx-policy":         { title: "FX translation policy", icon: "globe",        sub: "How foreign currency rolls up into USD presentation" },
    elimination:         { title: "Elimination policies",  icon: "git-branch",   sub: "What gets eliminated, and when" },
    "minority-interest": { title: "Minority interest",     icon: "users",        sub: "MI allocation for partly-owned subs" },
    rollup:              { title: "Region rollups",        icon: "layers",       sub: "How entities aggregate into regions and group" },
  }
  const order = ["ownership", "rollup", "fx-policy", "elimination", "minority-interest"]

  return (
    <>
      <TopBar breadcrumb={[{ label: "Multi-Entity" }, { label: "Consolidation Rules" }]} />
      <ActionBar
        title="Consolidation Rules"
        status={<><strong className="text-ink">{CONSOL_RULES.length} rules</strong> defined · last reviewed Apr 1, 2026</>}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">Add rule</Button>}
        secondary={<>
          <Button variant="ghost" leadingIcon="download" size="sm">Export policy</Button>
          <Button variant="ghost" leadingIcon="external" size="sm">Audit trail</Button>
        </>}
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1280px] mx-auto p-6 space-y-4">
          {order.map(cat => grouped[cat] && (
            <Card key={cat}>
              <CardHeader>
                <div className="flex items-center gap-2.5">
                  <span className="size-8 rounded-md bg-brand-soft text-brand inline-flex items-center justify-center">
                    <Icon name={labels[cat].icon as any} size={14} />
                  </span>
                  <div>
                    <div className="font-serif text-[18px] text-ink leading-none">{labels[cat].title}</div>
                    <div className="text-[10.5px] text-ink-mute mt-0.5">{labels[cat].sub}</div>
                  </div>
                </div>
                <Badge variant="neutral">{grouped[cat].length}</Badge>
              </CardHeader>
              <div className="divide-y divide-divider">
                {grouped[cat].map(r => (
                  <div key={r.id} className="grid grid-cols-[140px_1fr_1.4fr_140px] items-center px-4 py-3 text-[12px] hover:bg-cream-deep/40">
                    <span className="font-mono text-[11px] font-bold text-brand">{r.scope}</span>
                    <span className="text-ink font-medium">{r.rule}</span>
                    <span className="text-ink-soft">{r.value}</span>
                    <span className="text-right text-[10.5px] text-ink-mute">
                      Eff. {r.effectiveFrom}
                      {r.changedBy && <div className="text-[9.5px] mt-0.5">by {r.changedBy}</div>}
                    </span>
                  </div>
                ))}
              </div>
            </Card>
          ))}
        </div>
      </div>
    </>
  )
}

// SANDBOX
Object.assign(globalThis as any, {
  SubsidiaryNavigator, IntercompanyTxns, Eliminations, InterEntityTransfers, ConsolidationRules,
})

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/marketplace/marketplace.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/marketplace/marketplace.tsx
 * ----------------------------------------------------------------------------
 * Cenora Marketplace — paid add-ons, integrations, industries, country packs.
 *
 *   Modules           — the 11 functional modules
 *   Add-ons & AI      — Cenora AI Pro, API, SSO, advanced reports, etc.
 *   Integrations      — connectors to outside SaaS (Slack, QuickBooks, …)
 *   Industry Packs    — 20+ vertical templates (country-neutral)
 *   Country Packs     — localization: tax, payroll, banks, statutory forms
 * ============================================================================
 */

interface MarketItem {
  id: string
  name: string
  tagline: string
  category: "module" | "addon" | "integration" | "industry" | "country"
  price: string
  priceSub?: string
  status: "subscribed" | "trial-available" | "not-installed" | "installing" | "coming-soon"
  rating?: number
  reviewCount?: number
  topReview?: { text: string; author: string; role: string }
  installs?: string
  icon: string
  iconBg: string
  iconFg: string
  highlights: string[]
  badge?: { text: string; tone: string }
  publisher?: string
  moduleHref?: string
}

const MARKET_ITEMS: MarketItem[] = [
  /* ─── MODULES ───────────────────────────────────────────────────── */
  { id: "mod-products",     name: "Products",         tagline: "Master data: products, categories, attributes, IRQs",        category: "module", price: "Included", status: "subscribed", icon: "tag",          iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["4-level category hierarchy", "Attribute templates", "IRQ approval workflow", "Item lifecycle states", "1000+ subcategory templates"], publisher: "Cenora", moduleHref: "/products/list" },
  { id: "mod-procurement",  name: "Procurement",      tagline: "Order Requests → Requisitions → PO → IR → Vendor bill",       category: "module", price: "Included", status: "subscribed", icon: "doc",          iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Multi-step approvals", "Combine requisitions", "3-way match auto-derive", "Vendor performance scoring"], publisher: "Cenora", moduleHref: "/procurement/order-requests" },
  { id: "mod-finance",      name: "Finance",          tagline: "GL, AR, AP, cash, bank recon, financial closings",            category: "module", price: "Included", status: "subscribed", icon: "trending-up",  iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["AI bank reconciliation", "Multi-currency consolidation", "Year-end + month-end runbook", "P&L variance with forecast"], publisher: "Cenora", moduleHref: "/finance" },
  { id: "mod-billing",      name: "Subscriptions & Billing", tagline: "Recurring billing, usage metering, dunning, rev-rec — Cenora runs on this", category: "module", price: "$149", priceSub: "/mo", status: "subscribed", icon: "refresh",  iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Tiered, usage & flat price books", "Proration, upgrades, pauses", "Dunning & failed-payment recovery", "ASC 606 / IFRS 15 rev-rec schedules", "Powers Cenora's own subscriptions"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/billing" },
  { id: "mod-warehouse",    name: "Warehouse",        tagline: "Multi-warehouse stock, receive, ship, transfers, RMA",        category: "module", price: "Included", status: "subscribed", icon: "box",          iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Lot + expiry tracking", "Cycle count mobile", "Reorder rules", "RMA + write-down workflow"], publisher: "Cenora", moduleHref: "/warehouse" },
  { id: "mod-hris",         name: "HRIS",             tagline: "Directory, org, recruitment, performance, payroll, cases",     category: "module", price: "Included", status: "subscribed", icon: "users",        iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["9 sub-modules", "Indeed + LinkedIn connectors", "Biometric + Teams ingestion", "AI performance flags"], publisher: "Cenora", moduleHref: "/hr/dashboard" },
  { id: "mod-crm",          name: "CRM",              tagline: "Leads, customers, activities, opportunities, quotes, orders",  category: "module", price: "Included", status: "subscribed", icon: "trending-up",  iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Weighted pipeline by stage", "Quote → SO automation", "Customer health signals", "Multi-currency deals"], publisher: "Cenora", moduleHref: "/crm/opportunities" },
  { id: "mod-mfg",          name: "Manufacturing",    tagline: "Planning, work orders, BOMs, shop floor, quality",            category: "module", price: "Included", status: "subscribed", icon: "list",         iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["BOM versioning", "Shortage AI auto-OR", "Shop-floor real-time", "QC defect tracking"], publisher: "Cenora", moduleHref: "/manufacturing" },
  { id: "mod-pm",           name: "Project Management", tagline: "Projects, tasks, milestones, schedule, resources",          category: "module", price: "Included", status: "subscribed", icon: "briefcase",    iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Gantt scheduling", "Resource utilization", "Health flags", "Change request workflow"], publisher: "Cenora", moduleHref: "/pm/projects" },
  { id: "mod-pa",           name: "Project Accounting", tagline: "Portfolio, time, WIP, project billing — fixed-bid or T&M",  category: "module", price: "Included", status: "subscribed", icon: "grid",         iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["WIP roll-forward", "Margin watchdog", "Time → invoice automation", "Revenue recognition"], publisher: "Cenora", moduleHref: "/pa/portfolio" },
  { id: "mod-logistics",    name: "Logistics / TMS",  tagline: "Transport orders, route planning, fleet, GPS tracking",       category: "module", price: "Included", status: "subscribed", icon: "truck",        iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Live GPS tracking", "Route optimization", "Fleet maintenance", "POD + freight cost"], publisher: "Cenora", moduleHref: "/logistics" },
  { id: "mod-assets",       name: "Assets",           tagline: "Fixed + Intangible branches: register, depreciation, disposals, licences, patents, trademarks, goodwill", category: "module", price: "Included", status: "subscribed", icon: "shield",       iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Fixed: 4 depreciation methods, lifecycle, NBV", "Digital: software licences, domains, certs, IP", "Amortisation schedules", "Renewal / expiry alerts", "Fixed | Digital branch toggle"], badge: { text: "Expanded in V4", tone: "accent" }, publisher: "Cenora", moduleHref: "/assets/register" },
  { id: "mod-tax",          name: "Tax & Compliance", tagline: "Multi-jurisdiction filings + audit trail",                    category: "module", price: "Included", status: "subscribed", icon: "doc",          iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Tax setup + rules engine", "Filing calendar", "Penalty calculator", "Audit trail export"], publisher: "Cenora", moduleHref: "/tax/calendar" },
  { id: "mod-field",        name: "Field Service",    tagline: "Tickets, dispatch, crew mobile, SLAs, contracts",             category: "module", price: "Included", status: "subscribed", icon: "truck",        iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Map-based dispatch", "Skill-aware assignment", "Mobile crew app", "SLA breach alerts"], publisher: "Cenora", moduleHref: "/field/dispatch" },
  { id: "mod-comms",        name: "Communications",   tagline: "Groups · channels · sub-channels · DMs · calls · meetings",   category: "module", price: "Included", status: "subscribed", icon: "message",      iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Teams-like (groups → channels → sub-channels)", "Voice + video calls + meetings", "Doc-embedded discussion (PO, IRQ, etc.)", "Dockable global slide-out panel", "AI thread summarize + @cenora bot"], badge: { text: "New in V3", tone: "warn" }, publisher: "Cenora", moduleHref: "/comms/channels" },
  { id: "mod-multi-entity", name: "Multi-Entity",     tagline: "Consolidation · subsidiary navigator · intercompany · eliminations", category: "module", price: "$199", priceSub: "/mo · per entity", status: "subscribed", icon: "globe",        iconBg: "bg-brand-soft",   iconFg: "text-brand",         highlights: ["Hierarchical consolidation (entity → region → group)", "Intercompany match + dispute", "Auto + manual eliminations", "Inter-entity cash/inventory/asset transfers", "Ownership %, FX policy, MI rules"], badge: { text: "Now a module", tone: "accent" }, publisher: "Cenora", moduleHref: "/multi-entity/consolidated" },
  { id: "mod-commerce",     name: "E-commerce Storefront", tagline: "Native commerce — storefront, online orders, promotions, B2B portal", category: "module", price: "$249", priceSub: "/mo", status: "subscribed", icon: "globe",   iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Native storefront (no Shopify needed)", "Live inventory & price from Warehouse/Products — no sync", "Orders → Sales Orders + AR automatically", "B2B account pricing & credit portal", "Discount codes, bundles, tiered promos"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/commerce" },
  { id: "mod-pos",          name: "Point of Sale",    tagline: "Touch register, shift reconciliation, loyalty — Retail + Restaurant", category: "module", price: "$59", priceSub: "/register/mo", status: "subscribed", icon: "grid", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Tablet touch register", "Retail & Restaurant/QSR modes", "Split payment, tips, discounts", "Offline mode + auto-sync", "Sells live Warehouse stock; tills reconcile to Finance"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/pos" },
  { id: "mod-bookings",     name: "Bookings & Reservations", tagline: "One engine, four types: appointment · table · room-night · resource", category: "module", price: "$99", priceSub: "/mo", status: "subscribed", icon: "calendar", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Shared booking engine (like Tickets)", "Resource calendar (staff/room/equipment)", "Public self-service page + reminders", "Deposits flow to Finance", "Deposit & no-show rules per type"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/bookings" },
  { id: "mod-quality",      name: "Quality Management (QMS)", tagline: "NCRs, CAPA, inspections, ISO 9001 audits & document control", category: "module", price: "$149", priceSub: "/mo", status: "subscribed", icon: "check-square", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["NCRs ride the Tickets engine", "8D / 5-why CAPA workflow", "AQL inspection plans", "ISO 9001 audit management", "Links into Manufacturing QC + incoming inspection"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/quality" },
  { id: "mod-ehs",          name: "Health & Safety (EHS)", tagline: "Incidents, inspections, permits-to-work, corrective actions", category: "module", price: "$149", priceSub: "/mo", status: "subscribed", icon: "shield", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Incidents ride the Tickets engine", "Mobile incident & inspection capture", "Permits-to-work + JSA + toolbox talks", "TRIR / days-since-incident tracking", "Actions link to QMS CAPA; training to HRIS"], badge: { text: "New in V4", tone: "warn" }, publisher: "Cenora", moduleHref: "/ehs" },

  /* ─── ADD-ONS ─────────────────────────────────────────────────────── */
  { id: "addon-workforce-pro", name: "Cenora Workforce Pro", tagline: "Activity monitoring · contractor management · field workforce · performance scoring — native to your ERP", category: "addon", price: "Contact sales", status: "trial-available", rating: 4.8, reviewCount: 96, installs: "1.2k tenants", icon: "users", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Desktop activity agent (branded per tenant)", "Contractor contracts + multi-currency + tax forms", "Mobile GPS clock-in + geofencing + photo proof", "Performance scoring with Article 22 firewall + right-to-challenge", "Per-entity feature toggles · approved hours flow to AP/AR/Payroll"], badge: { text: "New", tone: "info" }, topReview: { text: "Approved hours land in AP and AR the same day. The Article 22 safeguards made our works council say yes.", author: "Anil V.", role: "CFO · Diversified industrial" }, publisher: "Cenora" },
  { id: "addon-ai-pro",     name: "Cenora AI Pro",       tagline: "Unlimited AI minutes, deep-research mode, custom agents",  category: "addon", price: "$99",       priceSub: "/user/mo", status: "trial-available", rating: 4.9, reviewCount: 412, installs: "8.4k tenants", icon: "sparkle",     iconBg: "bg-accent-soft",  iconFg: "text-accent-deep",   highlights: ["Unlimited Ask AI usage", "Deep-research mode (multi-step)", "Custom agent builder", "Priority routing", "Workspace knowledge base"], badge: { text: "Most popular", tone: "warn" }, topReview: { text: "Cut our month-end close from 9 days to 4. The custom agents are insane.", author: "Maria A.", role: "CFO · Mid-market manufacturing" }, publisher: "Cenora" },
  { id: "addon-api",        name: "Open API + Webhooks", tagline: "REST API + signed webhooks for every doctype",             category: "addon", price: "$199",      priceSub: "/mo",       status: "not-installed",   rating: 4.7, reviewCount: 188, installs: "2.1k tenants", icon: "external",    iconBg: "bg-info-soft",    iconFg: "text-info",          highlights: ["Full REST surface", "Webhook events", "100k req/day quota", "OpenAPI spec", "GraphQL beta"], topReview: { text: "Built our internal dashboard on top of Cenora's API in two days.", author: "Tom K.", role: "Platform Engineer" }, publisher: "Cenora" },
  { id: "addon-sso",        name: "SSO + Advanced Auth", tagline: "SAML, OIDC, SCIM, conditional access policies",            category: "addon", price: "$9",        priceSub: "/user/mo",  status: "trial-available", rating: 4.8, reviewCount: 240, installs: "3.0k tenants", icon: "lock",        iconBg: "bg-success-soft", iconFg: "text-success",       highlights: ["SAML 2.0 + OIDC", "SCIM auto-provisioning", "Conditional access policies", "Per-IP allow list", "Audit-grade logs"], topReview: { text: "Plug-and-play with Azure AD. Our IT team was thrilled.", author: "Sofia M.", role: "IT Director" }, publisher: "Cenora" },
  { id: "addon-reports",    name: "Custom Report Builder", tagline: "Drag-and-drop reports with cross-module joins",          category: "addon", price: "$49",       priceSub: "/user/mo",  status: "not-installed",   rating: 4.6, reviewCount: 96,  installs: "1.4k tenants", icon: "pie",         iconBg: "bg-warn-soft",    iconFg: "text-warn",          highlights: ["Visual query builder", "Pivot + cross-tab", "Scheduled email/Slack delivery", "Embed in Home", "Power BI / Looker export"], topReview: { text: "Our finance team is finally self-service for ad-hoc reports.", author: "Anil V.", role: "CFO · Logistics" }, publisher: "Cenora" },
  { id: "addon-support",    name: "Premium Support 24/7", tagline: "Dedicated CSM + 1-hr response, 24/7",                     category: "addon", price: "$499",      priceSub: "/mo",       status: "not-installed",   rating: 4.9, reviewCount: 64,  installs: "800 tenants",   icon: "help",        iconBg: "bg-accent-soft",  iconFg: "text-accent-deep",   highlights: ["Named Customer Success Manager", "1-hr response SLA", "Quarterly business review", "Priority bug fixes", "Direct Slack channel"], topReview: { text: "The dedicated CSM saved us during go-live. Worth every cent.", author: "Daniel R.", role: "COO · Construction" }, publisher: "Cenora" },
  { id: "addon-archive",    name: "Cold Archive Storage",tagline: "Move closed periods to cold storage, save 80%",            category: "addon", price: "$0.002", priceSub: "/GB/mo",      status: "not-installed",   rating: 4.4, reviewCount: 32,  installs: "420 tenants",   icon: "box",         iconBg: "bg-cream",        iconFg: "text-ink-soft",      highlights: ["Auto-archive after 7y retention", "Point-in-time recovery", "Audit-ready exports", "GDPR / DPA compliant", "Glacier-class storage"], topReview: { text: "Cut our storage spend by 78% with one toggle.", author: "Priya I.", role: "VP Operations" }, publisher: "Cenora" },
  { id: "addon-workflows",  name: "Workflow Builder",   tagline: "Visual workflow designer with triggers + AI steps",          category: "addon", price: "$79", priceSub: "/user/mo",     status: "not-installed",   rating: 4.8, reviewCount: 142, installs: "1.8k tenants", icon: "git-branch", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["50+ pre-built templates", "Conditional branching", "AI-powered decision nodes", "Schedule + event triggers", "Inter-module orchestration"], topReview: { text: "Automated our 3-step PO approval into 30 seconds end-to-end.", author: "Maya C.", role: "Procurement Director" }, publisher: "Cenora" },

  /* ─── INTEGRATIONS ───────────────────────────────────────────────── */
  { id: "int-slack",        name: "Slack",               tagline: "Approvals, notifications, /cenora command in your channels", category: "integration", price: "Free", status: "not-installed", rating: 4.8, reviewCount: 320, installs: "5.2k tenants", icon: "message",  iconBg: "bg-info-soft",    iconFg: "text-info",          highlights: ["Approve POs from Slack", "Daily AI brief at 8am", "/cenora command bar", "Channel-scoped alerts"], topReview: { text: "Approving POs from Slack is a productivity hack we didn't know we needed.", author: "Kenneth T.", role: "Sales Lead" }, publisher: "Slack Inc." },
  { id: "int-quickbooks",   name: "QuickBooks Online",   tagline: "Two-way GL, AP, AR sync",                                  category: "integration", price: "Free",          status: "not-installed",   rating: 4.5, reviewCount: 240, installs: "1.8k tenants", icon: "credit-card", iconBg: "bg-success-soft", iconFg: "text-success",     highlights: ["Bi-directional GL sync", "Customer + vendor mapping", "AP/AR push-back", "Daily reconciliation"], topReview: { text: "Migrated 5 years of QuickBooks data overnight. Zero drama.", author: "Maria A.", role: "Controller" }, publisher: "Intuit" },
  { id: "int-stripe",       name: "Stripe",              tagline: "Card + ACH payments, auto-reconciled to Cenora AR",        category: "integration", price: "Stripe fees", status: "not-installed",   rating: 4.9, reviewCount: 480, installs: "4.6k tenants", icon: "credit-card", iconBg: "bg-accent-soft", iconFg: "text-accent-deep", highlights: ["Hosted invoice payment", "Auto-match to AR", "Recurring billing", "Stripe Tax handoff"], topReview: { text: "Cut DSO from 42 days to 18. Hosted invoice payment is a game-changer.", author: "Maya R.", role: "Finance Manager" }, publisher: "Stripe Inc." },
  { id: "int-xero",         name: "Xero",                tagline: "Sync with your existing Xero books",                       category: "integration", price: "Free",          status: "not-installed",   rating: 4.4, reviewCount: 140, installs: "1.1k tenants", icon: "credit-card", iconBg: "bg-info-soft",    iconFg: "text-info",        highlights: ["GL bi-directional", "Bank rule sync", "ANZ region tuned", "Multi-org support"], topReview: { text: "Best Xero integration on any ERP we evaluated.", author: "Mia H.", role: "PM · AU" }, publisher: "Xero Ltd." },
  { id: "int-hubspot",      name: "HubSpot",             tagline: "Push closed-won opps to Cenora as projects + customers",    category: "integration", price: "Free",          status: "not-installed",   rating: 4.6, reviewCount: 88,  installs: "2.4k tenants", icon: "users",       iconBg: "bg-accent-soft",  iconFg: "text-accent-deep", highlights: ["Closed-won → Customer", "Deal → Project handoff", "Contact sync", "Pipeline mirror"], topReview: { text: "Eliminated the dual-entry nightmare between sales and ops.", author: "Mason P.", role: "AE" }, publisher: "HubSpot Inc." },
  { id: "int-shopify",      name: "Shopify",             tagline: "Storefront orders → invoices + inventory deduction",       category: "integration", price: "$49", priceSub: "/store/mo", status: "not-installed", rating: 4.7, reviewCount: 196, installs: "2.0k tenants", icon: "package",  iconBg: "bg-success-soft", iconFg: "text-success",     highlights: ["Order → AR invoice", "Inventory auto-deduct", "Multi-location handoff", "Refund handling"], topReview: { text: "Inventory finally accurate across 4 Shopify stores + 2 warehouses.", author: "Eric W.", role: "E-comm Manager" }, publisher: "Shopify Inc." },
  { id: "int-docusign",     name: "DocuSign",            tagline: "Send POs, quotes, contracts for e-signature",              category: "integration", price: "DocuSign fees",     status: "not-installed",   rating: 4.5, reviewCount: 112, installs: "1.5k tenants", icon: "edit",        iconBg: "bg-warn-soft",    iconFg: "text-warn",        highlights: ["E-sign any document", "Audit trail attached", "Template library", "Bulk send"], topReview: { text: "Closing big POs with one click. Audit team loves the embedded trail.", author: "Maya C.", role: "Procurement Director" }, publisher: "DocuSign Inc." },
  { id: "int-power-bi",     name: "Power BI Connector",  tagline: "Live data feed to your Power BI workspace",                category: "integration", price: "Included with API",   status: "coming-soon",     icon: "chart",        iconBg: "bg-info-soft",    iconFg: "text-info",          highlights: ["Live DirectQuery", "100+ pre-built measures", "Row-level security aware", "Dataflow ready"], badge: { text: "Coming Q3", tone: "accent" }, publisher: "Cenora" },
  { id: "int-teams",        name: "Microsoft Teams",     tagline: "Shifts, voice approvals, mentions, files",                  category: "integration", price: "Free", status: "not-installed", rating: 4.6, reviewCount: 168, installs: "3.4k tenants", icon: "video", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["Shifts integration (attendance)", "Voice-approve via Teams call", "@cenora mentions in chat", "File attachments to records"], topReview: { text: "Our supervisors love voice-approving leave requests during their shift.", author: "Sofia M.", role: "HR BP" }, publisher: "Microsoft" },
  { id: "int-zapier",       name: "Zapier",              tagline: "5,000+ app connections via Zapier",                          category: "integration", price: "Zapier fees", status: "not-installed", rating: 4.5, reviewCount: 220, installs: "2.8k tenants", icon: "zap", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["50+ Cenora triggers", "100+ Cenora actions", "Multi-step zaps", "Native filters + paths"], topReview: { text: "Connected Cenora to Notion, Calendly, Airtable in an afternoon.", author: "Vanessa L.", role: "Ops Engineer" }, publisher: "Zapier Inc." },

  /* ─── INDUSTRY PACKS ─────────────────────────────────────────────── */
  { id: "ind-construction",  name: "Construction & Engineering", tagline: "BOQ, progress billing, retention, subcontractor mgmt", category: "industry", price: "$199", priceSub: "/mo", status: "trial-available", rating: 4.8, reviewCount: 88, installs: "240 tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["BOQ + S-curve templates", "Progress billing schedules", "Retention tracking", "Subcontractor mgmt", "Variation orders"], badge: { text: "Industry pick", tone: "accent" }, topReview: { text: "Saved 60+ hours/month on progress billings.", author: "Daniel R.", role: "PM · Construction" }, publisher: "Cenora · Industry team" },
  { id: "ind-fnb",          name: "F&B · Restaurant & QSR", tagline: "HACCP, batch tracking, recipe costing, menu engineering",  category: "industry", price: "$99", priceSub: "/location/mo", status: "not-installed", rating: 4.7, reviewCount: 56, installs: "180 tenants", icon: "package", iconBg: "bg-success-soft", iconFg: "text-success", highlights: ["Recipe BOM + costing", "HACCP checklists", "Lot/batch traceability", "Menu engineering reports"], topReview: { text: "Recipe costing accurate to the cent. Margin visibility on every dish.", author: "Chef M.", role: "F&B Director" }, publisher: "Cenora" },
  { id: "ind-retail",       name: "Multi-store Retail", tagline: "POS reconciliation, store ops, daily Z-out, loyalty",        category: "industry", price: "$49", priceSub: "/store/mo", status: "not-installed", rating: 4.6, reviewCount: 72, installs: "320 tenants", icon: "credit-card", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["Daily Z-out automation", "Multi-store transfers", "Loss reporting", "Loyalty + gift card"], topReview: { text: "Z-out reconciliation now takes 5 minutes per store, was 45.", author: "Tom N.", role: "Retail Ops" }, publisher: "Cenora" },
  { id: "ind-logistics",    name: "3PL · Bonded Logistics", tagline: "Bonded warehouse, customs duty, freight cost-roll",       category: "industry", price: "$149", priceSub: "/mo", status: "not-installed", rating: 4.5, reviewCount: 38, installs: "120 tenants", icon: "truck", iconBg: "bg-cream", iconFg: "text-ink-soft", highlights: ["Bonded WH module", "Customs landed cost", "Freight rate engine", "Carrier scorecard"], topReview: { text: "Landed cost calc finally accurate including duties + brokerage.", author: "Priya I.", role: "VP Ops" }, publisher: "Cenora" },
  { id: "ind-professional", name: "Professional Services", tagline: "Time-and-billing for consulting + legal + agencies",      category: "industry", price: "$79", priceSub: "/user/mo", status: "not-installed", rating: 4.7, reviewCount: 124, installs: "440 tenants", icon: "user", iconBg: "bg-accent-soft", iconFg: "text-accent-deep", highlights: ["Tap-to-time entry", "Multi-rate billing", "Realisation reports", "WIP write-up/down"], topReview: { text: "Our utilization improved 12pp in the first quarter.", author: "Anya R.", role: "Practice Lead" }, publisher: "Cenora" },
  { id: "ind-manufacturing",name: "Discrete Manufacturing", tagline: "Production scheduling, MES integration, OEE tracking",   category: "industry", price: "$249", priceSub: "/mo", status: "not-installed", rating: 4.8, reviewCount: 96, installs: "180 tenants", icon: "list", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["APS scheduling engine", "MES adapter library", "OEE dashboards", "Andon system", "Maintenance management"], topReview: { text: "OEE visibility transformed how we run our 3 plants.", author: "Anand K.", role: "Plant Manager" }, publisher: "Cenora" },
  { id: "ind-process",      name: "Process Manufacturing", tagline: "Formula management, batch records, by-products",         category: "industry", price: "$229", priceSub: "/mo", status: "not-installed", rating: 4.6, reviewCount: 42, installs: "84 tenants", icon: "package", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["Formula version control", "Electronic batch records", "By-product + co-product mgmt", "FDA 21 CFR Part 11"], topReview: { text: "Audit-ready batch records out of the box.", author: "Q. Director", role: "Quality" }, publisher: "Cenora" },
  { id: "ind-healthcare",   name: "Healthcare · Clinics", tagline: "Patient billing, insurance claims, inventory of consumables", category: "industry", price: "$179", priceSub: "/clinic/mo", status: "not-installed", rating: 4.7, reviewCount: 58, installs: "120 tenants", icon: "shield", iconBg: "bg-danger-soft", iconFg: "text-danger", highlights: ["Insurance claim processing", "Patient billing", "Consumables inventory", "HIPAA-aligned audit trail"], publisher: "Cenora · Healthcare team" },
  { id: "ind-education",    name: "Education · Schools & Universities", tagline: "Tuition, fees, donations, grants, alumni",   category: "industry", price: "$149", priceSub: "/mo", status: "not-installed", rating: 4.6, reviewCount: 34, installs: "78 tenants", icon: "graduation", iconBg: "bg-accent-soft", iconFg: "text-accent-deep", highlights: ["Tuition + financial aid", "Grant accounting", "Donor management", "Alumni engagement"], topReview: { text: "Unified our SIS + finance + alumni systems in one place.", author: "Bursar", role: "Private University" }, publisher: "Cenora" },
  { id: "ind-nonprofit",    name: "Nonprofit & NGO", tagline: "Fund accounting, grants, donor receipts, restricted use",       category: "industry", price: "$99", priceSub: "/mo", status: "not-installed", rating: 4.8, reviewCount: 52, installs: "140 tenants", icon: "thumbs-up", iconBg: "bg-danger-soft", iconFg: "text-danger", highlights: ["Fund accounting", "Restricted vs unrestricted", "Donor CRM + receipts", "Grant reporting", "501(c)(3) Form 990"], topReview: { text: "Audit time went from 6 weeks to 10 days.", author: "ED", role: "501(c)(3) Foundation" }, publisher: "Cenora" },
  { id: "ind-realestate",   name: "Real Estate & Property", tagline: "Lease management, rent roll, CAM reconciliation",        category: "industry", price: "$129", priceSub: "/property/mo", status: "not-installed", rating: 4.5, reviewCount: 28, installs: "62 tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["Lease abstraction", "Rent roll + escalations", "CAM reconciliation", "ASC 842 compliance"], publisher: "Cenora" },
  { id: "ind-saas",         name: "SaaS · Subscription Business", tagline: "MRR/ARR, churn, revenue recognition (ASC 606)",   category: "industry", price: "$99", priceSub: "/mo", status: "not-installed", rating: 4.9, reviewCount: 108, installs: "320 tenants", icon: "trending-up", iconBg: "bg-success-soft", iconFg: "text-success", highlights: ["MRR/ARR roll-forwards", "Churn + cohort analysis", "ASC 606 rev rec", "Usage-based billing"], badge: { text: "Hot category", tone: "warn" }, topReview: { text: "MRR + cohort retention reports out of the box. Magic.", author: "VP Finance", role: "B2B SaaS · Series B" }, publisher: "Cenora" },
  { id: "ind-agriculture",  name: "Agriculture & Agribusiness", tagline: "Crop planning, livestock, harvest, commodity",       category: "industry", price: "$119", priceSub: "/mo", status: "not-installed", rating: 4.4, reviewCount: 22, installs: "40 tenants", icon: "package", iconBg: "bg-success-soft", iconFg: "text-success", highlights: ["Crop + harvest planning", "Livestock tracking", "Commodity pricing", "Subsidy tracking"], publisher: "Cenora" },
  { id: "ind-energy",       name: "Energy & Utilities", tagline: "Meter billing, rate plans, regulatory reporting",            category: "industry", price: "$229", priceSub: "/mo", status: "not-installed", rating: 4.6, reviewCount: 18, installs: "32 tenants", icon: "zap", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["Meter-to-cash workflows", "Multi-rate plans", "Regulatory reporting", "Demand response"], publisher: "Cenora" },
  { id: "ind-hospitality",  name: "Hospitality · Hotels", tagline: "Folio management, RevPAR, channel manager integration",    category: "industry", price: "$149", priceSub: "/property/mo", status: "not-installed", rating: 4.5, reviewCount: 44, installs: "88 tenants", icon: "building", iconBg: "bg-accent-soft", iconFg: "text-accent-deep", highlights: ["Folio + reservation mgmt", "RevPAR + ADR reports", "Channel manager links", "Banquet costing"], publisher: "Cenora" },
  { id: "ind-automotive",   name: "Automotive · Dealership", tagline: "Vehicle inventory, F&I, service ROs, warranty claims",  category: "industry", price: "$199", priceSub: "/dealership/mo", status: "not-installed", rating: 4.6, reviewCount: 38, installs: "72 tenants", icon: "truck", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["VIN-level inventory", "F&I + finance penetration", "Service repair orders", "OEM warranty claims"], publisher: "Cenora" },
  { id: "ind-fintech",      name: "Fintech · Lending", tagline: "Loan origination, servicing, regulatory capital",            category: "industry", price: "$249", priceSub: "/mo", status: "not-installed", rating: 4.7, reviewCount: 24, installs: "44 tenants", icon: "wallet", iconBg: "bg-brand-soft", iconFg: "text-brand", highlights: ["Loan origination workflow", "Servicing + collections", "Regulatory capital reports", "KYC + AML hooks"], publisher: "Cenora · Fintech team" },
  { id: "ind-legal",        name: "Legal · Law Firms", tagline: "Matter management, trust accounting, time + billing",         category: "industry", price: "$129", priceSub: "/user/mo", status: "not-installed", rating: 4.7, reviewCount: 86, installs: "240 tenants", icon: "doc", iconBg: "bg-ink-soft/10", iconFg: "text-ink-soft", highlights: ["Matter-based time entry", "Trust + IOLTA accounting", "Conflict checks", "LEDES e-billing"], publisher: "Cenora" },
  { id: "ind-media",        name: "Media & Entertainment", tagline: "Rights management, royalties, production accounting",     category: "industry", price: "$179", priceSub: "/mo", status: "not-installed", rating: 4.4, reviewCount: 26, installs: "38 tenants", icon: "video", iconBg: "bg-accent-soft", iconFg: "text-accent-deep", highlights: ["Rights + IP tracking", "Royalty calculations", "Production cost accounting", "Talent contracts"], publisher: "Cenora" },
  { id: "ind-ecommerce",    name: "E-commerce · DTC Brand", tagline: "Multi-channel inventory, ad-spend attribution, returns",  category: "industry", price: "$99", priceSub: "/mo", status: "not-installed", rating: 4.8, reviewCount: 156, installs: "480 tenants", icon: "package", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["Multi-channel inventory", "Ad-spend attribution", "Returns + RMA workflow", "Contribution margin"], topReview: { text: "Finally know which channel actually makes money.", author: "DTC Brand CEO", role: "Apparel" }, publisher: "Cenora" },
  { id: "ind-mining",       name: "Mining & Resources", tagline: "Pit-to-port costing, royalties, regulatory reporting",       category: "industry", price: "$299", priceSub: "/mo", status: "not-installed", rating: 4.5, reviewCount: 14, installs: "22 tenants", icon: "layers", iconBg: "bg-ink-soft/10", iconFg: "text-ink-soft", highlights: ["Pit-to-port cost rollup", "Royalty calculations", "Environmental compliance", "Stockpile tracking"], publisher: "Cenora · Resources" },

  /* ─── COUNTRY LOCALIZATION PACKS ─────────────────────────────────── */
  { id: "ctry-ph",          name: "Philippines Localization", tagline: "BIR forms, PCAB compliance, VAT/EWT, SSS/PhilHealth/HDMF", category: "country", price: "$89", priceSub: "/entity/mo", status: "subscribed", rating: 4.9, reviewCount: 124, installs: "480 PH tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["BIR 1601-EQ, 2306, 2307, 0619-E", "12% VAT + EWT auto-calc on POs", "SSS, PhilHealth, HDMF payroll", "PCAB construction license tracking", "Withholding tax certificates"], badge: { text: "Subscribed", tone: "success" }, publisher: "Cenora · PH team" },
  { id: "ctry-sg",          name: "Singapore Localization", tagline: "GST F5, CPF, IRAS, ACRA reporting",                       category: "country", price: "$89", priceSub: "/entity/mo", status: "subscribed", rating: 4.8, reviewCount: 88, installs: "320 SG tenants", icon: "building", iconBg: "bg-danger-soft", iconFg: "text-danger", highlights: ["GST F5 quarterly returns", "CPF contribution auto-calc", "IRAS year-end forms (IR8A, IR21)", "ACRA filings", "Skills Development Levy"], badge: { text: "Subscribed", tone: "success" }, publisher: "Cenora · SG team" },
  { id: "ctry-hk",          name: "Hong Kong Localization", tagline: "MPF, IRD profits tax, BR renewal",                        category: "country", price: "$89", priceSub: "/entity/mo", status: "subscribed", rating: 4.7, reviewCount: 52, installs: "180 HK tenants", icon: "building", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["MPF contribution mgmt", "IRD profits tax computation", "BR license renewal calendar", "BIR56A/IR56B forms", "Stamp duty calc"], badge: { text: "Subscribed", tone: "success" }, publisher: "Cenora · HK team" },
  { id: "ctry-au",          name: "Australia Localization", tagline: "GST BAS, ATO, SuperStream, single-touch payroll (STP)",   category: "country", price: "$89", priceSub: "/entity/mo", status: "subscribed", rating: 4.8, reviewCount: 96, installs: "240 AU tenants", icon: "building", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["BAS quarterly / monthly", "Single-Touch Payroll (STP)", "SuperStream contributions", "PAYG withholding", "Fringe Benefits Tax"], badge: { text: "Subscribed", tone: "success" }, publisher: "Cenora · AU team" },
  { id: "ctry-in",          name: "India Localization", tagline: "GST, TDS, TCS, EPF/ESI, statutory bonus",                     category: "country", price: "$89", priceSub: "/entity/mo", status: "subscribed", rating: 4.7, reviewCount: 142, installs: "620 IN tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["GST GSTR-1/3B/9 filings", "TDS + TCS auto-deduct", "EPF + ESI payroll", "PT slabs by state", "Statutory bonus calc"], badge: { text: "Subscribed", tone: "success" }, publisher: "Cenora · India team" },
  { id: "ctry-us",          name: "United States Localization", tagline: "Federal + state tax, 1099, W-2, sales tax nexus",     category: "country", price: "$129", priceSub: "/entity/mo", status: "not-installed", rating: 4.7, reviewCount: 88, installs: "320 US tenants", icon: "building", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["Federal + state income tax", "1099-NEC / 1099-MISC", "W-2 generation", "Sales tax nexus tracking", "Avalara integration"], topReview: { text: "Sales tax nexus tracking saved us a $40k state audit.", author: "Tax Manager", role: "Multi-state Retailer" }, publisher: "Cenora · US team" },
  { id: "ctry-uk",          name: "United Kingdom Localization", tagline: "VAT MTD, RTI payroll, P45/P60, CIS construction",    category: "country", price: "$99", priceSub: "/entity/mo", status: "not-installed", rating: 4.6, reviewCount: 56, installs: "180 UK tenants", icon: "building", iconBg: "bg-danger-soft", iconFg: "text-danger", highlights: ["VAT MTD-compliant filings", "RTI payroll submissions", "P45 / P60 generation", "CIS for construction", "Pension auto-enrolment"], publisher: "Cenora · UK team" },
  { id: "ctry-eu",          name: "European Union (Multi-country)", tagline: "EU VAT, OSS, intra-community supplies, Intrastat", category: "country", price: "$149", priceSub: "/entity/mo", status: "not-installed", rating: 4.5, reviewCount: 38, installs: "120 EU tenants", icon: "globe", iconBg: "bg-info-soft", iconFg: "text-info", highlights: ["EU VAT registrations", "OSS quarterly returns", "Intra-community supplies", "Intrastat declarations", "Per-country invoice rules"], publisher: "Cenora · EU team" },
  { id: "ctry-mx",          name: "Mexico Localization", tagline: "CFDI e-invoicing, DIOT, payroll IMSS",                       category: "country", price: "$109", priceSub: "/entity/mo", status: "not-installed", rating: 4.5, reviewCount: 32, installs: "84 MX tenants", icon: "building", iconBg: "bg-success-soft", iconFg: "text-success", highlights: ["CFDI 4.0 e-invoicing", "DIOT monthly report", "IMSS payroll contributions", "ISR withholding", "Complemento de pagos"], publisher: "Cenora · MX team" },
  { id: "ctry-br",          name: "Brazil Localization", tagline: "NF-e, SPED, ICMS/IPI/PIS/COFINS, eSocial",                  category: "country", price: "$129", priceSub: "/entity/mo", status: "not-installed", rating: 4.4, reviewCount: 26, installs: "62 BR tenants", icon: "building", iconBg: "bg-success-soft", iconFg: "text-success", highlights: ["NF-e e-invoicing", "SPED Contábil/Fiscal", "ICMS/IPI/PIS/COFINS", "eSocial submissions"], publisher: "Cenora · BR team" },
  { id: "ctry-ae",          name: "UAE Localization", tagline: "VAT, FTA filings, WPS payroll, Emiratization",                category: "country", price: "$99", priceSub: "/entity/mo", status: "not-installed", rating: 4.6, reviewCount: 28, installs: "88 UAE tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["5% VAT FTA filings", "WPS-compliant payroll", "Emiratization reporting", "Free zone rules"], publisher: "Cenora · UAE team" },
  { id: "ctry-id",          name: "Indonesia Localization", tagline: "PPN, PPh withholdings, BPJS, e-Faktur",                   category: "country", price: "$79", priceSub: "/entity/mo", status: "not-installed", rating: 4.5, reviewCount: 44, installs: "120 ID tenants", icon: "building", iconBg: "bg-danger-soft", iconFg: "text-danger", highlights: ["e-Faktur PPN", "PPh 21/22/23 withholdings", "BPJS Kesehatan + Ketenagakerjaan", "Tax holiday tracking"], publisher: "Cenora · ID team" },
  { id: "ctry-vn",          name: "Vietnam Localization", tagline: "VAT, PIT, e-invoicing, social insurance",                  category: "country", price: "$79", priceSub: "/entity/mo", status: "not-installed", rating: 4.4, reviewCount: 38, installs: "96 VN tenants", icon: "building", iconBg: "bg-warn-soft", iconFg: "text-warn", highlights: ["VAT 10% + e-invoicing", "Personal income tax", "Social/health/unemployment", "Foreign contractor tax"], publisher: "Cenora · VN team" },
]

const MARKET_LABEL: Record<MarketItem["category"], string> = {
  module: "Modules", addon: "Add-ons & AI", integration: "Integrations", industry: "Industry Packs", country: "Country Packs",
}

function Marketplace() {
  const { push } = useRouter()
  const [tab, setTab] = React.useState("addon")
  const [view, setView] = React.useState("cards")
  const [search, setSearch] = React.useState("")
  /* In-state install statuses so the buttons can toggle */
  const [statuses, setStatuses] = React.useState({})

  const items = MARKET_ITEMS.filter(i => i.category === tab && (
    !search.trim() || i.name.toLowerCase().includes(search.toLowerCase()) || i.tagline.toLowerCase().includes(search.toLowerCase())
  ))
  const counts: Record<string, number> = {
    module: MARKET_ITEMS.filter(i => i.category === "module").length,
    addon: MARKET_ITEMS.filter(i => i.category === "addon").length,
    integration: MARKET_ITEMS.filter(i => i.category === "integration").length,
    industry: MARKET_ITEMS.filter(i => i.category === "industry").length,
    country: MARKET_ITEMS.filter(i => i.category === "country").length,
  }

  function statusOf(item: MarketItem): MarketItem["status"] { return (statuses[item.id] as any) || item.status }
  function setStatus(id: string, s: string) { setStatuses(prev => ({ ...prev, [id]: s })) }
  function startInstall(item: MarketItem) {
    setStatus(item.id, "installing")
    setTimeout(() => setStatus(item.id, "subscribed"), 2500)
  }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Workspace" }, { label: "Marketplace" }]} />
      <ActionBar
        title="Marketplace"
        status={`${MARKET_ITEMS.length} items · ${MARKET_ITEMS.filter(i => i.status === "subscribed").length} subscribed · 30-day free trial on most add-ons`}
        view={<ListCardToggle value={view} onChange={setView} />}
        primary={<Button variant="primary" leadingIcon="settings" size="sm">Manage subscriptions</Button>}
        secondary={<Button variant="ghost" leadingIcon="external" size="sm">Publish on Marketplace</Button>}
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 space-y-5">
          {/* Hero */}
          <div className="bg-gradient-to-br from-brand to-brand-light text-white rounded-lg p-8 relative overflow-hidden">
            <div className="relative z-10 max-w-3xl">
              <div className="flex items-center gap-2 mb-2">
                <Icon name="sparkle" size={14} className="text-accent" />
                <span className="text-[10px] uppercase tracking-[0.2em] font-bold text-accent">Cenora Marketplace</span>
              </div>
              <h1 className="font-serif text-[36px] leading-[1.1] text-white">Extend Cenora to fit your business.</h1>
              <p className="text-[14px] text-white/75 mt-3 max-w-xl leading-relaxed">
                One platform, infinite leverage. 13 modules, AI Pro, 20+ industry packs, country localizations,
                and connectors to the tools your team already uses.
              </p>
              <div className="flex items-center gap-2 mt-5 flex-wrap">
                <Pill>{MARKET_ITEMS.length} items available</Pill>
                <Pill>{MARKET_ITEMS.filter(i => i.status === "subscribed").length} subscribed</Pill>
                <Pill tone="accent"><Icon name="sparkle" size={11} /> 30-day free trial on most add-ons</Pill>
              </div>
            </div>
            <div className="absolute right-8 top-6 grid grid-cols-3 gap-2 opacity-30 pointer-events-none">
              {[0, 1, 2, 3, 4, 5].map(i => (
                <div key={i} className="size-12 rounded-md border border-accent/30 bg-white/5 flex items-center justify-center">
                  <Icon name={["sparkle", "message", "credit-card", "building", "shield", "external"][i]} size={20} className="text-accent" />
                </div>
              ))}
            </div>
          </div>

          {/* Tabs + search */}
          <div className="flex items-center justify-between gap-3 flex-wrap">
            <div className="inline-flex bg-cream p-0.5 rounded-md border border-divider">
              {(["module", "addon", "integration", "industry", "country"]).map(t => (
                <button key={t} onClick={() => setTab(t)}
                  className={cn(
                    "px-3.5 h-8 rounded text-[12px] font-semibold transition-colors inline-flex items-center gap-1.5",
                    tab === t ? "bg-surface text-ink shadow-sm" : "text-ink-mute hover:text-ink"
                  )}>
                  {MARKET_LABEL[t]}
                  <span className="text-[10px] text-ink-mute font-mono">{counts[t]}</span>
                </button>
              ))}
            </div>
            <SearchBox value={search} onChange={setSearch} placeholder="Search marketplace…" className="w-[280px]" />
          </div>

          {/* Items */}
          {view === "cards" ? (
            <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4">
              {items.map(item => <MarketCard key={item.id} item={item} status={statusOf(item)} onInstall={() => startInstall(item)} onOpen={() => item.moduleHref && push(item.moduleHref)} />)}
              {items.length === 0 && <EmptyState />}
            </div>
          ) : (
            <MarketList items={items} statuses={statuses} startInstall={startInstall} statusOf={statusOf} onOpen={(it) => it.moduleHref && push(it.moduleHref)} />
          )}
        </div>
      </div>
    </>
  )
}

function Pill({ children, tone }: { children: React.ReactNode; tone?: "accent" }) {
  return (
    <div className={cn("rounded-md px-3 py-1.5 text-[11px] flex items-center gap-2",
      tone === "accent" ? "bg-accent/15 border border-accent/30 text-accent" : "bg-white/12 text-white"
    )}>{children}</div>
  )
}

function EmptyState() {
  return <div className="col-span-3 bg-surface border border-divider rounded-lg p-12 text-center text-ink-mute text-[12px]">Nothing matches your search.</div>
}

/* ─── Market card ──────────────────────────────────────────────────── */
function MarketCard({ item, status, onInstall, onOpen }: { item: MarketItem; status: MarketItem["status"]; onInstall: () => void; onOpen: () => void }) {
  return (
    <div className="bg-surface border border-divider rounded-lg p-5 flex flex-col hover:shadow-md transition-shadow">
      <div className="flex items-start gap-3">
        <div className={cn("size-12 rounded-md flex items-center justify-center shrink-0", item.iconBg, item.iconFg)}>
          <Icon name={item.icon} size={20} />
        </div>
        <div className="flex-1 min-w-0">
          <div className="flex items-start justify-between gap-2">
            <h3 className="font-serif text-[17px] text-ink leading-tight">{item.name}</h3>
            {item.badge && <Badge variant={item.badge.tone}>{item.badge.text}</Badge>}
          </div>
          {item.publisher && <div className="text-[10.5px] text-ink-mute mt-0.5">by {item.publisher}</div>}
        </div>
      </div>

      <p className="text-[12.5px] text-ink-soft mt-3 leading-relaxed line-clamp-2">{item.tagline}</p>

      <ul className="mt-3 space-y-1.5 flex-1">
        {item.highlights.slice(0, 5).map((h, i) => (
          <li key={i} className="flex items-start gap-1.5 text-[11.5px] text-ink-soft">
            <Icon name="check" size={11} className="text-success mt-0.5 shrink-0" />
            <span>{h}</span>
          </li>
        ))}
      </ul>

      {item.topReview && (
        <div className="mt-3 pt-3 border-t border-divider-soft bg-cream/30 -mx-5 px-5 py-2.5">
          <div className="flex items-center gap-1.5 mb-1">
            {Array.from({ length: 5 }).map((_, i) => <Icon key={i} name="star" size={10} className="text-accent" />)}
            <span className="text-[10px] text-ink-mute font-mono">{item.rating?.toFixed(1)} · {item.reviewCount} reviews</span>
          </div>
          <div className="text-[11px] text-ink-soft italic leading-snug">"{item.topReview.text}"</div>
          <div className="text-[10px] text-ink-mute mt-1">— {item.topReview.author}, {item.topReview.role}</div>
        </div>
      )}
      {!item.topReview && item.rating && (
        <div className="flex items-center gap-2 mt-3 pt-3 border-t border-divider-soft text-[11px] text-ink-mute">
          <span className="inline-flex items-center gap-0.5"><Icon name="star" size={11} className="text-accent" /><strong className="text-ink">{item.rating.toFixed(1)}</strong></span>
          <span>·</span><span>{item.reviewCount} reviews</span>
          {item.installs && (<><span>·</span><span>{item.installs}</span></>)}
        </div>
      )}

      <div className="flex items-end justify-between mt-3 pt-3 border-t border-divider-soft gap-2">
        <div>
          {status === "subscribed" ? (
            <Badge variant="success" dot>Subscribed</Badge>
          ) : (
            <>
              <div className="font-serif text-[20px] text-ink leading-none">{item.price}</div>
              {item.priceSub && <div className="text-[10px] text-ink-mute mt-0.5">{item.priceSub}</div>}
            </>
          )}
        </div>
        <CTAButton item={item} status={status} onInstall={onInstall} onOpen={onOpen} />
      </div>
    </div>
  )
}

function CTAButton({ item, status, onInstall, onOpen }: { item: MarketItem; status: MarketItem["status"]; onInstall: () => void; onOpen: () => void }) {
  if (status === "installing") {
    return (
      <Button variant="ghost" size="sm" disabled>
        <span className="inline-flex items-center gap-1.5">
          <Icon name="refresh" size={11} className="animate-spin" /> Installing…
        </span>
      </Button>
    )
  }
  if (status === "subscribed") {
    if (item.moduleHref) {
      return <Button variant="primary" size="sm" trailingIcon="arrow-right" onClick={onOpen}>Open</Button>
    }
    return <Button variant="ghost" size="sm">Manage</Button>
  }
  if (status === "trial-available") {
    return <Button variant="primary" size="sm" leadingIcon="play" onClick={onInstall}>Try free 30 days</Button>
  }
  if (status === "coming-soon") {
    return <Button variant="ghost" size="sm" leadingIcon="bell">Notify me</Button>
  }
  return <Button variant="primary" size="sm" leadingIcon="download" onClick={onInstall}>Install</Button>
}

function MarketList({ items, statusOf, startInstall, onOpen }: { items: MarketItem[]; statuses: any; statusOf: (i: MarketItem) => MarketItem["status"]; startInstall: (i: MarketItem) => void; onOpen: (i: MarketItem) => void }) {
  return (
    <div className="bg-surface border border-divider rounded-lg overflow-hidden">
      <div className="grid grid-cols-[40px_1.6fr_2fr_120px_120px_160px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
        <span></span><span>Name</span><span>Description</span><span>Price</span><span>Rating</span><span></span>
      </div>
      {items.map(item => {
        const s = statusOf(item)
        return (
          <div key={item.id} className="grid grid-cols-[40px_1.6fr_2fr_120px_120px_160px] px-4 py-3 items-center border-t border-divider-soft hover:bg-cream/40">
            <div className={cn("size-8 rounded inline-flex items-center justify-center", item.iconBg, item.iconFg)}>
              <Icon name={item.icon} size={14} />
            </div>
            <div>
              <div className="text-[12px] text-ink font-semibold">{item.name}</div>
              <div className="text-[10px] text-ink-mute">{item.publisher}</div>
            </div>
            <div className="text-[11.5px] text-ink-soft truncate">{item.tagline}</div>
            <div className="text-[11.5px] font-mono">{s === "subscribed" ? <Badge variant="success" dot>Subscribed</Badge> : <><span className="font-bold text-ink">{item.price}</span><span className="text-ink-mute">{item.priceSub ?? ""}</span></>}</div>
            <div className="text-[11px] text-ink-mute flex items-center gap-1">
              {item.rating ? <><Icon name="star" size={11} className="text-accent" /><span className="font-bold text-ink">{item.rating.toFixed(1)}</span> <span>· {item.reviewCount}</span></> : <span className="italic">—</span>}
            </div>
            <div><CTAButton item={item} status={s} onInstall={() => startInstall(item)} onOpen={() => onOpen(item)} /></div>
          </div>
        )
      })}
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/pipeline.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/sales/pipeline.tsx
 * ----------------------------------------------------------------------------
 * Sales · CRM hero screen — Opportunities pipeline kanban.
 *
 * Layout:
 *   • Hero stat strip: weighted pipeline / closing this month / win rate
 *   • Kanban: 5 stage columns + closed (won/lost) collapsed
 *   • Each card: customer, value, probability bar, owner, days-in-stage,
 *     next-step
 *   • Click card → slideout with full opp record
 * ============================================================================
 */

function SalesPipeline() {
  const { push } = useRouter()
  const [ownerFilter, setOwnerFilter] = React.useState<"all" | "mine">("all")
  const [tierFilter, setTierFilter] = React.useState<"all" | "Enterprise" | "Mid-market" | "SMB">("all")
  const [selected, setSelected] = React.useState<Opportunity | null>(null)

  const filtered = React.useMemo(() => {
    let r = OPPORTUNITIES
    if (ownerFilter === "mine") r = r.filter(o => o.owner.initials === "KL")
    if (tierFilter !== "all")   r = r.filter(o => o.customer.tier === tierFilter)
    return r
  }, [ownerFilter, tierFilter])

  /* Aggregate metrics — all in USD-equivalent for top strip */
  const fxToUSD = (v: number, c: Opportunity["currency"]) => c === "PHP" ? v / 56.95 : c === "SGD" ? v / 1.35 : c === "HKD" ? v / 7.81 : v
  const openOpps = filtered.filter(o => o.stage !== "won" && o.stage !== "lost")
  const weighted = openOpps.reduce((s, o) => s + fxToUSD(o.value, o.currency) * (o.probability / 100), 0)
  const closingThisMonth = openOpps.filter(o => o.closeDays <= 30 && o.closeDays >= 0)
  const closingValue = closingThisMonth.reduce((s, o) => s + fxToUSD(o.value, o.currency), 0)
  const won = OPPORTUNITIES.filter(o => o.stage === "won").length
  const lost = OPPORTUNITIES.filter(o => o.stage === "lost").length
  const winRate = won + lost > 0 ? Math.round((won / (won + lost)) * 100) : 0

  const openStages = OPP_STAGES.filter(s => s.key !== "won" && s.key !== "lost")

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "CRM" }, { label: "Opportunities" }]}
      />

      <ActionBar
        title="Opportunities"
        status={`${OPPORTUNITIES.length} opportunities · ${openOpps.length} open · ${closingThisMonth.length} closing in 30 days`}
        secondary={
          <>
            <Button variant="ghost" leadingIcon="calendar">Apr 2026</Button>
            <Button variant="ghost" leadingIcon="download">Export</Button>
          </>
        }
        primary={<Button variant="primary" leadingIcon="plus">New Opportunity</Button>}
        filters={
          <>
            <FilterChip active={ownerFilter === "all"} onClick={() => setOwnerFilter("all")}>All Owners</FilterChip>
            <FilterChip active={ownerFilter === "mine"} onClick={() => setOwnerFilter("mine")} icon="user">Mine (Maya)</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            {(["all", "Enterprise", "Mid-market", "SMB"] as const).map(t => (
              <FilterChip key={t} active={tierFilter === t} onClick={() => setTierFilter(t)} tone={t === "Enterprise" ? "info" : t === "Mid-market" ? "accent" : "neutral"}>
                {t === "all" ? "All Tiers" : t}
              </FilterChip>
            ))}
          </>
        }
      />

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

          {/* Hero strip */}
          <div className="grid grid-cols-[2fr_1fr_1fr_1fr] gap-4">
            <div className="bg-gradient-to-br from-brand to-brand-light rounded-lg p-5 text-white relative overflow-hidden">
              <div className="text-[10px] font-bold uppercase tracking-wider text-accent">Weighted Pipeline · April 2026</div>
              <div className="font-serif text-[40px] leading-none mt-1.5">${(weighted / 1000).toFixed(0)}k</div>
              <div className="text-[12px] text-white/70 mt-1.5">
                {openOpps.length} open opportunities · {(openOpps.reduce((s, o) => s + fxToUSD(o.value, o.currency), 0) / 1000).toFixed(0)}k unweighted
              </div>
              <div className="absolute right-0 bottom-0 opacity-15">
                <svg width="120" height="80" viewBox="0 0 120 80" fill="none"><path d="M0,80 L20,60 L40,65 L60,40 L80,45 L100,20 L120,25" stroke="#D4A96A" strokeWidth="2.5" /></svg>
              </div>
            </div>
            <KpiTile label="Closing 30d" value={`$${(closingValue / 1000).toFixed(0)}k`} sub={`${closingThisMonth.length} deals`} accent="brand" />
            <KpiTile label="Win Rate · YTD" value={`${winRate}%`} sub={`${won} W · ${lost} L`} accent="success" deltaDir="up" delta="+4pp" />
            <KpiTile label="Avg. Deal" value={`$${Math.round((openOpps.reduce((s, o) => s + fxToUSD(o.value, o.currency), 0) / Math.max(openOpps.length, 1)) / 1000)}k`} sub="USD equiv." accent="accent" />
          </div>

          {/* Kanban — horizontal scroll on overflow */}
          <div className="grid grid-cols-5 gap-3 min-w-[1100px]">
            {openStages.map(stage => {
              const cards = filtered.filter(o => o.stage === stage.key)
              const colTotal = cards.reduce((s, o) => s + fxToUSD(o.value, o.currency), 0)
              return (
                <div key={stage.key} className="bg-cream/50 border border-divider rounded-lg overflow-hidden flex flex-col">
                  <header className="px-3 py-2.5 border-b border-divider bg-surface flex items-center justify-between">
                    <div className="flex items-center gap-2">
                      <Badge variant={stage.color as any} dot>{stage.label}</Badge>
                      <span className="text-[10.5px] text-ink-mute font-mono">{cards.length}</span>
                    </div>
                    <span className="text-[10.5px] font-mono text-ink-soft">${(colTotal / 1000).toFixed(0)}k</span>
                  </header>
                  <div className="p-2 space-y-2 flex-1 min-h-[200px]">
                    {cards.map(o => <OppCard key={o.id} opp={o} onClick={() => setSelected(o)} />)}
                    {cards.length === 0 && (
                      <div className="text-center text-[10.5px] text-ink-faint italic py-8">No deals</div>
                    )}
                  </div>
                </div>
              )
            })}
          </div>

          {/* Closed summary */}
          <div className="grid grid-cols-2 gap-3">
            <ClosedStrip stage="won" />
            <ClosedStrip stage="lost" />
          </div>

        </div>
      </div>

      {selected && <OppSheet opp={selected} onClose={() => setSelected(null)} push={push} />}
    </>
  )
}

/* ─── Opp card ───────────────────────────────────────────────────────── */
function OppCard({ opp, onClick }: { opp: Opportunity; onClick: () => void }) {
  const symbol = opp.currency === "PHP" ? "₱" : opp.currency === "SGD" ? "S$" : opp.currency === "HKD" ? "HK$" : "$"
  const healthDot = opp.health === "on-track" ? "bg-success" : opp.health === "at-risk" ? "bg-warn" : "bg-danger"
  return (
    <button onClick={onClick}
      className="w-full bg-surface border border-divider rounded-md p-2.5 text-left hover:border-divider-strong hover:shadow-sm transition-all space-y-2 cursor-pointer">
      <div className="flex items-start gap-2">
        <Avatar initials={opp.customer.initials} tone={opp.customer.tone as any} size="sm" />
        <div className="flex-1 min-w-0">
          <div className="text-[12px] font-semibold text-ink leading-tight line-clamp-2">{opp.title}</div>
          <div className="text-[10px] text-ink-mute mt-0.5">{opp.customer.name}</div>
        </div>
        {opp.starred && <Icon name="star" size={11} className="text-accent fill-accent shrink-0" />}
      </div>
      <div className="flex items-baseline justify-between">
        <div className="font-mono font-bold text-ink text-[13.5px]">{symbol}{opp.value >= 1000 ? `${(opp.value / 1000).toFixed(0)}k` : opp.value}</div>
        <span className="text-[10px] text-ink-mute font-mono">{opp.probability}%</span>
      </div>
      {/* probability bar */}
      <div className="h-1 bg-cream-deep rounded-full overflow-hidden">
        <div className={cn("h-full rounded-full", opp.probability >= 70 ? "bg-success" : opp.probability >= 40 ? "bg-accent" : "bg-info")} style={{ width: `${opp.probability}%` }} />
      </div>
      <div className="flex items-center justify-between text-[10px] text-ink-mute">
        <span className="inline-flex items-center gap-1">
          <span className={cn("size-1.5 rounded-full", healthDot)} />
          {opp.closeDays > 0 ? `${opp.closeDays}d` : "today"}
        </span>
        <Avatar initials={opp.owner.initials} tone={opp.owner.tone as any} size="xs" />
      </div>
    </button>
  )
}

/* ─── Closed strip ───────────────────────────────────────────────────────── */
function ClosedStrip({ stage }: { stage: "won" | "lost" }) {
  const items = OPPORTUNITIES.filter(o => o.stage === stage)
  const label = stage === "won" ? "Closed Won — recent" : "Closed Lost — postmortem"
  const tone = stage === "won" ? "success" : "danger"
  const total = items.reduce((s, o) => s + (o.currency === "USD" ? o.value : o.currency === "PHP" ? o.value / 56.95 : o.currency === "SGD" ? o.value / 1.35 : o.value / 7.81), 0)
  return (
    <div className="bg-surface border border-divider rounded-lg overflow-hidden">
      <header className="px-3 py-2.5 border-b border-divider bg-cream/50 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <Badge variant={tone as any} dot>{label}</Badge>
        </div>
        <span className="text-[10.5px] font-mono text-ink-soft">${(total / 1000).toFixed(0)}k · {items.length} {items.length === 1 ? "deal" : "deals"}</span>
      </header>
      <div className="divide-y divide-divider-soft">
        {items.map(o => {
          const symbol = o.currency === "PHP" ? "₱" : o.currency === "SGD" ? "S$" : o.currency === "HKD" ? "HK$" : "$"
          return (
            <div key={o.id} className="px-3 py-2 flex items-center gap-3">
              <Avatar initials={o.customer.initials} tone={o.customer.tone as any} size="sm" />
              <div className="flex-1 min-w-0">
                <div className="text-[12px] font-semibold text-ink leading-tight truncate">{o.title}</div>
                <div className="text-[10px] text-ink-mute">{o.customer.name} · closed {Math.abs(o.closeDays)}d ago</div>
              </div>
              <div className="font-mono text-[12px] text-ink-soft shrink-0">{symbol}{(o.value / 1000).toFixed(0)}k</div>
            </div>
          )
        })}
      </div>
    </div>
  )
}

/* ─── Detail slideout ────────────────────────────────────────────────────── */
function OppSheet({ opp, onClose, push }: { opp: Opportunity; onClose: () => void; push: (r: string) => void }) {
  const symbol = opp.currency === "PHP" ? "₱" : opp.currency === "SGD" ? "S$" : opp.currency === "HKD" ? "HK$" : "$"
  const stageMeta = OPP_STAGES.find(s => s.key === opp.stage)!
  return (
    <Sheet open={true} onOpenChange={() => onClose()} side="right" className="w-[560px]">
      <SheetHeader>
        <div className="flex items-start justify-between gap-3">
          <div>
            <div className="font-mono text-[10px] text-ink-mute">{opp.id}</div>
            <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{opp.title}</h2>
            <div className="text-[11px] text-ink-soft mt-1.5">{opp.customer.name} · {opp.customer.tier} · {opp.industry}</div>
          </div>
          <Badge variant={stageMeta.color as any} dot>{stageMeta.label}</Badge>
        </div>
      </SheetHeader>

      <div className="flex-1 overflow-y-auto p-5 space-y-5">
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Deal Value</div>
              <div className="font-serif text-xl text-ink mt-0.5">{symbol}{opp.value.toLocaleString()}</div>
              <div className="text-[10px] text-ink-mute">{opp.currency}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Probability</div>
              <div className="font-serif text-xl text-ink mt-0.5">{opp.probability}%</div>
              <div className="h-1 bg-cream-deep rounded-full overflow-hidden mt-1">
                <div className={cn("h-full rounded-full", opp.probability >= 70 ? "bg-success" : opp.probability >= 40 ? "bg-accent" : "bg-info")} style={{ width: `${opp.probability}%` }} />
              </div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Close Date</div>
              <div className="font-serif text-xl text-ink mt-0.5">{opp.closeDate.split(",")[0]}</div>
              <div className="text-[10px] text-ink-mute">{opp.closeDays > 0 ? `${opp.closeDays}d away` : `${Math.abs(opp.closeDays)}d ago`}</div>
            </div>
          </div>
        </section>

        {opp.nextStep && (
          <section>
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Next Step</div>
            <div className="bg-accent-soft/40 border border-accent/30 rounded-md p-3 flex items-center gap-2">
              <Icon name="arrow-right" size={14} className="text-accent-deep" />
              <span className="text-[12.5px] text-ink-soft">{opp.nextStep}</span>
            </div>
          </section>
        )}

        <section>
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Owner & Account</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft">
            <div className="px-3 py-2.5 flex items-center justify-between">
              <div className="flex items-center gap-2.5">
                <Avatar initials={opp.owner.initials} tone={opp.owner.tone as any} size="sm" />
                <div>
                  <div className="text-[12px] font-semibold text-ink">{opp.owner.name}</div>
                  <div className="text-[10px] text-ink-mute">Account Executive</div>
                </div>
              </div>
              <Button variant="ghost" size="sm" trailingIcon="arrow-right">Reassign</Button>
            </div>
            <div className="px-3 py-2.5 flex items-center justify-between">
              <div className="flex items-center gap-2.5">
                <Avatar initials={opp.customer.initials} tone={opp.customer.tone as any} size="sm" />
                <div>
                  <div className="text-[12px] font-semibold text-ink">{opp.customer.name}</div>
                  <div className="text-[10px] text-ink-mute">{opp.customer.tier} · {opp.industry}</div>
                </div>
              </div>
              <Button variant="ghost" size="sm" trailingIcon="arrow-right" onClick={() => push("/sales/customers")}>View</Button>
            </div>
          </div>
        </section>

        <section>
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Activity Snapshot</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
            <ActivityLine when="Apr 15" who="Maya C." what="Demo meeting · 45min" detail="Showed Bank Recon AI auto-match" />
            <ActivityLine when="Apr 10" who="Rico T." what="Email exchange (3)" detail="Pricing structure confirmed" />
            <ActivityLine when="Apr 04" who="Maya C." what="Discovery call" detail="3 stakeholders identified" />
          </div>
        </section>
      </div>

      <footer className="border-t border-divider px-5 py-3 flex items-center justify-between gap-2 shrink-0">
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <div className="flex items-center gap-2">
          <Button variant="ghost" leadingIcon="message">Log activity</Button>
          <Button variant="primary" leadingIcon="arrow-right">Advance stage</Button>
        </div>
      </footer>
    </Sheet>
  )
}

function ActivityLine({ when, who, what, detail }: { when: string; who: string; what: string; detail: string }) {
  return (
    <div className="px-3 py-2 flex items-baseline gap-2">
      <span className="font-mono text-[10px] text-ink-faint w-12 shrink-0">{when}</span>
      <span className="text-ink"><strong className="font-semibold">{what}</strong> <span className="text-ink-mute">· {detail}</span></span>
      <span className="ml-auto text-[10px] text-ink-mute">{who}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/customers.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/sales/customers.tsx
 * ----------------------------------------------------------------------------
 * Sales · CRM — Customers list with detail slideout.
 *
 * Reuses CRM_CUSTOMERS from lib/mock/sales.tsx.
 * ============================================================================
 */

function SalesCustomers() {
  const { push } = useRouter()
  const [tier, setTier]       = React.useState<"all" | "Enterprise" | "Mid-market" | "SMB">("all")
  const [country, setCountry] = React.useState<"all" | "PH" | "SG" | "HK">("all")
  const [health, setHealth]   = React.useState<"all" | "green" | "amber" | "red">("all")
  const [q, setQ]             = React.useState("")
  const [selected, setSelected] = React.useState<any | null>(null)

  const filtered = React.useMemo(() => {
    let r = CRM_CUSTOMERS
    if (tier !== "all")     r = r.filter(c => c.tier === tier)
    if (country !== "all")  r = r.filter(c => c.country === country)
    if (health !== "all")   r = r.filter(c => c.health === health)
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(c => c.name.toLowerCase().includes(s) || c.industry.toLowerCase().includes(s))
    return r
  }, [tier, country, health, q])

  const totalLTV  = CRM_CUSTOMERS.reduce((s, c) => s + c.lifetimeValue, 0)
  const totalAR   = CRM_CUSTOMERS.reduce((s, c) => s + c.arOpen, 0)
  const atRisk    = CRM_CUSTOMERS.filter(c => c.health !== "green").length

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "CRM" }, { label: "Customers" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="upload">Import</Button>
            <Button variant="primary" leadingIcon="plus">New Customer</Button>
          </>
        }
      />

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

          {/* Hero KPI strip */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Active customers" value={CRM_CUSTOMERS.length}                                  sub="across 3 countries"          accent="brand"   />
            <KpiTile label="Lifetime value"   value={`$${(totalLTV / 1_000_000).toFixed(1)}M`}              sub="USD equivalent"              accent="success" />
            <KpiTile label="Open AR"          value={`$${(totalAR / 1000).toFixed(0)}k`}                    sub={`${CRM_CUSTOMERS.filter(c => c.arOpen > 0).length} customers`} accent="warn" />
            <KpiTile label="Health watchlist" value={atRisk}                                                 sub="amber + red status"          accent="danger"  delta="+1 this week" deltaDir="down" />
          </div>

          {/* AI callout */}
          <AICallout
            title="2 customers need attention"
            body="Bright Industries HK has slipped from green to red (4 invoices >60d). Pacific Ventures Ltd. dropped to amber after a late payment last week. Both have open opportunities — worth a touch from Maya."
            matches={[
              { code: "c-bright",  name: "Bright Industries HK · health → red",  pct: 100 },
              { code: "c-pacvent", name: "Pacific Ventures Ltd. · health → amber", pct: 92 },
            ]}
            dismissLabel="Ignore for now"
            proceedLabel="Open both records"
          />

          {/* Filter strip */}
          <div className="flex items-center gap-2 flex-wrap">
            <SearchBox value={q} onChange={setQ} placeholder="Search customers, industry…" className="max-w-[280px]" />
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={tier === "all"}        onClick={() => setTier("all")}>All Tiers</FilterChip>
            <FilterChip active={tier === "Enterprise"} onClick={() => setTier("Enterprise")} tone="info">Enterprise</FilterChip>
            <FilterChip active={tier === "Mid-market"} onClick={() => setTier("Mid-market")} tone="accent">Mid-market</FilterChip>
            <FilterChip active={tier === "SMB"}        onClick={() => setTier("SMB")}>SMB</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={country === "all"} onClick={() => setCountry("all")}>All Countries</FilterChip>
            <FilterChip active={country === "PH"}  onClick={() => setCountry("PH")}>🇵🇭 PH</FilterChip>
            <FilterChip active={country === "SG"}  onClick={() => setCountry("SG")}>🇸🇬 SG</FilterChip>
            <FilterChip active={country === "HK"}  onClick={() => setCountry("HK")}>🇭🇰 HK</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={health === "all"}   onClick={() => setHealth("all")}>All Health</FilterChip>
            <FilterChip active={health === "green"} onClick={() => setHealth("green")} tone="success">Healthy</FilterChip>
            <FilterChip active={health === "amber"} onClick={() => setHealth("amber")} tone="warn">Watch</FilterChip>
            <FilterChip active={health === "red"}   onClick={() => setHealth("red")}   tone="danger">At Risk</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {CRM_CUSTOMERS.length}</span>
          </div>

          {/* Table */}
          <Card>
            <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">Customer</th>
                  <th>Tier</th>
                  <th>Industry</th>
                  <th>Country</th>
                  <th className="text-right">Lifetime Value</th>
                  <th className="text-right">Open AR</th>
                  <th className="text-center">Opps</th>
                  <th>Last touch</th>
                  <th>Health</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(c => (
                  <tr key={c.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(c)}>
                    <td className="px-4 py-2.5">
                      <div className="flex items-center gap-2.5">
                        <Avatar initials={c.initials} tone={c.tone} size="sm" />
                        <div>
                          <div className="font-semibold text-ink">{c.name}</div>
                          <div className="font-mono text-[10px] text-ink-mute">{c.id}</div>
                        </div>
                      </div>
                    </td>
                    <td>
                      <Badge variant={c.tier === "Enterprise" ? "info" : c.tier === "Mid-market" ? "accent" : "neutral"}>{c.tier}</Badge>
                    </td>
                    <td className="text-ink-soft">{c.industry}</td>
                    <td className="text-ink-soft">{c.country}</td>
                    <td className="text-right font-mono font-semibold text-ink">${(c.lifetimeValue / 1000).toFixed(0)}k</td>
                    <td className={cn("text-right font-mono font-semibold", c.arOpen > 0 ? "text-warn" : "text-ink-faint")}>{c.arOpen > 0 ? `$${(c.arOpen / 1000).toFixed(1)}k` : "—"}</td>
                    <td className="text-center font-mono text-ink-soft">{c.openOpps || "—"}</td>
                    <td className="text-ink-soft text-[11px]">{c.lastTouch}</td>
                    <td>
                      <span className={cn(
                        "inline-flex items-center gap-1 text-[10.5px] font-semibold",
                        c.health === "green" ? "text-success" : c.health === "amber" ? "text-warn" : "text-danger",
                      )}>
                        <span className={cn("size-1.5 rounded-full",
                          c.health === "green" ? "bg-success" : c.health === "amber" ? "bg-warn" : "bg-danger"
                        )} />
                        {c.health === "green" ? "Healthy" : c.health === "amber" ? "Watch" : "At Risk"}
                      </span>
                    </td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </div>
      </div>

      {selected && <CustomerSheet customer={selected} onClose={() => setSelected(null)} push={push} />}
    </>
  )
}

/* ─── Detail slideout ───────────────────────────────────────────────────── */
function CustomerSheet({ customer, onClose, push }: { customer: any; onClose: () => void; push: (r: string) => void }) {
  const opp = OPPORTUNITIES.find(o => o.customer.name === customer.name)
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div className="flex items-start gap-3">
          <Avatar initials={customer.initials} tone={customer.tone} size="lg" />
          <div>
            <div className="font-mono text-[10px] text-ink-mute">{customer.id}</div>
            <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{customer.name}</h2>
            <div className="text-[11px] text-ink-soft mt-1 flex items-center gap-2">
              <Badge variant={customer.tier === "Enterprise" ? "info" : customer.tier === "Mid-market" ? "accent" : "neutral"}>{customer.tier}</Badge>
              <span>·</span><span>{customer.industry}</span>
              <span>·</span><span>{customer.country}</span>
            </div>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>

      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Lifetime value</div>
              <div className="font-serif text-xl text-ink mt-0.5">${(customer.lifetimeValue / 1000).toFixed(0)}k</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Open AR</div>
              <div className={cn("font-serif text-xl mt-0.5", customer.arOpen > 0 ? "text-warn" : "text-ink")}>{customer.arOpen > 0 ? `$${(customer.arOpen / 1000).toFixed(1)}k` : "$0"}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Open opps</div>
              <div className="font-serif text-xl text-ink mt-0.5">{customer.openOpps}</div>
            </div>
          </div>
        </section>

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Contacts</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft">
            <ContactRow initials="MR" tone="ink"  name="Maria R. — CFO"          role="Primary · Finance · maria.r@example.com" />
            <ContactRow initials="DC" tone="teal" name="David C. — Procurement"  role="procurement@example.com · +63 917 555 0118" />
            <ContactRow initials="LL" tone="amber" name="Liza L. — IT Manager"   role="liza.l@example.com" />
          </div>
        </section>

        {opp && (
          <section className="mt-5">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Open opportunity</div>
            <button onClick={() => { onClose(); push("/sales/pipeline") }}
              className="w-full bg-accent-soft/40 border border-accent/40 rounded-md p-3 text-left hover:border-accent transition-colors">
              <div className="flex items-center justify-between">
                <div>
                  <div className="font-semibold text-ink">{opp.title}</div>
                  <div className="text-[10px] text-ink-mute mt-0.5">{opp.id} · {opp.probability}% · {opp.closeDate}</div>
                </div>
                <Icon name="arrow-right" size={14} className="text-accent-deep" />
              </div>
            </button>
          </section>
        )}

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Activity snapshot</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
            <ActivityLineCx when={customer.lastTouch} who="Maya C." what="Email exchange" detail="Pricing follow-up · 2 replies" />
            <ActivityLineCx when="Apr 11"             who="Rico T." what="Discovery call" detail="60min · 3 stakeholders" />
            <ActivityLineCx when="Apr 04"             who="System"  what="Invoice INV-2026-0322 paid" detail="$18,200 PHP" />
            <ActivityLineCx when="Mar 22"             who="Ana Q."  what="Quote sent · Q-2026-0044" detail="$84,000 · valid 30d" />
          </div>
        </section>
      </SheetBody>

      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="ghost" leadingIcon="message" className="ml-auto">Log activity</Button>
        <Button variant="primary" leadingIcon="arrow-right" onClick={() => { onClose(); push("/sales/quotes") }}>New quote</Button>
      </SheetFooter>
    </Sheet>
  )
}

function ContactRow({ initials, tone, name, role }: any) {
  return (
    <div className="px-3 py-2.5 flex items-center gap-2.5">
      <Avatar initials={initials} tone={tone} size="sm" />
      <div className="flex-1 min-w-0">
        <div className="text-[12px] font-semibold text-ink">{name}</div>
        <div className="text-[10px] text-ink-mute truncate">{role}</div>
      </div>
      <Icon name="mail" size={12} className="text-ink-faint" />
    </div>
  )
}
function ActivityLineCx({ when, who, what, detail }: any) {
  return (
    <div className="px-3 py-2 flex items-baseline gap-2">
      <span className="font-mono text-[10px] text-ink-faint w-12 shrink-0">{when}</span>
      <span className="text-ink"><strong className="font-semibold">{what}</strong> <span className="text-ink-mute">· {detail}</span></span>
      <span className="ml-auto text-[10px] text-ink-mute">{who}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/quotes.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/sales/quotes.tsx
 * ----------------------------------------------------------------------------
 * Sales · CRM — Quotes list with line-item detail and customer/opportunity
 * back-links. Surfaces an expiry watchlist at the top.
 * ============================================================================
 */

/* ─── Mock data (local) ─────────────────────────────────────────────────── */
interface QuoteLine { sku: string; description: string; qty: number; uom: string; price: number; discount: number }
interface Quote {
  id: string
  customer: { name: string; initials: string; tone: any }
  oppId?: string
  title: string
  status: "draft" | "sent" | "accepted" | "rejected" | "expired"
  currency: "PHP" | "SGD" | "HKD" | "USD"
  amount: number
  tax: number
  total: number
  validUntil: string
  daysToExpiry: number   /* neg = expired */
  owner: { name: string; initials: string; tone: any }
  createdAt: string
  sentAt?: string
  lines: QuoteLine[]
  notes?: string
}
const QUOTES: Quote[] = [
  { id: "Q-2026-0118", customer: { name: "Megaworld Corp",    initials: "MW", tone: "teal" },  oppId: "OPP-3094", title: "Megaworld · Phase 2 procurement add-on", status: "sent",     currency: "USD", amount: 320000, tax: 38400,  total: 358400, validUntil: "Apr 30, 2026", daysToExpiry: 13, owner: { name: "Maya C.",  initials: "MC", tone: "teal" }, createdAt: "Apr 09, 2026", sentAt: "Apr 11, 2026", lines: [
      { sku: "MOD-PRC-ENT",    description: "Cenora Procurement · Enterprise tier · 8 entities",      qty: 12, uom: "month", price: 14400, discount: 0  },
      { sku: "SRV-IMPL-PRC",   description: "Implementation · scope of 8 entities, 240h",              qty: 1,  uom: "fixed", price: 72000, discount: 0  },
      { sku: "SUP-PRC-PRM",    description: "Premium support · 24/7 PH + SG · 1yr",                    qty: 1,  uom: "year",  price: 38000, discount: 10 },
    ], notes: "Renews to standard support after Year 1 unless extended." },
  { id: "Q-2026-0117", customer: { name: "Republic Cement",   initials: "RP", tone: "teal" },  oppId: "OPP-3093", title: "Month-end automation · 4-module bundle",     status: "accepted", currency: "USD", amount: 144000, tax: 17280,  total: 161280, validUntil: "Apr 25, 2026", daysToExpiry: 8,  owner: { name: "Rico T.", initials: "RT", tone: "blue" }, createdAt: "Apr 04, 2026", sentAt: "Apr 06, 2026", lines: [
      { sku: "MOD-FIN-ENT",    description: "Cenora Finance · Enterprise tier · 3 entities",           qty: 12, uom: "month", price: 7200,  discount: 0  },
      { sku: "MOD-HRIS-ENT",   description: "Cenora HRIS · 240 employees",                              qty: 12, uom: "month", price: 4800,  discount: 0  },
      { sku: "SRV-IMPL-MEC",   description: "Month-end close blueprint · 60h",                          qty: 1,  uom: "fixed", price: 18000, discount: 0  },
    ] },
  { id: "Q-2026-0116", customer: { name: "Pacific Solar PH",  initials: "PS", tone: "blue" },  oppId: "OPP-3095", title: "Field service add-on · pilot 4 sites",       status: "sent",     currency: "USD", amount: 22000,  tax: 2640,   total: 24640,  validUntil: "Apr 20, 2026", daysToExpiry: 3,  owner: { name: "Ana Q.",  initials: "AQ", tone: "sand" }, createdAt: "Mar 28, 2026", sentAt: "Apr 02, 2026", lines: [
      { sku: "MOD-FLD-SMB",    description: "Cenora Field Service · SMB tier · up to 8 crew",          qty: 12, uom: "month", price: 1500,  discount: 0  },
      { sku: "SRV-IMPL-FLD",   description: "Pilot rollout · 4 PH sites",                                qty: 1,  uom: "fixed", price: 4000,  discount: 0  },
    ] },
  { id: "Q-2026-0115", customer: { name: "Apex Kitchen Supplies", initials: "AK", tone: "amber" }, oppId: "OPP-3092", title: "Multi-entity HRIS · 3 entities", status: "draft",    currency: "USD", amount: 38000,  tax: 4560,   total: 42560,  validUntil: "May 02, 2026", daysToExpiry: 15, owner: { name: "Ana Q.",  initials: "AQ", tone: "sand" }, createdAt: "Apr 14, 2026", lines: [
      { sku: "MOD-HRIS-MID",   description: "Cenora HRIS · 120 employees, 3 entities",                  qty: 12, uom: "month", price: 2400,  discount: 0  },
      { sku: "SRV-IMPL-HRIS",  description: "Implementation · 80h",                                      qty: 1,  uom: "fixed", price: 9200,  discount: 0  },
    ] },
  { id: "Q-2026-0114", customer: { name: "Filinvest Land",    initials: "FL", tone: "teal" },  oppId: "OPP-3099", title: "Project accounting · PoC pricing",            status: "sent",     currency: "USD", amount: 142000, tax: 17040,  total: 159040, validUntil: "Apr 18, 2026", daysToExpiry: 1,  owner: { name: "Rico T.", initials: "RT", tone: "blue" }, createdAt: "Apr 02, 2026", sentAt: "Apr 03, 2026", lines: [
      { sku: "MOD-PRJ-ENT",    description: "Cenora Project Accounting · 25 active projects",          qty: 12, uom: "month", price: 8500,  discount: 0  },
      { sku: "SRV-IMPL-PRJ",   description: "Implementation · 200h",                                     qty: 1,  uom: "fixed", price: 40000, discount: 0  },
    ] },
  { id: "Q-2026-0113", customer: { name: "Bright Industries HK", initials: "BI", tone: "red" },  oppId: "OPP-3097", title: "Full suite · 8 entities (Bright Group)", status: "sent",   currency: "HKD", amount: 880000, tax: 0,      total: 880000, validUntil: "May 31, 2026", daysToExpiry: 44, owner: { name: "Maya C.",  initials: "MC", tone: "teal" }, createdAt: "Apr 08, 2026", sentAt: "Apr 09, 2026", lines: [
      { sku: "MOD-SUITE-ENT",  description: "Cenora Business Suite · all 11 modules · 8 entities",     qty: 12, uom: "month", price: 65000, discount: 12 },
      { sku: "SRV-IMPL-SUITE", description: "Implementation · senior team, 720h, 4 stages",            qty: 1,  uom: "fixed", price: 184000, discount: 0  },
    ] },
  { id: "Q-2026-0112", customer: { name: "SteelMark Industries", initials: "SM", tone: "amber" }, oppId: "OPP-3098", title: "Vendor portal + AP automation", status: "accepted", currency: "USD", amount: 64000,  tax: 7680,   total: 71680,  validUntil: "May 12, 2026", daysToExpiry: 25, owner: { name: "Ana Q.",  initials: "AQ", tone: "sand" }, createdAt: "Mar 30, 2026", sentAt: "Mar 31, 2026", lines: [
      { sku: "MOD-PRC-MID",    description: "Procurement · Mid-market · vendor portal",                qty: 12, uom: "month", price: 3200,  discount: 0  },
      { sku: "SRV-IMPL-PRC",   description: "AP automation · 120h",                                      qty: 1,  uom: "fixed", price: 25600, discount: 0  },
    ] },
  { id: "Q-2026-0111", customer: { name: "Holcim Philippines", initials: "HP", tone: "red" },    oppId: "OPP-3089", title: "Full suite · 6 entities",                    status: "rejected", currency: "USD", amount: 240000, tax: 28800,  total: 268800, validUntil: "Apr 02, 2026", daysToExpiry: -15, owner: { name: "Rico T.", initials: "RT", tone: "blue" }, createdAt: "Mar 12, 2026", sentAt: "Mar 14, 2026", lines: [
      { sku: "MOD-SUITE-ENT",  description: "Cenora Business Suite · 6 entities",                     qty: 12, uom: "month", price: 15800, discount: 0  },
      { sku: "SRV-IMPL-SUITE", description: "Implementation · 480h",                                  qty: 1,  uom: "fixed", price: 50400, discount: 0  },
    ] },
  { id: "Q-2026-0110", customer: { name: "TechNet Solutions", initials: "TN", tone: "teal" },    oppId: "OPP-3091", title: "Cenora Suite · subscription",                 status: "accepted", currency: "USD", amount: 88000,  tax: 10560,  total: 98560,  validUntil: "Apr 15, 2026", daysToExpiry: -2, owner: { name: "Maya C.",  initials: "MC", tone: "teal" }, createdAt: "Mar 22, 2026", sentAt: "Mar 22, 2026", lines: [
      { sku: "MOD-SUITE-MID",  description: "Cenora Suite · mid-market tier",                          qty: 12, uom: "month", price: 5400,  discount: 0  },
      { sku: "SRV-IMPL-SUITE", description: "Implementation · 120h",                                   qty: 1,  uom: "fixed", price: 23200, discount: 0  },
    ] },
  { id: "Q-2026-0109", customer: { name: "Cebu Pacific",      initials: "CP", tone: "amber" },   oppId: "OPP-3102", title: "Fleet maintenance ERP · proposal v1",         status: "expired",  currency: "USD", amount: 420000, tax: 50400,  total: 470400, validUntil: "Apr 10, 2026", daysToExpiry: -7, owner: { name: "Rico T.", initials: "RT", tone: "blue" }, createdAt: "Feb 18, 2026", sentAt: "Feb 22, 2026", lines: [
      { sku: "MOD-MFG-ENT",    description: "Cenora Manufacturing · fleet variant",                    qty: 12, uom: "month", price: 18500, discount: 0  },
      { sku: "SRV-IMPL-MFG",   description: "Implementation · 480h",                                   qty: 1,  uom: "fixed", price: 198000, discount: 0  },
    ] },
]
const QUOTE_STATUS_LABEL: Record<Quote["status"], string> = { draft: "Draft", sent: "Sent", accepted: "Accepted", rejected: "Rejected", expired: "Expired" }
const QUOTE_STATUS_TONE: Record<Quote["status"], string> = { draft: "neutral", sent: "info", accepted: "success", rejected: "danger", expired: "warn" }

const sym = (c: Quote["currency"]) => c === "PHP" ? "₱" : c === "SGD" ? "S$" : c === "HKD" ? "HK$" : "$"
const toUSDQ = (v: number, c: Quote["currency"]) => c === "PHP" ? v / 56.95 : c === "SGD" ? v / 1.35 : c === "HKD" ? v / 7.81 : v

function SalesQuotes() {
  const { push } = useRouter()
  const [status, setStatus] = React.useState<"all" | Quote["status"]>("all")
  const [owner, setOwner]   = React.useState<"all" | "mine">("all")
  const [q, setQ]           = React.useState("")
  const [selected, setSelected] = React.useState<Quote | null>(null)

  const filtered = React.useMemo(() => {
    let r = QUOTES
    if (status !== "all") r = r.filter(x => x.status === status)
    if (owner === "mine") r = r.filter(x => x.owner.initials === "KL")
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(x => x.title.toLowerCase().includes(s) || x.customer.name.toLowerCase().includes(s) || x.id.toLowerCase().includes(s))
    return r
  }, [status, owner, q])

  const counts: Record<string, number> = { all: QUOTES.length }
  for (const x of QUOTES) counts[x.status] = (counts[x.status] ?? 0) + 1

  const openValue   = QUOTES.filter(q => q.status === "sent" || q.status === "draft").reduce((s, x) => s + toUSDQ(x.total, x.currency), 0)
  const winValue    = QUOTES.filter(q => q.status === "accepted").reduce((s, x) => s + toUSDQ(x.total, x.currency), 0)
  const expiringSoon = QUOTES.filter(q => q.status === "sent" && q.daysToExpiry <= 5 && q.daysToExpiry >= 0)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "CRM" }, { label: "Quotes" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="primary" leadingIcon="plus">New Quote</Button>
          </>
        }
      />

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

          {/* KPI strip */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Open value"         value={`$${(openValue / 1000).toFixed(0)}k`}     sub={`${QUOTES.filter(q => q.status === "sent" || q.status === "draft").length} live quotes`} accent="brand"   />
            <KpiTile label="Won · MTD"          value={`$${(winValue / 1000).toFixed(0)}k`}      sub={`${counts.accepted ?? 0} accepted`}      accent="success" delta="+12% MoM" deltaDir="up" />
            <KpiTile label="Expiring ≤5d"       value={expiringSoon.length}                     sub={`$${(expiringSoon.reduce((s, x) => s + toUSDQ(x.total, x.currency), 0) / 1000).toFixed(0)}k at risk`} accent="warn" />
            <KpiTile label="Avg cycle (sent → accepted)" value="6.4d"                          sub="median across closed-won"                accent="accent" />
          </div>

          {/* AI callout — expiring */}
          {expiringSoon.length > 0 && (
            <AICallout
              title={`${expiringSoon.length} quote${expiringSoon.length === 1 ? "" : "s"} expiring this week`}
              body="Filinvest's PoC quote expires in 24h — last contact was Apr 10. Pacific Solar's pilot expires in 3 days; they asked for a small scope tweak that's still in Slack. Want to auto-draft expiry-reminder emails for both?"
              matches={expiringSoon.map(q => ({ code: q.id, name: `${q.customer.name} · ${q.daysToExpiry === 0 ? "expires today" : q.daysToExpiry + "d left"}`, pct: 100 - q.daysToExpiry * 5 }))}
              dismissLabel="Ignore"
              proceedLabel="Draft reminders"
            />
          )}

          {/* Filters */}
          <div className="flex items-center gap-2 flex-wrap">
            <SearchBox value={q} onChange={setQ} placeholder="Search by customer, ID, title…" className="max-w-[280px]" />
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={status === "all"}       onClick={() => setStatus("all")}>All <span className="opacity-50 ml-1 font-mono">{counts.all}</span></FilterChip>
            <FilterChip active={status === "draft"}     onClick={() => setStatus("draft")}>Draft <span className="opacity-50 ml-1 font-mono">{counts.draft ?? 0}</span></FilterChip>
            <FilterChip active={status === "sent"}      onClick={() => setStatus("sent")} tone="info">Sent <span className="opacity-50 ml-1 font-mono">{counts.sent ?? 0}</span></FilterChip>
            <FilterChip active={status === "accepted"}  onClick={() => setStatus("accepted")} tone="success">Accepted <span className="opacity-50 ml-1 font-mono">{counts.accepted ?? 0}</span></FilterChip>
            <FilterChip active={status === "rejected"}  onClick={() => setStatus("rejected")} tone="danger">Rejected <span className="opacity-50 ml-1 font-mono">{counts.rejected ?? 0}</span></FilterChip>
            <FilterChip active={status === "expired"}   onClick={() => setStatus("expired")} tone="warn">Expired <span className="opacity-50 ml-1 font-mono">{counts.expired ?? 0}</span></FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={owner === "all"}  onClick={() => setOwner("all")}>All Owners</FilterChip>
            <FilterChip active={owner === "mine"} onClick={() => setOwner("mine")} icon="user">Mine (Maya)</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {QUOTES.length}</span>
          </div>

          {/* List */}
          <Card>
            <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">Quote</th>
                  <th>Customer</th>
                  <th>Title</th>
                  <th>Status</th>
                  <th className="text-right">Amount</th>
                  <th className="text-right">Tax</th>
                  <th className="text-right">Total</th>
                  <th>Valid until</th>
                  <th>Owner</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(quote => (
                  <tr key={quote.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(quote)}>
                    <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{quote.id}</td>
                    <td>
                      <div className="flex items-center gap-2">
                        <Avatar initials={quote.customer.initials} tone={quote.customer.tone} size="xs" />
                        <span className="font-semibold text-ink">{quote.customer.name}</span>
                      </div>
                    </td>
                    <td className="text-ink-soft max-w-[280px] truncate">{quote.title}</td>
                    <td><Badge variant={QUOTE_STATUS_TONE[quote.status] as any} dot>{QUOTE_STATUS_LABEL[quote.status]}</Badge></td>
                    <td className="text-right font-mono text-ink-soft">{sym(quote.currency)}{quote.amount.toLocaleString()}</td>
                    <td className="text-right font-mono text-ink-faint text-[11px]">{sym(quote.currency)}{quote.tax.toLocaleString()}</td>
                    <td className="text-right font-mono font-semibold text-ink">{sym(quote.currency)}{quote.total.toLocaleString()}</td>
                    <td>
                      <div className="text-[11px] text-ink-soft">{quote.validUntil}</div>
                      {quote.status === "sent" && (
                        <div className={cn("text-[10px] font-semibold", quote.daysToExpiry <= 3 ? "text-danger" : quote.daysToExpiry <= 7 ? "text-warn" : "text-ink-mute")}>
                          {quote.daysToExpiry < 0 ? `${Math.abs(quote.daysToExpiry)}d ago` : quote.daysToExpiry === 0 ? "today" : `${quote.daysToExpiry}d`}
                        </div>
                      )}
                    </td>
                    <td><Avatar initials={quote.owner.initials} tone={quote.owner.tone} size="xs" /></td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </div>
      </div>

      {selected && <QuoteSheet quote={selected} onClose={() => setSelected(null)} push={push} />}
    </>
  )
}

/* ─── Quote slideout ────────────────────────────────────────────────────── */
function QuoteSheet({ quote, onClose, push }: { quote: Quote; onClose: () => void; push: (r: string) => void }) {
  return (
    <Sheet open={true} onOpenChange={onClose} side="right" >
      <SheetHeader>
        <div>
          <div className="font-mono text-[10px] text-ink-mute">{quote.id}</div>
          <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{quote.title}</h2>
          <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
            <Avatar initials={quote.customer.initials} tone={quote.customer.tone} size="xs" />
            <span className="font-semibold">{quote.customer.name}</span>
            <span>·</span>
            <Badge variant={QUOTE_STATUS_TONE[quote.status] as any} dot>{QUOTE_STATUS_LABEL[quote.status]}</Badge>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>

      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Subtotal</div>
              <div className="font-serif text-xl text-ink mt-0.5">{sym(quote.currency)}{quote.amount.toLocaleString()}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Tax</div>
              <div className="font-serif text-xl text-ink mt-0.5">{sym(quote.currency)}{quote.tax.toLocaleString()}</div>
            </div>
            <div className="bg-brand-soft border border-brand/20 rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-brand-mid">Total</div>
              <div className="font-serif text-xl text-brand mt-0.5">{sym(quote.currency)}{quote.total.toLocaleString()}</div>
              <div className="text-[10px] text-ink-mute mt-0.5">{quote.currency} · valid until {quote.validUntil}</div>
            </div>
          </div>
        </section>

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Line items</div>
          <div className="bg-surface border border-divider rounded-md overflow-hidden">
            <table className="w-full text-[11.5px]">
              <thead>
                <tr className="bg-cream text-left text-[9.5px] font-bold uppercase tracking-wider text-ink-mute">
                  <th className="px-3 py-2">SKU</th>
                  <th>Description</th>
                  <th className="text-right">Qty</th>
                  <th className="text-right">Unit</th>
                  <th className="text-right">Disc.</th>
                  <th className="text-right">Line total</th>
                </tr>
              </thead>
              <tbody>
                {quote.lines.map(l => {
                  const gross = l.qty * l.price
                  const net   = gross * (1 - l.discount / 100)
                  return (
                    <tr key={l.sku} className="border-t border-divider-soft">
                      <td className="px-3 py-2 font-mono font-semibold text-brand-mid text-[10.5px]">{l.sku}</td>
                      <td className="text-ink-soft">{l.description}</td>
                      <td className="text-right font-mono text-ink">{l.qty}<span className="text-ink-faint text-[10px]"> {l.uom}</span></td>
                      <td className="text-right font-mono text-ink-soft">{sym(quote.currency)}{l.price.toLocaleString()}</td>
                      <td className="text-right font-mono text-ink-faint">{l.discount > 0 ? `${l.discount}%` : "—"}</td>
                      <td className="text-right font-mono font-semibold text-ink">{sym(quote.currency)}{Math.round(net).toLocaleString()}</td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </div>
        </section>

        {quote.notes && (
          <section className="mt-5">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Terms &amp; notes</div>
            <div className="bg-cream/60 border border-divider rounded-md p-3 text-[11.5px] text-ink-soft leading-relaxed">{quote.notes}</div>
          </section>
        )}

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Linked records</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[12px]">
            {quote.oppId && (
              <button onClick={() => { onClose(); push("/sales/pipeline") }} className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-cream/40">
                <div className="flex items-center gap-2">
                  <Icon name="trending-up" size={12} className="text-ink-mute" />
                  <span className="text-ink-soft">Opportunity</span>
                  <span className="font-mono font-semibold text-brand-mid">{quote.oppId}</span>
                </div>
                <Icon name="arrow-right" size={12} className="text-ink-faint" />
              </button>
            )}
            <button onClick={() => { onClose(); push("/sales/customers") }} className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-cream/40">
              <div className="flex items-center gap-2">
                <Icon name="user" size={12} className="text-ink-mute" />
                <span className="text-ink-soft">Customer</span>
                <span className="font-semibold text-ink">{quote.customer.name}</span>
              </div>
              <Icon name="arrow-right" size={12} className="text-ink-faint" />
            </button>
          </div>
        </section>
      </SheetBody>

      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="ghost" leadingIcon="copy" className="ml-auto">Duplicate</Button>
        <Button variant="ghost" leadingIcon="printer">PDF</Button>
        {quote.status === "draft" && <Button variant="primary" leadingIcon="mail">Send to customer</Button>}
        {quote.status === "sent"  && <Button variant="primary" leadingIcon="check">Mark accepted</Button>}
      </SheetFooter>
    </Sheet>
  )
}

// SANDBOX
;(globalThis as any).SalesQuotes = SalesQuotes
;(globalThis as any).QUOTES = QUOTES

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/invoices.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/sales/invoices.tsx
 * ----------------------------------------------------------------------------
 * Sales · CRM — Invoices &amp; AR aging.
 *
 * Hero: total AR + aging bands (Current / 1-30 / 31-60 / 61-90 / 90+),
 *       collected MTD, DSO.
 * Then a list of invoices with status, due date, balance, customer back-link.
 * AI callout highlights the largest collection opportunity.
 * ============================================================================
 */

interface Invoice {
  id: string
  customer: { name: string; initials: string; tone: any }
  issued: string
  due: string
  daysPastDue: number   /* neg = not yet due, 0 = due today, pos = past due */
  status: "draft" | "sent" | "partial" | "paid" | "overdue"
  amount: number
  paid: number
  balance: number
  currency: "PHP" | "SGD" | "HKD" | "USD"
  ref?: string           /* PO ref */
  ageBand: "current" | "1-30" | "31-60" | "61-90" | "90+"
}

const INVOICES: Invoice[] = [
  /* current / future */
  { id: "INV-2026-0451", customer: { name: "Megaworld Corp",     initials: "MW", tone: "teal"  }, issued: "Apr 14, 2026", due: "May 14, 2026", daysPastDue: -27, status: "sent",    amount: 84000,  paid: 0,      balance: 84000,  currency: "USD", ref: "PO-Atlas-1089", ageBand: "current" },
  { id: "INV-2026-0450", customer: { name: "Globe Telecom",      initials: "GT", tone: "amber" }, issued: "Apr 12, 2026", due: "May 12, 2026", daysPastDue: -25, status: "sent",    amount: 22400,  paid: 0,      balance: 22400,  currency: "USD",                          ageBand: "current" },
  { id: "INV-2026-0449", customer: { name: "Republic Cement",    initials: "RP", tone: "teal"  }, issued: "Apr 09, 2026", due: "May 09, 2026", daysPastDue: -22, status: "partial", amount: 28800,  paid: 14000,  balance: 14800,  currency: "USD",                          ageBand: "current" },
  /* 1–30 overdue */
  { id: "INV-2026-0442", customer: { name: "Prime Hotels Group", initials: "PH", tone: "amber" }, issued: "Mar 12, 2026", due: "Apr 11, 2026", daysPastDue: 6,    status: "overdue", amount: 18400,  paid: 0,      balance: 18400,  currency: "USD",                          ageBand: "1-30" },
  { id: "INV-2026-0440", customer: { name: "Megaworld Corp",     initials: "MW", tone: "teal"  }, issued: "Mar 08, 2026", due: "Apr 07, 2026", daysPastDue: 10,   status: "partial", amount: 24000,  paid: 14000,  balance: 10000,  currency: "USD",                          ageBand: "1-30" },
  { id: "INV-2026-0438", customer: { name: "Metro Retail Chain", initials: "MR", tone: "ink"   }, issued: "Mar 04, 2026", due: "Apr 03, 2026", daysPastDue: 14,   status: "overdue", amount: 33600,  paid: 0,      balance: 33600,  currency: "USD",                          ageBand: "1-30" },
  { id: "INV-2026-0436", customer: { name: "Pacific Ventures Ltd.", initials: "PV", tone: "blue" }, issued: "Feb 28, 2026", due: "Mar 30, 2026", daysPastDue: 18, status: "overdue", amount: 19200,  paid: 0,      balance: 19200,  currency: "SGD",                          ageBand: "1-30" },
  /* 31–60 */
  { id: "INV-2026-0410", customer: { name: "Cebu Pacific",       initials: "CP", tone: "amber" }, issued: "Feb 12, 2026", due: "Mar 14, 2026", daysPastDue: 34,   status: "overdue", amount: 14200,  paid: 0,      balance: 14200,  currency: "USD",                          ageBand: "31-60" },
  { id: "INV-2026-0408", customer: { name: "Bright Industries HK", initials: "BI", tone: "red" }, issued: "Feb 06, 2026", due: "Mar 08, 2026", daysPastDue: 40,   status: "overdue", amount: 7800,   paid: 0,      balance: 7800,   currency: "HKD",                          ageBand: "31-60" },
  /* 61–90 */
  { id: "INV-2026-0390", customer: { name: "Bright Industries HK", initials: "BI", tone: "red" }, issued: "Jan 14, 2026", due: "Feb 13, 2026", daysPastDue: 63,   status: "overdue", amount: 4800,   paid: 0,      balance: 4800,   currency: "HKD",                          ageBand: "61-90" },
  { id: "INV-2026-0388", customer: { name: "Bright Industries HK", initials: "BI", tone: "red" }, issued: "Jan 10, 2026", due: "Feb 09, 2026", daysPastDue: 67,   status: "overdue", amount: 1800,   paid: 0,      balance: 1800,   currency: "HKD",                          ageBand: "61-90" },
  /* 90+ */
  { id: "INV-2026-0360", customer: { name: "Bright Industries HK", initials: "BI", tone: "red" }, issued: "Dec 18, 2025", due: "Jan 17, 2026", daysPastDue: 90,   status: "overdue", amount: 1200,   paid: 0,      balance: 1200,   currency: "HKD",                          ageBand: "90+" },
  /* paid (recent activity) */
  { id: "INV-2026-0430", customer: { name: "Filinvest Land",     initials: "FL", tone: "teal"  }, issued: "Mar 18, 2026", due: "Apr 17, 2026", daysPastDue: 0,    status: "paid",    amount: 18200,  paid: 18200,  balance: 0,      currency: "USD",                          ageBand: "current" },
  { id: "INV-2026-0428", customer: { name: "TechNet Solutions",  initials: "TN", tone: "teal"  }, issued: "Mar 15, 2026", due: "Apr 14, 2026", daysPastDue: 0,    status: "paid",    amount: 5400,   paid: 5400,   balance: 0,      currency: "USD",                          ageBand: "current" },
]

const INV_STATUS_TONE: Record<Invoice["status"], string> = { draft: "neutral", sent: "info", partial: "warn", paid: "success", overdue: "danger" }
const INV_STATUS_LABEL: Record<Invoice["status"], string> = { draft: "Draft", sent: "Sent", partial: "Partial", paid: "Paid", overdue: "Overdue" }

const symI  = (c: Invoice["currency"]) => c === "PHP" ? "₱" : c === "SGD" ? "S$" : c === "HKD" ? "HK$" : "$"
const toUSD = (v: number, c: Invoice["currency"]) => c === "PHP" ? v / 56.95 : c === "SGD" ? v / 1.35 : c === "HKD" ? v / 7.81 : v

function SalesInvoices() {
  const { push } = useRouter()
  const [band, setBand]     = React.useState<"all" | Invoice["ageBand"]>("all")
  const [status, setStatus] = React.useState<"all" | Invoice["status"]>("all")
  const [q, setQ]           = React.useState("")
  const [selected, setSelected] = React.useState<Invoice | null>(null)

  /* Aging totals — USD-equivalent */
  const aging = {
    current: 0, "1-30": 0, "31-60": 0, "61-90": 0, "90+": 0,
  } as Record<Invoice["ageBand"], number>
  let arTotal = 0
  for (const inv of INVOICES) {
    if (inv.status === "paid") continue
    const v = toUSD(inv.balance, inv.currency)
    aging[inv.ageBand] += v
    arTotal += v
  }
  const paidMTD     = INVOICES.filter(i => i.status === "paid").reduce((s, i) => s + toUSD(i.amount, i.currency), 0)
  const overdueAR   = aging["1-30"] + aging["31-60"] + aging["61-90"] + aging["90+"]
  const bandPct = (band: Invoice["ageBand"]) => (aging[band] / Math.max(arTotal, 1)) * 100

  const filtered = React.useMemo(() => {
    let r = INVOICES
    if (band !== "all")   r = r.filter(i => i.ageBand === band)
    if (status !== "all") r = r.filter(i => i.status === status)
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(i => i.customer.name.toLowerCase().includes(s) || i.id.toLowerCase().includes(s))
    return r
  }, [band, status, q])

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "CRM" }, { label: "Invoices · AR" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export aging</Button>
            <Button variant="ghost" leadingIcon="mail">Send statements</Button>
            <Button variant="primary" leadingIcon="plus">New Invoice</Button>
          </>
        }
      />

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

          {/* Hero: total AR + aging bands */}
          <div className="grid grid-cols-[1.4fr_1fr_1fr] gap-4">
            <div className="bg-gradient-to-br from-brand to-brand-light text-white rounded-lg p-5 relative overflow-hidden">
              <div className="text-[10px] font-bold uppercase tracking-wider text-accent">Open AR · all entities</div>
              <div className="font-serif text-[40px] leading-none mt-1.5">${(arTotal / 1000).toFixed(0)}k</div>
              <div className="text-[11px] text-white/70 mt-1.5">USD equivalent · {INVOICES.filter(i => i.status !== "paid").length} open invoices</div>

              {/* Aging band bar */}
              <div className="flex h-3 mt-5 rounded-full overflow-hidden bg-white/10">
                <div className="bg-success"     style={{ width: `${bandPct("current")}%` }} title="Current" />
                <div className="bg-info"        style={{ width: `${bandPct("1-30")}%` }}    title="1–30d" />
                <div className="bg-warn"        style={{ width: `${bandPct("31-60")}%` }}   title="31–60d" />
                <div className="bg-accent"      style={{ width: `${bandPct("61-90")}%` }}   title="61–90d" />
                <div className="bg-danger"      style={{ width: `${bandPct("90+")}%` }}     title="90+d" />
              </div>
              <div className="grid grid-cols-5 gap-1 mt-2 text-[10px]">
                <BandLabel label="Current"   pct={bandPct("current")} amt={aging["current"]} color="text-success" />
                <BandLabel label="1–30d"     pct={bandPct("1-30")}    amt={aging["1-30"]}    color="text-info"    />
                <BandLabel label="31–60d"    pct={bandPct("31-60")}   amt={aging["31-60"]}   color="text-warn"    />
                <BandLabel label="61–90d"    pct={bandPct("61-90")}   amt={aging["61-90"]}   color="text-accent"  />
                <BandLabel label="90+d"      pct={bandPct("90+")}     amt={aging["90+"]}     color="text-danger"  />
              </div>
            </div>
            <KpiTile label="Overdue AR" value={`$${(overdueAR / 1000).toFixed(0)}k`} sub={`${INVOICES.filter(i => i.status === "overdue" || i.status === "partial").length} invoices`} accent="danger" delta="+3.2k WoW" deltaDir="down" />
            <KpiTile label="Collected · MTD" value={`$${(paidMTD / 1000).toFixed(0)}k`} sub={`${INVOICES.filter(i => i.status === "paid").length} payments`} accent="success" delta="+18%" deltaDir="up" />
          </div>

          {/* AI callout */}
          <AICallout
            title="Bright Industries HK · 4 invoices, 90+d aging"
            body="Their AR concentration is 64% of all overdue >60d. The pattern matches a CFO change announced in their Q1 release. Want to draft a polite statement of account email and book a call on Maya's calendar for tomorrow?"
            matches={[
              { code: "INV-2026-0360", name: "HK$1,200 · 90d past due",  pct: 100 },
              { code: "INV-2026-0388", name: "HK$1,800 · 67d past due",  pct: 95  },
              { code: "INV-2026-0390", name: "HK$4,800 · 63d past due",  pct: 92  },
            ]}
            dismissLabel="Not now"
            proceedLabel="Draft email + book call"
          />

          {/* Filter strip */}
          <div className="flex items-center gap-2 flex-wrap">
            <SearchBox value={q} onChange={setQ} placeholder="Search invoices / customers…" className="max-w-[280px]" />
            <span className="w-px h-5 bg-divider mx-1" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Aging</span>
            <FilterChip active={band === "all"}    onClick={() => setBand("all")}>All</FilterChip>
            <FilterChip active={band === "current"} onClick={() => setBand("current")} tone="success">Current</FilterChip>
            <FilterChip active={band === "1-30"}   onClick={() => setBand("1-30")}  tone="info">1–30d</FilterChip>
            <FilterChip active={band === "31-60"}  onClick={() => setBand("31-60")} tone="warn">31–60d</FilterChip>
            <FilterChip active={band === "61-90"}  onClick={() => setBand("61-90")} tone="accent">61–90d</FilterChip>
            <FilterChip active={band === "90+"}    onClick={() => setBand("90+")}   tone="danger">90+d</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={status === "all"}     onClick={() => setStatus("all")}>All status</FilterChip>
            <FilterChip active={status === "overdue"} onClick={() => setStatus("overdue")} tone="danger">Overdue</FilterChip>
            <FilterChip active={status === "partial"} onClick={() => setStatus("partial")} tone="warn">Partial</FilterChip>
            <FilterChip active={status === "sent"}    onClick={() => setStatus("sent")} tone="info">Sent</FilterChip>
            <FilterChip active={status === "paid"}    onClick={() => setStatus("paid")} tone="success">Paid</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {INVOICES.length}</span>
          </div>

          {/* List */}
          <Card>
            <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">Invoice</th>
                  <th>Customer</th>
                  <th>Issued</th>
                  <th>Due</th>
                  <th>Age</th>
                  <th>Status</th>
                  <th className="text-right">Amount</th>
                  <th className="text-right">Balance</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(inv => (
                  <tr key={inv.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(inv)}>
                    <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{inv.id}</td>
                    <td>
                      <div className="flex items-center gap-2">
                        <Avatar initials={inv.customer.initials} tone={inv.customer.tone} size="xs" />
                        <span className="font-semibold text-ink">{inv.customer.name}</span>
                      </div>
                    </td>
                    <td className="text-ink-soft text-[11px]">{inv.issued}</td>
                    <td className="text-ink-soft text-[11px]">{inv.due}</td>
                    <td className={cn("text-[11px] font-semibold",
                      inv.daysPastDue < 0 ? "text-ink-mute" :
                      inv.daysPastDue === 0 ? "text-ink" :
                      inv.daysPastDue <= 30 ? "text-warn" :
                      inv.daysPastDue <= 60 ? "text-accent-deep" : "text-danger")}>
                      {inv.daysPastDue < 0 ? `due in ${Math.abs(inv.daysPastDue)}d` : inv.daysPastDue === 0 ? "due today" : `${inv.daysPastDue}d late`}
                    </td>
                    <td><Badge variant={INV_STATUS_TONE[inv.status] as any} dot>{INV_STATUS_LABEL[inv.status]}</Badge></td>
                    <td className="text-right font-mono text-ink-soft">{symI(inv.currency)}{inv.amount.toLocaleString()}</td>
                    <td className={cn("text-right font-mono font-semibold", inv.balance === 0 ? "text-success" : inv.balance < inv.amount ? "text-warn" : "text-ink")}>
                      {symI(inv.currency)}{inv.balance.toLocaleString()}
                    </td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </div>
      </div>

      {selected && <InvoiceSheet invoice={selected} onClose={() => setSelected(null)} push={push} />}
    </>
  )
}

function BandLabel({ label, pct, amt, color }: any) {
  return (
    <div>
      <div className={cn("text-[10px] uppercase tracking-wider font-bold", color)}>{label}</div>
      <div className="font-mono text-white text-[11px] mt-0.5">${(amt / 1000).toFixed(1)}k</div>
      <div className="text-white/50 text-[9px]">{pct.toFixed(0)}%</div>
    </div>
  )
}

function InvoiceSheet({ invoice, onClose, push }: { invoice: Invoice; onClose: () => void; push: (r: string) => void }) {
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div>
          <div className="font-mono text-[10px] text-ink-mute">{invoice.id}</div>
          <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{invoice.customer.name}</h2>
          <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
            <Badge variant={INV_STATUS_TONE[invoice.status] as any} dot>{INV_STATUS_LABEL[invoice.status]}</Badge>
            <span>·</span>
            <span>Issued {invoice.issued} · Due {invoice.due}</span>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>
      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Total</div>
              <div className="font-serif text-xl text-ink mt-0.5">{symI(invoice.currency)}{invoice.amount.toLocaleString()}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Paid</div>
              <div className="font-serif text-xl text-success mt-0.5">{symI(invoice.currency)}{invoice.paid.toLocaleString()}</div>
            </div>
            <div className={cn("border rounded-md p-3", invoice.balance === 0 ? "bg-success-soft border-success/30" : "bg-warn-soft/40 border-warn/30")}>
              <div className={cn("text-[9px] uppercase tracking-wider font-bold", invoice.balance === 0 ? "text-success" : "text-warn")}>Balance due</div>
              <div className={cn("font-serif text-xl mt-0.5", invoice.balance === 0 ? "text-success" : "text-warn")}>
                {symI(invoice.currency)}{invoice.balance.toLocaleString()}
              </div>
              {invoice.balance > 0 && (
                <div className="text-[10px] text-ink-mute mt-0.5">
                  {invoice.daysPastDue < 0 ? `due in ${Math.abs(invoice.daysPastDue)}d` : invoice.daysPastDue === 0 ? "due today" : `${invoice.daysPastDue}d past due`}
                </div>
              )}
            </div>
          </div>
        </section>

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Payment history</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
            {invoice.status === "paid" && (
              <PayRow when={invoice.due} amount={invoice.amount} currency={invoice.currency} ref={`Bank match · ${invoice.id}-P01`} />
            )}
            {invoice.status === "partial" && (
              <PayRow when="Apr 02, 2026" amount={invoice.paid} currency={invoice.currency} ref={`Wire transfer · ${invoice.id}-P01`} />
            )}
            {invoice.balance > 0 && (
              <div className="px-3 py-2.5 text-ink-mute italic">Outstanding · payment expected by {invoice.due}</div>
            )}
          </div>
        </section>

        {invoice.ref && (
          <section className="mt-5">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Linked records</div>
            <div className="bg-surface border border-divider rounded-md text-[12px]">
              <button onClick={() => { onClose(); push("/procurement/po") }} className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-cream/40">
                <div className="flex items-center gap-2">
                  <Icon name="doc" size={12} className="text-ink-mute" />
                  <span className="text-ink-soft">Reference PO</span>
                  <span className="font-mono font-semibold text-brand-mid">{invoice.ref}</span>
                </div>
                <Icon name="arrow-right" size={12} className="text-ink-faint" />
              </button>
            </div>
          </section>
        )}
      </SheetBody>

      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="ghost" leadingIcon="printer" className="ml-auto">PDF</Button>
        <Button variant="ghost" leadingIcon="mail">Send reminder</Button>
        {invoice.balance > 0 && <Button variant="primary" leadingIcon="check">Record payment</Button>}
      </SheetFooter>
    </Sheet>
  )
}

function PayRow({ when, amount, currency, ref }: any) {
  return (
    <div className="px-3 py-2.5 flex items-center gap-2">
      <span className="font-mono text-[10px] text-ink-faint w-20">{when}</span>
      <span className="text-ink-soft truncate flex-1">{ref}</span>
      <span className="font-mono font-semibold text-success">{symI(currency)}{amount.toLocaleString()}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/activities.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/sales/activities.tsx
 * CRM — Activities: calls, emails, meetings, notes.
 */

function CRMActivities() {
  const acts = [
    { id: "AC-9012", type: "call",    summary: "Discovery call · Pacific Steel Co.",  rep: "Maya C.",     initials: "MC", account: "Pacific Steel Co.",   when: "Today 14:30", duration: "45m", outcome: "Demo scheduled" },
    { id: "AC-9011", type: "email",   summary: "Follow-up on quote QT-0418",          rep: "Maya C.",     initials: "MC", account: "Calamba Devt Corp.",  when: "Today 13:18", duration: "—",   outcome: "Awaiting response" },
    { id: "AC-9010", type: "meeting", summary: "Steel supply contract negotiation",   rep: "Daniel R.",  initials: "DR", account: "Pacific Steel Co.",   when: "Today 11:00", duration: "1h",  outcome: "Counter-proposal sent" },
    { id: "AC-9009", type: "call",    summary: "Renewal discussion · Crown Holdings",rep: "Mason P.",   initials: "MP", account: "Crown Holdings",      when: "Today 10:14", duration: "30m", outcome: "Verbal commit" },
    { id: "AC-9008", type: "note",    summary: "BDM lead from trade fair · Q-rated",  rep: "Kenneth T.", initials: "KT", account: "Greenbox Industries", when: "Yesterday",   duration: "—",   outcome: "Needs follow-up" },
    { id: "AC-9007", type: "meeting", summary: "Pricing review · Tan Beng",          rep: "Kenneth T.", initials: "KT", account: "Tan Beng Holdings",   when: "Yesterday",   duration: "45m", outcome: "Approved" },
    { id: "AC-9006", type: "email",   summary: "RFP submission · Bengaluru tech",     rep: "Priya I.",   initials: "PI", account: "Stellar Tech Ltd.",   when: "Yesterday",   duration: "—",   outcome: "Submitted" },
    { id: "AC-9005", type: "call",    summary: "Site visit recap · Sydney corridor",  rep: "Mia H.",     initials: "MH", account: "Westfield Group",     when: "Yesterday",   duration: "20m", outcome: "Quote in progress" },
  ]
  const [typeFilter, setTypeFilter] = React.useState<string>("all")
  let filtered = typeFilter === "all" ? acts : acts.filter(a => a.type === typeFilter)

  return (
    <>
      <TopBar breadcrumb={[{ label: "CRM" }, { label: "Activities" }]} />
      <ActionBar
        title="Activities"
        status={`${acts.length} activities this week · ${acts.filter(a => a.type === "call").length} calls · ${acts.filter(a => a.type === "meeting").length} meetings`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">Log activity</Button>}
        secondary={<Button variant="ghost" leadingIcon="phone" size="sm">Click-to-call</Button>}
        filters={
          <>
            <FilterChip active={typeFilter === "all"} onClick={() => setTypeFilter("all")}>All</FilterChip>
            <FilterChip active={typeFilter === "call"} onClick={() => setTypeFilter("call")}>Calls</FilterChip>
            <FilterChip active={typeFilter === "email"} onClick={() => setTypeFilter("email")}>Emails</FilterChip>
            <FilterChip active={typeFilter === "meeting"} onClick={() => setTypeFilter("meeting")}>Meetings</FilterChip>
            <FilterChip active={typeFilter === "note"} onClick={() => setTypeFilter("note")}>Notes</FilterChip>
          </>
        }
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1180px] mx-auto p-6">
          <Card>
            {filtered.map(a => (
              <div key={a.id} className="px-4 py-3 flex items-center gap-3 border-b border-divider-soft last:border-b-0 hover:bg-cream/40">
                <div className={cn(
                  "size-9 rounded-md inline-flex items-center justify-center shrink-0",
                  a.type === "call"    ? "bg-info-soft text-info" :
                  a.type === "email"   ? "bg-accent-soft text-accent-deep" :
                  a.type === "meeting" ? "bg-success-soft text-success" :
                                          "bg-cream-deep text-ink-mute"
                )}>
                  <Icon name={a.type === "call" ? "phone" : a.type === "email" ? "mail" : a.type === "meeting" ? "video" : "edit"} size={15} />
                </div>
                <div className="flex-1 min-w-0">
                  <div className="text-[12.5px] text-ink font-semibold">{a.summary}</div>
                  <div className="text-[10.5px] text-ink-mute mt-0.5">{a.account} · {a.outcome}</div>
                </div>
                <div className="flex items-center gap-1.5">
                  <Avatar initials={a.initials} tone="sand" size="xs" />
                  <span className="text-[10.5px] text-ink-mute">{a.rep}</span>
                </div>
                <span className="text-[10.5px] font-mono text-ink-mute w-20 text-right">{a.duration}</span>
                <span className="text-[11px] text-ink-soft w-24 text-right">{a.when}</span>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/sales/sales-orders.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/sales/sales-orders.tsx
 * CRM — Sales Orders (the upstream of invoices). Each SO has line items
 * and routes to fulfillment + billing.
 */

function SalesOrders() {
  const sos = [
    { id: "SO-HVPH-0312", customer: "Pacific Steel Co.",      amount: 4_852_000, ccy: "PHP",  status: "fulfilling", date: "Today",        owner: "Maya C.",      entity: "HVPH", items: 4 },
    { id: "SO-HVPH-0310", customer: "Calamba Devt Corp.",     amount: 1_240_000, ccy: "PHP",  status: "approved",   date: "Today",        owner: "Maya C.",      entity: "HVPH", items: 8 },
    { id: "SO-HVPH-0306", customer: "Pacific Steel Co.",      amount:   245_000, ccy: "PHP",  status: "delivered",  date: "Yesterday",    owner: "Maya C.",      entity: "HVPH", items: 2 },
    { id: "SO-HVSG-0298", customer: "Tan Beng Holdings",      amount:    88_500, ccy: "SGD",  status: "delivered",  date: "Yesterday",    owner: "Kenneth T.",  entity: "HVSG", items: 6 },
    { id: "SO-HVSG-0294", customer: "DBS Bank",               amount:    14_200, ccy: "SGD",  status: "invoiced",   date: "2d ago",       owner: "Kenneth T.",  entity: "HVSG", items: 1 },
    { id: "SO-HVAU-0312", customer: "Crown Holdings",         amount:   248_500, ccy: "AUD",  status: "approval",   date: "Today",        owner: "Mason P.",    entity: "HVAU", items: 12 },
    { id: "SO-HVAU-0310", customer: "Westfield Group",        amount:    82_400, ccy: "AUD",  status: "fulfilling", date: "Yesterday",    owner: "Mason P.",    entity: "HVAU", items: 8 },
    { id: "SO-HVIN-0288", customer: "Devanand Logistics",     amount:    64_400, ccy: "INR",  status: "fulfilling", date: "Today",        owner: "Priya I.",    entity: "HVIN", items: 2 },
    { id: "SO-HVIN-0282", customer: "Stellar Tech Ltd.",      amount:    28_800, ccy: "INR",  status: "draft",      date: "Today",        owner: "Priya I.",    entity: "HVIN", items: 4 },
    { id: "SO-HVHK-0214", customer: "Yuen Long Logistics",    amount:   156_400, ccy: "HKD",  status: "fulfilling", date: "Yesterday",    owner: "Eric W.",     entity: "HVHK", items: 4 },
  ]
  function ccyPrefix(c: string) { return c === "PHP" ? "₱" : c === "SGD" ? "S$" : c === "HKD" ? "HK$" : c === "AUD" ? "A$" : c === "INR" ? "₹" : "$" }
  return (
    <>
      <TopBar breadcrumb={[{ label: "CRM" }, { label: "Sales Orders" }]} />
      <ActionBar
        title="Sales Orders"
        status={`${sos.length} orders this week · ${sos.filter(s => s.status === "fulfilling").length} fulfilling · ${sos.filter(s => s.status === "approval").length} pending approval`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New SO</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Margin check</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1280px] mx-auto p-6">
          <Card>
            <div className="grid grid-cols-[150px_2fr_120px_120px_140px_120px_80px_80px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
              <span>SO #</span><span>Customer</span><span>Entity</span><span className="text-right">Items / Amount</span><span>Owner</span><span>Status</span><span>Date</span><span></span>
            </div>
            {sos.map(s => (
              <div key={s.id} className="grid grid-cols-[150px_2fr_120px_120px_140px_120px_80px_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-brand-mid">{s.id}</span>
                <span className="text-ink font-medium">{s.customer}</span>
                <span className="font-mono text-ink-mute">{s.entity}</span>
                <span className="text-right">
                  <div className="font-mono">{s.items} item{s.items > 1 ? "s" : ""}</div>
                  <div className="font-mono text-[10.5px] font-bold text-ink">{ccyPrefix(s.ccy)} {(s.amount).toLocaleString()}</div>
                </span>
                <div className="flex items-center gap-1.5"><Avatar initials={s.owner.split(" ").map(x => x[0]).join("").slice(0, 2)} tone="sand" size="xs" />{s.owner}</div>
                {s.status === "fulfilling" ? <Badge variant="info" dot>Fulfilling</Badge>
                : s.status === "delivered"  ? <Badge variant="success">Delivered</Badge>
                : s.status === "invoiced"   ? <Badge variant="success">✓ Invoiced</Badge>
                : s.status === "approved"   ? <Badge variant="info">Approved</Badge>
                : s.status === "approval"   ? <Badge variant="warn" dot>Approval</Badge>
                                            : <Badge variant="neutral">Draft</Badge>}
                <span className="text-ink-soft">{s.date}</span>
                <Button variant="ghost" size="sm">Open</Button>
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/work-orders.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/manufacturing/work-orders.tsx
 * ----------------------------------------------------------------------------
 * Manufacturing hero — Work Orders dashboard. Shop-floor view: KPIs across
 * top, status filter chips, then a list of active work orders with progress
 * bars and shortage warnings.
 * ============================================================================
 */

function WorkOrders() {
  const { push } = useRouter()
  const [statusFilter, setStatusFilter] = React.useState<"active" | "all" | WorkOrder["status"]>("active")
  const [stationFilter, setStationFilter] = React.useState<string | null>(null)

  const stations = Array.from(new Set(WORK_ORDERS.filter(w => w.station !== "—").map(w => w.station.split(" · ")[0])))

  const counts: Record<string, number> = { all: WORK_ORDERS.length, active: 0 }
  for (const w of WORK_ORDERS) {
    counts[w.status] = (counts[w.status] ?? 0) + 1
    if (w.status !== "done") counts.active++
  }

  const rows = React.useMemo(() => {
    let r = WORK_ORDERS
    if (statusFilter === "active") r = r.filter(w => w.status !== "done")
    else if (statusFilter !== "all") r = r.filter(w => w.status === statusFilter)
    if (stationFilter) r = r.filter(w => w.station.startsWith(stationFilter))
    return r
  }, [statusFilter, stationFilter])

  const blocked = WORK_ORDERS.filter(w => w.status === "blocked")
  const inProgress = WORK_ORDERS.filter(w => w.status === "in-progress")
  const totalUnitsToday = inProgress.reduce((s, w) => s + Math.round(w.qty * (w.completionPct / 100)), 0)
  const totalUnitsScheduled = inProgress.reduce((s, w) => s + w.qty, 0)
  const onTimePct = 92

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Manufacturing" }, { label: "Work Orders" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="grid">Shop-floor view</Button>
            <Button variant="primary" leadingIcon="plus">New Work Order</Button>
          </>
        }
      />

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

          {/* KPI strip */}
          <div className="grid grid-cols-5 gap-3">
            <KpiTile label="Active WOs" value={counts.active.toString()} sub={`${counts["in-progress"] ?? 0} in progress`} accent="brand" />
            <KpiTile label="Units today" value={`${totalUnitsToday} / ${totalUnitsScheduled}`} sub="Across 4 bays" accent="info" />
            <KpiTile label="On-time" value={`${onTimePct}%`} sub="YTD" accent="success" deltaDir="up" delta="+3pp" />
            <KpiTile label="Blocked" value={blocked.length.toString()} sub={blocked.length ? "Material shortages" : "All clear"} accent={blocked.length ? "danger" : "success"} />
            <KpiTile label="QC pending" value={(counts.qc ?? 0).toString()} sub="Inspection queue" accent="warn" />
          </div>

          {/* AI shortage alert */}
          {blocked.length > 0 && (
            <div className="bg-danger-soft/60 border border-danger/30 rounded-lg p-4 flex items-start gap-3">
              <div className="size-8 rounded-full bg-danger/15 text-danger flex items-center justify-center shrink-0">
                <Icon name="alert-triangle" size={14} />
              </div>
              <div className="flex-1">
                <div className="flex items-center gap-2 mb-0.5">
                  <span className="text-[10px] uppercase tracking-wider font-bold text-danger">Material Shortage · Cenora AI</span>
                </div>
                <div className="text-[13px] text-ink leading-snug">
                  <strong>{blocked.length} work order{blocked.length > 1 ? "s" : ""} blocked.</strong>{" "}
                  WO-0139 needs Rebar 25mm (28 PC short) and Sealant 5L (12 short).
                  Suggested action: <strong>auto-PR</strong> from preferred vendor SteelMark — lead time 3 days, fits the May 12 deadline.
                </div>
              </div>
              <Button variant="primary" size="sm" leadingIcon="sparkle">Create auto-PR</Button>
            </div>
          )}

          {/* Filters */}
          <div className="flex items-center gap-1.5 flex-wrap">
            <FilterChip active={statusFilter === "active"} onClick={() => setStatusFilter("active")} count={counts.active}>Active</FilterChip>
            <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")} count={counts.all}>All</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            {(["in-progress", "qc", "blocked", "released", "planned", "done"] as const).map(s => {
              const c = counts[s] ?? 0
              if (c === 0) return null
              return (
                <FilterChip key={s} active={statusFilter === s} onClick={() => setStatusFilter(s)} count={c} tone={WO_STATUS_TONE[s] as any}>
                  {WO_STATUS_LABEL[s]}
                </FilterChip>
              )
            })}
            <span className="w-px h-5 bg-divider mx-1" />
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Station:</span>
            <FilterChip active={stationFilter === null} onClick={() => setStationFilter(null)}>Any</FilterChip>
            {stations.map(s => (
              <FilterChip key={s} active={stationFilter === s} onClick={() => setStationFilter(s)}>{s}</FilterChip>
            ))}
          </div>

          {/* Work order rows */}
          <div className="space-y-2">
            {rows.map(w => <WORow key={w.id} w={w} push={push} />)}
            {rows.length === 0 && (
              <div className="bg-surface border border-divider rounded-lg p-12 text-center text-ink-mute text-[12px]">
                No work orders match these filters.
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  )
}

/* ─── Work Order row ──────────────────────────────────────────────────── */
function WORow({ w, push }: { w: WorkOrder; push: (r: string) => void }) {
  const priorityBadge = w.priority === "expedited" ? <Badge variant="danger">EXPEDITED</Badge> : w.priority === "rush" ? <Badge variant="warn">RUSH</Badge> : null
  return (
    <div className={cn(
      "bg-surface border rounded-lg p-4 transition-shadow hover:shadow-md",
      w.status === "blocked" ? "border-danger/50 bg-danger-soft/20" : "border-divider"
    )}>
      <div className="flex items-start gap-4">
        {/* WO id badge */}
        <div className="shrink-0 w-24">
          <Badge variant={WO_STATUS_TONE[w.status] as any} dot>{WO_STATUS_LABEL[w.status]}</Badge>
          <div className="font-mono text-[10px] text-ink-mute mt-1">{w.id}</div>
        </div>

        {/* Main */}
        <div className="flex-1 min-w-0">
          <div className="flex items-baseline justify-between gap-3 flex-wrap">
            <div className="flex items-baseline gap-2 flex-wrap min-w-0">
              <h3 className="font-semibold text-ink text-[14px] leading-snug">{w.product.name}</h3>
              <span className="font-mono text-[10px] text-ink-faint">{w.product.code}</span>
              {priorityBadge}
            </div>
            <div className="font-mono text-[12px] text-ink-soft shrink-0">
              <strong className="text-ink">{w.qty}</strong> {w.uom}
            </div>
          </div>

          <div className="flex items-center gap-4 mt-1.5 text-[11px] text-ink-mute flex-wrap">
            <span className="inline-flex items-center gap-1.5">
              <Icon name="building" size={11} /> {w.station}
            </span>
            <span>·</span>
            {w.operator.initials ? (
              <span className="inline-flex items-center gap-1.5">
                <Avatar initials={w.operator.initials} tone={w.operator.tone as any} size="xs" /> {w.operator.name}
              </span>
            ) : (
              <span className="text-ink-faint italic">Unassigned</span>
            )}
            <span>·</span>
            <span>Due <strong className="text-ink-soft">{w.due}</strong></span>
            {w.customer && (<><span>·</span><span>{w.customer}</span></>)}
          </div>

          {/* Progress bar */}
          {w.status !== "planned" && w.status !== "released" && (
            <div className="mt-3">
              <div className="flex items-baseline justify-between text-[10px] mb-1">
                <span className="text-ink-mute font-mono">{w.completionPct}% complete</span>
                <span className="text-ink-faint">Started {w.startedAt}</span>
              </div>
              <div className="h-1.5 bg-cream-deep rounded-full overflow-hidden">
                <div className={cn("h-full rounded-full",
                  w.status === "blocked" ? "bg-danger" : w.status === "done" ? "bg-success" : w.status === "qc" ? "bg-warn" : "bg-brand-mid"
                )} style={{ width: `${w.completionPct}%` }} />
              </div>
            </div>
          )}

          {/* Shortage alert */}
          {w.bomShortages && w.bomShortages.length > 0 && (
            <div className="mt-3 flex items-center gap-1.5 bg-danger-soft/60 text-danger text-[11px] px-2 py-1.5 rounded-md">
              <Icon name="alert-triangle" size={11} />
              <span><strong>Material shortage:</strong> {w.bomShortages.length} component{w.bomShortages.length > 1 ? "s" : ""} ({w.bomShortages.join(", ")})</span>
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/bom.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/manufacturing/bom.tsx
 * ----------------------------------------------------------------------------
 * Manufacturing — Bill of Materials list with multi-level component tree.
 *
 * Reuses BOMS from lib/mock/manufacturing.tsx and expands per-BOM components
 * inline (sample data) since BOM tree shape is specific to this screen.
 * ============================================================================
 */

interface BomComp {
  sku: string
  name: string
  qty: number
  uom: string
  unitCost: number
  scrap: number   /* % */
  children?: BomComp[]
}

const BOM_TREE: Record<string, BomComp[]> = {
  "BOM-FAB-PCS-200-v4": [
    { sku: "02010102-014", name: "Steel Rebar 16mm × 12m Grade 60",  qty: 8,    uom: "PC",  unitCost: 92,   scrap: 2 },
    { sku: "01030102-010", name: "Portland Cement 40kg Type 1",       qty: 6,    uom: "BAG", unitCost: 290,  scrap: 3 },
    { sku: "07030101-014", name: "Waterproof Sealant 5L Grey",        qty: 0.5,  uom: "PC",  unitCost: 920,  scrap: 5 },
    { sku: "FAB-PRC-MOLD", name: "Casting mold · 200mm slab",         qty: 1,    uom: "USE", unitCost: 800,  scrap: 0,  children: [
      { sku: "RAW-STL-PLATE", name: "Steel plate stock (mold consumable)", qty: 1, uom: "EA", unitCost: 800, scrap: 0 }
    ]},
    { sku: "RAW-AGG-FINE",  name: "Aggregate · fine (consumable)",     qty: 30,   uom: "KG",  unitCost: 6,    scrap: 4 },
    { sku: "RAW-AGG-COARSE",name: "Aggregate · coarse",                 qty: 45,   uom: "KG",  unitCost: 8,    scrap: 4 },
    { sku: "LAB-CSTR",      name: "Labor · casting team",                qty: 1.5,  uom: "HR",  unitCost: 380,  scrap: 0 },
    { sku: "LAB-QC",        name: "Labor · QC inspection",                qty: 0.4,  uom: "HR",  unitCost: 540,  scrap: 0 },
  ],
  "BOM-FAB-IBM-W8-v2": [
    { sku: "RAW-STL-IBEAM", name: "I-Beam blank W8×31",                qty: 1,    uom: "PC",  unitCost: 1400, scrap: 1 },
    { sku: "LAB-CUT",       name: "Labor · cutting station",            qty: 0.5,  uom: "HR",  unitCost: 380,  scrap: 0 },
    { sku: "RAW-CUT-CONS",  name: "Cutting consumables · blade wear",  qty: 1,    uom: "EA",  unitCost: 70,   scrap: 0 },
  ],
  "BOM-FAB-WLD-FRA-v3": [
    { sku: "FAB-IBM-W8",    name: "I-Beam W8×31 pre-cut to 12m",        qty: 3,    uom: "PC",  unitCost: 1850, scrap: 1, children: [
      { sku: "RAW-STL-IBEAM", name: "I-Beam blank (drawn from sub-assembly)", qty: 1, uom: "PC", unitCost: 1400, scrap: 1 }
    ]},
    { sku: "02010102-014",  name: "Steel Rebar 16mm × 12m Grade 60",     qty: 4,    uom: "PC",  unitCost: 92,   scrap: 2 },
    { sku: "07030101-014",  name: "Waterproof Sealant 5L Grey",          qty: 0.2,  uom: "PC",  unitCost: 920,  scrap: 0 },
    { sku: "RAW-WELD-WIRE", name: "MIG welding wire · ER70S-6",          qty: 2,    uom: "KG",  unitCost: 480,  scrap: 8 },
    { sku: "RAW-GAS-CO2",   name: "Welding shielding gas · CO2",         qty: 1,    uom: "TANK",unitCost: 1200, scrap: 5 },
    { sku: "LAB-WELD",      name: "Labor · welding team",                 qty: 4,    uom: "HR",  unitCost: 520,  scrap: 0 },
    { sku: "LAB-QC",        name: "Labor · weld QC / dye-penetrant",      qty: 1,    uom: "HR",  unitCost: 540,  scrap: 0 },
  ],
  "BOM-FAB-PCS-150-v3": [
    { sku: "02010102-014", name: "Steel Rebar 16mm × 12m Grade 60",  qty: 6,    uom: "PC",  unitCost: 92,   scrap: 2 },
    { sku: "01030102-010", name: "Portland Cement 40kg Type 1",       qty: 4.5,  uom: "BAG", unitCost: 290,  scrap: 3 },
    { sku: "07030101-014", name: "Waterproof Sealant 5L Grey",        qty: 0.4,  uom: "PC",  unitCost: 920,  scrap: 5 },
    { sku: "RAW-AGG-FINE", name: "Aggregate · fine (consumable)",     qty: 24,   uom: "KG",  unitCost: 6,    scrap: 4 },
    { sku: "RAW-AGG-COARSE",name:"Aggregate · coarse",                 qty: 36,   uom: "KG",  unitCost: 8,    scrap: 4 },
    { sku: "LAB-CSTR",     name: "Labor · casting team",                qty: 1.2,  uom: "HR",  unitCost: 380,  scrap: 0 },
  ],
  "BOM-FAB-WLD-FRA-v4": [
    { sku: "FAB-IBM-W8",    name: "I-Beam W8×31 pre-cut to 12m",        qty: 3,    uom: "PC",  unitCost: 1850, scrap: 1 },
    { sku: "02010102-014",  name: "Steel Rebar 16mm × 12m Grade 60",     qty: 4,    uom: "PC",  unitCost: 92,   scrap: 2 },
    { sku: "07030101-018",  name: "Waterproof Sealant Premium 5L",       qty: 0.3,  uom: "PC",  unitCost: 1180, scrap: 0 },
    { sku: "RAW-WELD-WIRE", name: "MIG welding wire · ER70S-6",          qty: 2.4,  uom: "KG",  unitCost: 480,  scrap: 8 },
    { sku: "RAW-GAS-CO2",   name: "Welding shielding gas · CO2",         qty: 1,    uom: "TANK",unitCost: 1200, scrap: 5 },
    { sku: "LAB-WELD",      name: "Labor · welding team (upskilled)",     qty: 4.5,  uom: "HR",  unitCost: 560,  scrap: 0 },
    { sku: "LAB-QC",        name: "Labor · weld QC / dye-penetrant",      qty: 1.2,  uom: "HR",  unitCost: 540,  scrap: 0 },
  ],
}

function BillOfMaterials() {
  const [status, setStatus] = React.useState<"all" | "active" | "draft" | "deprecated">("active")
  const [q, setQ] = React.useState("")
  const [selected, setSelected] = React.useState<any | null>(null)

  const filtered = React.useMemo(() => {
    let r = BOMS
    if (status !== "all") r = r.filter(b => b.status === status)
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(b => b.product.name.toLowerCase().includes(s) || b.product.code.toLowerCase().includes(s) || b.id.toLowerCase().includes(s))
    return r
  }, [status, q])

  const active = BOMS.filter(b => b.status === "active")
  const totalRolled = active.reduce((s, b) => s + b.totalCost, 0)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Manufacturing" }, { label: "Bill of Materials" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="upload">Import CSV</Button>
            <Button variant="primary" leadingIcon="plus">New BOM</Button>
          </>
        }
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 space-y-4">

          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Active BOMs"        value={active.length}                                                  sub={`across ${new Set(active.map(b => b.product.code)).size} products`} accent="brand" />
            <KpiTile label="Pending review"     value={BOMS.filter(b => b.status === "draft").length}                  sub="drafted, awaiting eng approval" accent="warn" />
            <KpiTile label="Rolled-up cost"     value={`₱${(totalRolled / 1000).toFixed(0)}k`}                          sub="standard cost · sum"            accent="accent" />
            <KpiTile label="Components reused"  value="62"                                                              sub="across all BOMs"                 accent="success" />
          </div>

          <AICallout
            title="BOM-FAB-WLD-FRA-v4 ready to publish"
            body="The v4 draft uses Sealant Premium (5L) instead of Grey + upskilled welder rate. Standard cost rises ₱800 (+4.3%) but the spec is required by the Filinvest order (FJ-2026-0418). Promoting it will replace v3 on WO-2026-0139 and WO-2026-0141."
            matches={[
              { code: "BOM-FAB-WLD-FRA-v4", name: "Welded Frame Type A · draft → active", pct: 100 },
            ]}
            dismissLabel="Keep as draft"
            proceedLabel="Publish v4"
          />

          <div className="flex items-center gap-2 flex-wrap">
            <SearchBox value={q} onChange={setQ} placeholder="Search BOMs, product codes…" className="max-w-[280px]" />
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={status === "all"}        onClick={() => setStatus("all")}>All</FilterChip>
            <FilterChip active={status === "active"}     onClick={() => setStatus("active")} tone="success">Active</FilterChip>
            <FilterChip active={status === "draft"}      onClick={() => setStatus("draft")} tone="warn">Draft</FilterChip>
            <FilterChip active={status === "deprecated"} onClick={() => setStatus("deprecated")}>Deprecated</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {BOMS.length}</span>
          </div>

          <Card>
            <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">BOM</th>
                  <th>Product</th>
                  <th>Version</th>
                  <th className="text-right">Components</th>
                  <th className="text-right">Standard cost</th>
                  <th>Last changed</th>
                  <th>Status</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(b => (
                  <tr key={b.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(b)}>
                    <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{b.id}</td>
                    <td>
                      <div className="font-semibold text-ink">{b.product.name}</div>
                      <div className="font-mono text-[10px] text-ink-mute">{b.product.code}</div>
                    </td>
                    <td><Badge variant="neutral">{b.version}</Badge></td>
                    <td className="text-right font-mono text-ink-soft">{b.components}</td>
                    <td className="text-right font-mono font-semibold text-ink">₱{b.totalCost.toLocaleString()}</td>
                    <td>
                      <div className="text-[11px] text-ink-soft">{b.lastChanged}</div>
                      <div className="text-[10px] text-ink-mute">by {b.changedBy}</div>
                    </td>
                    <td><Badge variant={b.status === "active" ? "success" : b.status === "draft" ? "warn" : "neutral"} dot>{b.status === "active" ? "Active" : b.status === "draft" ? "Draft" : "Deprecated"}</Badge></td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </div>
      </div>

      {selected && <BomSheet bom={selected} onClose={() => setSelected(null)} />}
    </>
  )
}

function BomSheet({ bom, onClose }: { bom: any; onClose: () => void }) {
  const tree = BOM_TREE[bom.id] ?? []
  const totalScrap = tree.reduce((s, c) => s + c.qty * c.unitCost * (c.scrap / 100), 0)
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div>
          <div className="font-mono text-[10px] text-ink-mute">{bom.id}</div>
          <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{bom.product.name}</h2>
          <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
            <span className="font-mono">{bom.product.code}</span>
            <span>·</span>
            <Badge variant={bom.status === "active" ? "success" : bom.status === "draft" ? "warn" : "neutral"} dot>{bom.version} · {bom.status === "active" ? "Active" : bom.status === "draft" ? "Draft" : "Deprecated"}</Badge>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>
      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Components</div>
              <div className="font-serif text-xl text-ink mt-0.5">{bom.components}</div>
              <div className="text-[10px] text-ink-mute mt-0.5">incl. labor + scrap</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Standard cost</div>
              <div className="font-serif text-xl text-ink mt-0.5">₱{bom.totalCost.toLocaleString()}</div>
              <div className="text-[10px] text-ink-mute mt-0.5">rolled-up</div>
            </div>
            <div className="bg-warn-soft/40 border border-warn/30 rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-warn">Expected scrap</div>
              <div className="font-serif text-xl text-warn mt-0.5">₱{Math.round(totalScrap).toLocaleString()}</div>
              <div className="text-[10px] text-ink-mute mt-0.5">{((totalScrap / bom.totalCost) * 100).toFixed(1)}% of std cost</div>
            </div>
          </div>
        </section>

        <section className="mt-5">
          <div className="flex items-center justify-between mb-2">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Component tree</div>
            <Button variant="ghost" size="sm" leadingIcon="copy">Clone as v{parseInt(bom.version.slice(1), 10) + 1}</Button>
          </div>
          <div className="bg-surface border border-divider rounded-md overflow-hidden">
            <div className="grid grid-cols-[1.4fr_50px_60px_60px_50px_80px] gap-2 px-3 py-2 bg-cream text-[9.5px] font-bold uppercase tracking-wider text-ink-mute border-b border-divider">
              <div>SKU / description</div>
              <div className="text-right">Qty</div>
              <div className="text-right">UoM</div>
              <div className="text-right">Unit</div>
              <div className="text-right">Scrap</div>
              <div className="text-right">Line</div>
            </div>
            {tree.map(c => <BomRow key={c.sku} c={c} depth={0} />)}
          </div>
        </section>

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Change history</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[11.5px]">
            <BomHistRow when={bom.lastChanged} who={bom.changedBy} what="Adjusted welding wire scrap from 6% → 8%" />
            <BomHistRow when="Mar 30"           who="Pedro S."       what="Added Sealant Premium 5L as substitute" />
            <BomHistRow when="Feb 14"           who="Warren C."      what="Initial release · approved by Eng" />
          </div>
        </section>
      </SheetBody>
      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="ghost" leadingIcon="printer" className="ml-auto">PDF</Button>
        {bom.status === "draft" && <Button variant="primary" leadingIcon="check">Publish version</Button>}
        {bom.status === "active" && <Button variant="ghost" leadingIcon="edit">Start new version</Button>}
      </SheetFooter>
    </Sheet>
  )
}

function BomRow({ c, depth }: { c: BomComp; depth: number }) {
  const [open, setOpen] = React.useState(depth === 0)
  const hasChildren = !!c.children?.length
  const line = c.qty * c.unitCost * (1 + c.scrap / 100)
  return (
    <>
      <div className="grid grid-cols-[1.4fr_50px_60px_60px_50px_80px] gap-2 px-3 py-2 border-t border-divider-soft text-[11.5px] items-center"
        style={{ paddingLeft: 12 + depth * 16 }}>
        <div className="flex items-center gap-1.5 min-w-0">
          {hasChildren ? (
            <button onClick={() => setOpen(!open)} className="size-4 inline-flex items-center justify-center text-ink-mute hover:text-ink">
              <Icon name={open ? "chevron-down" : "chevron-right"} size={10} />
            </button>
          ) : <span className="size-4" />}
          <div className="min-w-0">
            <div className="font-mono font-semibold text-brand-mid text-[10.5px]">{c.sku}</div>
            <div className="text-ink-soft truncate">{c.name}</div>
          </div>
        </div>
        <div className="text-right font-mono text-ink">{c.qty}</div>
        <div className="text-right font-mono text-ink-mute text-[10px]">{c.uom}</div>
        <div className="text-right font-mono text-ink-soft">₱{c.unitCost.toLocaleString()}</div>
        <div className="text-right font-mono text-warn text-[10.5px]">{c.scrap > 0 ? `${c.scrap}%` : "—"}</div>
        <div className="text-right font-mono font-semibold text-ink">₱{Math.round(line).toLocaleString()}</div>
      </div>
      {hasChildren && open && c.children!.map(ch => <BomRow key={ch.sku} c={ch} depth={depth + 1} />)}
    </>
  )
}
function BomHistRow({ when, who, what }: any) {
  return (
    <div className="px-3 py-2 flex items-baseline gap-2">
      <span className="font-mono text-[10px] text-ink-faint w-12 shrink-0">{when}</span>
      <span className="text-ink-soft flex-1">{what}</span>
      <span className="text-[10px] text-ink-mute">{who}</span>
    </div>
  )
}

// SANDBOX
;(globalThis as any).BillOfMaterials = BillOfMaterials
;(globalThis as any).BOM_TREE = BOM_TREE

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/routings.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/manufacturing/routings.tsx
 * ----------------------------------------------------------------------------
 * Manufacturing — Routings (shop-floor operation sequences). Each routing is
 * a series of operations on work centers with cycle times, setup, scrap %.
 *
 * Hero view: routing list, click → operation sequence + Gantt-ish strip.
 * ============================================================================
 */

interface RoutingOp {
  seq: number
  name: string
  workCenter: string
  setupMin: number
  cycleMin: number
  laborGrade: "L1 (Operator)" | "L2 (Skilled)" | "L3 (Senior)" | "QC"
  scrapPct: number
  notes?: string
}
interface Routing {
  id: string
  product: { code: string; name: string }
  version: string
  status: "active" | "draft"
  totalSetupMin: number
  totalCycleMin: number
  ops: RoutingOp[]
  lastChanged: string
  changedBy: string
}

const ROUTINGS: Routing[] = [
  { id: "RTG-FAB-PCS-200-v3", product: { code: "FAB-PCS-200", name: "Precast Hollow Core Slab 200mm" }, version: "v3", status: "active",
    totalSetupMin: 90, totalCycleMin: 220, lastChanged: "Apr 11", changedBy: "Warren C.",
    ops: [
      { seq: 10, name: "Mold prep + grease",       workCenter: "Bay 3 · Casting",    setupMin: 25, cycleMin: 18, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 20, name: "Rebar cage assembly",      workCenter: "Bay 3 · Casting",    setupMin: 12, cycleMin: 32, laborGrade: "L2 (Skilled)",  scrapPct: 1.5, notes: "Tied to BOM-FAB-PCS-200-v4" },
      { seq: 30, name: "Concrete pour + screed",   workCenter: "Bay 3 · Casting",    setupMin: 18, cycleMin: 42, laborGrade: "L2 (Skilled)",  scrapPct: 2.0 },
      { seq: 40, name: "Vibration + finish",       workCenter: "Bay 3 · Casting",    setupMin: 0,  cycleMin: 14, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 50, name: "Cure + strip mold",        workCenter: "Curing Yard",        setupMin: 0,  cycleMin: 96, laborGrade: "L1 (Operator)", scrapPct: 0,   notes: "Steam curing — 18hr min" },
      { seq: 60, name: "QC strength check",        workCenter: "QC Bay",             setupMin: 0,  cycleMin: 8,  laborGrade: "QC",            scrapPct: 0.5 },
      { seq: 70, name: "Surface seal + stamp",     workCenter: "Finishing",          setupMin: 5,  cycleMin: 6,  laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 80, name: "Stage for shipping",       workCenter: "Shipping Bay",       setupMin: 0,  cycleMin: 4,  laborGrade: "L1 (Operator)", scrapPct: 0 },
    ] },
  { id: "RTG-FAB-IBM-W8-v2", product: { code: "FAB-IBM-W8", name: "I-Beam W8×31 — pre-cut" }, version: "v2", status: "active",
    totalSetupMin: 22, totalCycleMin: 38, lastChanged: "Mar 22", changedBy: "Pedro S.",
    ops: [
      { seq: 10, name: "Stock pickup + ID stamp",  workCenter: "Bay 1 · Cutting",    setupMin: 8,  cycleMin: 6,  laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 20, name: "CNC plasma cut to length", workCenter: "Bay 1 · Cutting",    setupMin: 12, cycleMin: 22, laborGrade: "L2 (Skilled)",  scrapPct: 1.0, notes: "Blade wear flagged @ 14 cuts" },
      { seq: 30, name: "Edge deburr + clean",      workCenter: "Bay 1 · Cutting",    setupMin: 2,  cycleMin: 6,  laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 40, name: "QC straightness check",    workCenter: "QC Bay",             setupMin: 0,  cycleMin: 4,  laborGrade: "QC",            scrapPct: 0 },
    ] },
  { id: "RTG-FAB-WLD-FRA-v3", product: { code: "FAB-WLD-FRA", name: "Welded Frame Assembly Type A" }, version: "v3", status: "active",
    totalSetupMin: 75, totalCycleMin: 240, lastChanged: "Apr 04", changedBy: "Warren C.",
    ops: [
      { seq: 10, name: "Tack-weld jig setup",      workCenter: "Bay 2 · Welding",    setupMin: 25, cycleMin: 20, laborGrade: "L2 (Skilled)",  scrapPct: 0 },
      { seq: 20, name: "Tack welds × 8",           workCenter: "Bay 2 · Welding",    setupMin: 10, cycleMin: 28, laborGrade: "L2 (Skilled)",  scrapPct: 0.5 },
      { seq: 30, name: "Full seam MIG weld",       workCenter: "Bay 2 · Welding",    setupMin: 12, cycleMin: 95, laborGrade: "L3 (Senior)",   scrapPct: 1.5, notes: "ER70S-6 wire · CO₂ shielding" },
      { seq: 40, name: "Grind welds smooth",       workCenter: "Bay 2 · Welding",    setupMin: 8,  cycleMin: 36, laborGrade: "L2 (Skilled)",  scrapPct: 0.5 },
      { seq: 50, name: "Dye-penetrant QC",         workCenter: "QC Bay",             setupMin: 12, cycleMin: 18, laborGrade: "QC",            scrapPct: 2.0, notes: "Reject + repair if cracks" },
      { seq: 60, name: "Primer coat + cure",       workCenter: "Finishing",          setupMin: 8,  cycleMin: 32, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 70, name: "Stage for shipping",       workCenter: "Shipping Bay",       setupMin: 0,  cycleMin: 11, laborGrade: "L1 (Operator)", scrapPct: 0 },
    ] },
  { id: "RTG-FAB-WLD-FRA-v4", product: { code: "FAB-WLD-FRA", name: "Welded Frame Assembly Type A" }, version: "v4", status: "draft",
    totalSetupMin: 80, totalCycleMin: 232, lastChanged: "Apr 16", changedBy: "Warren C.",
    ops: [
      { seq: 10, name: "Tack-weld jig setup",      workCenter: "Bay 2 · Welding",    setupMin: 28, cycleMin: 18, laborGrade: "L2 (Skilled)",  scrapPct: 0 },
      { seq: 20, name: "Tack welds × 8",           workCenter: "Bay 2 · Welding",    setupMin: 10, cycleMin: 26, laborGrade: "L2 (Skilled)",  scrapPct: 0.4 },
      { seq: 30, name: "Robot-assisted seam weld", workCenter: "Bay 2 · Robot cell", setupMin: 14, cycleMin: 78, laborGrade: "L3 (Senior)",   scrapPct: 0.8, notes: "Welding Robot Cell · Bay 2" },
      { seq: 40, name: "Grind welds smooth",       workCenter: "Bay 2 · Welding",    setupMin: 8,  cycleMin: 32, laborGrade: "L2 (Skilled)",  scrapPct: 0.4 },
      { seq: 50, name: "Dye-penetrant QC",         workCenter: "QC Bay",             setupMin: 12, cycleMin: 18, laborGrade: "QC",            scrapPct: 1.5 },
      { seq: 60, name: "Premium primer + cure",    workCenter: "Finishing",          setupMin: 8,  cycleMin: 28, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 70, name: "Stage for shipping",       workCenter: "Shipping Bay",       setupMin: 0,  cycleMin: 12, laborGrade: "L1 (Operator)", scrapPct: 0 },
    ] },
  { id: "RTG-FAB-PCS-150-v3", product: { code: "FAB-PCS-150", name: "Precast Hollow Core Slab 150mm" }, version: "v3", status: "active",
    totalSetupMin: 76, totalCycleMin: 192, lastChanged: "Feb 14", changedBy: "Warren C.",
    ops: [
      { seq: 10, name: "Mold prep + grease",       workCenter: "Bay 3 · Casting",    setupMin: 22, cycleMin: 14, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 20, name: "Rebar cage assembly",      workCenter: "Bay 3 · Casting",    setupMin: 10, cycleMin: 26, laborGrade: "L2 (Skilled)",  scrapPct: 1.5 },
      { seq: 30, name: "Concrete pour + screed",   workCenter: "Bay 3 · Casting",    setupMin: 18, cycleMin: 36, laborGrade: "L2 (Skilled)",  scrapPct: 2.0 },
      { seq: 40, name: "Vibration + finish",       workCenter: "Bay 3 · Casting",    setupMin: 0,  cycleMin: 12, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 50, name: "Cure + strip mold",        workCenter: "Curing Yard",        setupMin: 0,  cycleMin: 82, laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 60, name: "QC strength check",        workCenter: "QC Bay",             setupMin: 0,  cycleMin: 8,  laborGrade: "QC",            scrapPct: 0.5 },
      { seq: 70, name: "Surface seal + stamp",     workCenter: "Finishing",          setupMin: 5,  cycleMin: 5,  laborGrade: "L1 (Operator)", scrapPct: 0 },
      { seq: 80, name: "Stage for shipping",       workCenter: "Shipping Bay",       setupMin: 0,  cycleMin: 4,  laborGrade: "L1 (Operator)", scrapPct: 0 },
    ] },
]

function ManufacturingRoutings() {
  const [status, setStatus] = React.useState<"all" | "active" | "draft">("active")
  const [q, setQ] = React.useState("")
  const [selected, setSelected] = React.useState<Routing | null>(null)

  const filtered = React.useMemo(() => {
    let r = ROUTINGS
    if (status !== "all") r = r.filter(x => x.status === status)
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(x => x.product.name.toLowerCase().includes(s) || x.product.code.toLowerCase().includes(s) || x.id.toLowerCase().includes(s))
    return r
  }, [status, q])

  /* Aggregate work-center load */
  const wcLoad: Record<string, number> = {}
  for (const r of ROUTINGS.filter(r => r.status === "active")) {
    for (const op of r.ops) {
      wcLoad[op.workCenter] = (wcLoad[op.workCenter] ?? 0) + op.setupMin + op.cycleMin
    }
  }
  const wcSorted = Object.entries(wcLoad).sort((a, b) => b[1] - a[1])
  const wcMax    = wcSorted[0]?.[1] ?? 1

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Manufacturing" }, { label: "Routings" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="primary" leadingIcon="plus">New Routing</Button>
          </>
        }
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 space-y-4">

          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Active routings"   value={ROUTINGS.filter(r => r.status === "active").length} sub="published, in-use" accent="brand"   />
            <KpiTile label="Avg ops / routing" value={Math.round(ROUTINGS.reduce((s, r) => s + r.ops.length, 0) / ROUTINGS.length)} sub="ops per routing" accent="accent" />
            <KpiTile label="Avg cycle time"    value={`${Math.round(ROUTINGS.reduce((s, r) => s + r.totalCycleMin, 0) / ROUTINGS.length)}m`} sub="excludes setup" accent="info" />
            <KpiTile label="Drafts pending"    value={ROUTINGS.filter(r => r.status === "draft").length} sub="awaiting eng approval" accent="warn" />
          </div>

          <div className="grid grid-cols-[1fr_360px] gap-4">
            <div className="space-y-4">
              <div className="flex items-center gap-2 flex-wrap">
                <SearchBox value={q} onChange={setQ} placeholder="Search routings, product…" className="max-w-[280px]" />
                <span className="w-px h-5 bg-divider mx-1" />
                <FilterChip active={status === "all"}    onClick={() => setStatus("all")}>All</FilterChip>
                <FilterChip active={status === "active"} onClick={() => setStatus("active")} tone="success">Active</FilterChip>
                <FilterChip active={status === "draft"}  onClick={() => setStatus("draft")} tone="warn">Draft</FilterChip>
              </div>

              <Card>
                <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">Routing</th>
                      <th>Product</th>
                      <th>Version</th>
                      <th className="text-right">Ops</th>
                      <th className="text-right">Setup</th>
                      <th className="text-right">Cycle</th>
                      <th>Last changed</th>
                      <th>Status</th>
                      <th></th>
                    </tr>
                  </thead>
                  <tbody>
                    {filtered.map(r => (
                      <tr key={r.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(r)}>
                        <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{r.id}</td>
                        <td>
                          <div className="font-semibold text-ink">{r.product.name}</div>
                          <div className="font-mono text-[10px] text-ink-mute">{r.product.code}</div>
                        </td>
                        <td><Badge variant="neutral">{r.version}</Badge></td>
                        <td className="text-right font-mono text-ink-soft">{r.ops.length}</td>
                        <td className="text-right font-mono text-ink-soft">{r.totalSetupMin}m</td>
                        <td className="text-right font-mono font-semibold text-ink">{r.totalCycleMin}m</td>
                        <td>
                          <div className="text-[11px] text-ink-soft">{r.lastChanged}</div>
                          <div className="text-[10px] text-ink-mute">by {r.changedBy}</div>
                        </td>
                        <td><Badge variant={r.status === "active" ? "success" : "warn"} dot>{r.status === "active" ? "Active" : "Draft"}</Badge></td>
                        <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </Card>
            </div>

            <div className="space-y-4">
              <AICallout
                title="Robot cell saves 17min / unit on RTG v4"
                body="The v4 draft replaces manual seam welding with the Bay-2 robot cell. Cycle time drops 17min (-7%); scrap drops from 1.5% → 0.8% at QC. Capex is already amortising; promoting v4 saves an estimated ₱68k/month across WO-2026-0139 and -0141."
                dismissLabel="Stay on v3"
                proceedLabel="Publish v4"
              />

              <Card>
                <CardHeader>
                  <CardTitle>Work-center load · today</CardTitle>
                  <span className="text-[10.5px] text-ink-mute font-mono">min</span>
                </CardHeader>
                <div className="p-3 space-y-2">
                  {wcSorted.slice(0, 6).map(([wc, mins]) => (
                    <div key={wc}>
                      <div className="flex items-baseline justify-between mb-0.5">
                        <span className="text-[11px] text-ink-soft truncate max-w-[180px]">{wc}</span>
                        <span className="font-mono text-[10.5px] text-ink">{mins}m</span>
                      </div>
                      <div className="h-1.5 rounded-full bg-cream-deep overflow-hidden">
                        <div className="h-full rounded-full bg-brand" style={{ width: `${(mins / wcMax) * 100}%` }} />
                      </div>
                    </div>
                  ))}
                </div>
              </Card>
            </div>
          </div>
        </div>
      </div>

      {selected && <RoutingSheet routing={selected} onClose={() => setSelected(null)} />}
    </>
  )
}

function RoutingSheet({ routing, onClose }: { routing: Routing; onClose: () => void }) {
  const total = routing.totalSetupMin + routing.totalCycleMin
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div>
          <div className="font-mono text-[10px] text-ink-mute">{routing.id}</div>
          <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{routing.product.name}</h2>
          <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
            <span className="font-mono">{routing.product.code}</span>
            <span>·</span>
            <Badge variant={routing.status === "active" ? "success" : "warn"} dot>{routing.version} · {routing.status === "active" ? "Active" : "Draft"}</Badge>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>
      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Operations</div>
              <div className="font-serif text-xl text-ink mt-0.5">{routing.ops.length}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Setup</div>
              <div className="font-serif text-xl text-ink mt-0.5">{routing.totalSetupMin}m</div>
            </div>
            <div className="bg-brand-soft border border-brand/30 rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-brand-mid">Cycle</div>
              <div className="font-serif text-xl text-brand mt-0.5">{routing.totalCycleMin}m</div>
              <div className="text-[10px] text-ink-mute mt-0.5">{total}m total</div>
            </div>
          </div>
        </section>

        {/* Gantt strip */}
        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Operation timing</div>
          <div className="bg-surface border border-divider rounded-md p-3">
            <div className="space-y-1.5">
              {routing.ops.map(op => {
                const startPct = (routing.ops.slice(0, op.seq / 10 - 1).reduce((s, o) => s + o.setupMin + o.cycleMin, 0) / total) * 100
                const lenPct   = ((op.setupMin + op.cycleMin) / total) * 100
                return (
                  <div key={op.seq} className="grid grid-cols-[44px_1fr] items-center gap-2 text-[10.5px]">
                    <span className="font-mono text-ink-mute">#{op.seq}</span>
                    <div className="relative h-4 rounded-sm bg-cream-deep overflow-hidden">
                      <div className="absolute h-full bg-warn/40" style={{ left: `${startPct}%`, width: `${(op.setupMin / total) * 100}%` }} title={`Setup ${op.setupMin}m`} />
                      <div className="absolute h-full bg-brand" style={{ left: `${startPct + (op.setupMin / total) * 100}%`, width: `${(op.cycleMin / total) * 100}%` }} title={`Cycle ${op.cycleMin}m`} />
                      <span className="absolute inset-0 px-1.5 flex items-center text-white text-[9.5px] font-semibold truncate" style={{ left: `${startPct}%`, width: `${lenPct}%` }}>{op.name}</span>
                    </div>
                  </div>
                )
              })}
            </div>
            <div className="flex items-center gap-3 mt-3 text-[10px] text-ink-mute">
              <span className="inline-flex items-center gap-1"><span className="size-2 rounded-sm bg-warn/60" /> setup</span>
              <span className="inline-flex items-center gap-1"><span className="size-2 rounded-sm bg-brand" /> cycle</span>
            </div>
          </div>
        </section>

        {/* Op detail table */}
        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Operations</div>
          <div className="bg-surface border border-divider rounded-md overflow-hidden">
            <table className="w-full text-[11.5px]">
              <thead>
                <tr className="bg-cream text-left text-[9.5px] font-bold uppercase tracking-wider text-ink-mute">
                  <th className="px-3 py-2 w-10">#</th>
                  <th>Operation / work center</th>
                  <th>Labor</th>
                  <th className="text-right">Setup</th>
                  <th className="text-right">Cycle</th>
                  <th className="text-right">Scrap</th>
                </tr>
              </thead>
              <tbody>
                {routing.ops.map(op => (
                  <tr key={op.seq} className="border-t border-divider-soft">
                    <td className="px-3 py-2 font-mono text-ink-mute">{op.seq}</td>
                    <td>
                      <div className="font-semibold text-ink">{op.name}</div>
                      <div className="text-[10px] text-ink-mute">{op.workCenter}</div>
                      {op.notes && <div className="text-[10px] text-warn mt-0.5">{op.notes}</div>}
                    </td>
                    <td className="text-ink-soft text-[10.5px]">{op.laborGrade}</td>
                    <td className="text-right font-mono text-ink-soft">{op.setupMin}m</td>
                    <td className="text-right font-mono font-semibold text-ink">{op.cycleMin}m</td>
                    <td className="text-right font-mono text-warn text-[10.5px]">{op.scrapPct > 0 ? `${op.scrapPct}%` : "—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </section>
      </SheetBody>
      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="ghost" leadingIcon="copy" className="ml-auto">Clone</Button>
        <Button variant="ghost" leadingIcon="edit">Edit</Button>
        {routing.status === "draft" && <Button variant="primary" leadingIcon="check">Publish</Button>}
      </SheetFooter>
    </Sheet>
  )
}

// SANDBOX
;(globalThis as any).ManufacturingRoutings = ManufacturingRoutings
;(globalThis as any).ROUTINGS = ROUTINGS

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/dashboard.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/manufacturing/dashboard.tsx
 * Manufacturing — module dashboard.
 */

function MfgDashboard() {
  return (
    <>
      <TopBar breadcrumb={[{ label: "Manufacturing" }, { label: "Dashboard" }]} />
      <ActionBar
        title="Manufacturing Dashboard"
        status="3 active plants · 6 work orders in flight · 14 BOMs · OEE 78.2%"
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New work order</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Yield analysis</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6 space-y-4">
          <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">Production · April</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">12,840</div>
              <div className="text-[12px] text-white/70 mt-1.5">Units completed · 94% of plan · OEE 78.2%</div>
            </div>
            <KpiTile label="Active WOs"  value="6"   sub="2 behind schedule" accent="info" />
            <KpiTile label="WIP"         value="$248k" sub="In flight" accent="warn" />
            <KpiTile label="Yield"       value="96.2%" sub="↑ 0.8% vs Mar" accent="success" deltaDir="up" delta="+0.8%" />
            <KpiTile label="Scrap"       value="$12.4k" sub="Material recovered" accent="danger" />
          </div>

          <div className="grid grid-cols-[2fr_1fr] gap-4">
            <Card>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Production by line</div>
                  <div className="font-serif text-[18px] text-ink mt-0.5">Today's output</div>
                </div>
              </CardHeader>
              <div className="p-4 space-y-3">
                {[
                  { line: "Calamba — Line A", plan: 200, actual: 184, status: "running" },
                  { line: "Calamba — Line B", plan: 150, actual: 142, status: "running" },
                  { line: "Pune — Line 1",    plan: 300, actual: 312, status: "running" },
                  { line: "Pune — Line 2",    plan: 280, actual: 256, status: "running" },
                  { line: "Pune — Line 3",    plan: 240, actual: 0,   status: "down" },
                ].map(l => {
                  const rate = l.actual / l.plan
                  return (
                    <div key={l.line}>
                      <div className="flex items-center justify-between mb-1">
                        <div className="flex items-center gap-2">
                          <span className={cn("size-2 rounded-full", l.status === "down" ? "bg-danger animate-pulse" : "bg-success")} />
                          <span className="text-[11.5px] text-ink font-medium">{l.line}</span>
                        </div>
                        <span className="text-[10.5px] font-mono font-bold text-ink">{l.actual} / {l.plan} <span className="text-ink-mute">({Math.round(rate * 100)}%)</span></span>
                      </div>
                      <div className="h-2 bg-cream rounded-full overflow-hidden">
                        <div className={cn("h-full", l.status === "down" ? "bg-danger" : rate >= 1 ? "bg-success" : rate >= 0.9 ? "bg-info" : "bg-warn")} style={{ width: `${Math.min(rate, 1.1) * 100}%` }} />
                      </div>
                    </div>
                  )
                })}
              </div>
            </Card>

            <Card>
              <CardHeader>
                <div>
                  <div className="text-[10px] uppercase tracking-wider font-bold text-danger">Alerts</div>
                  <div className="font-serif text-[18px] text-ink mt-0.5">Needs attention</div>
                </div>
              </CardHeader>
              <div className="p-3 space-y-2">
                <Alert tone="danger"  text="Pune — Line 3 is DOWN · Compressor failure" />
                <Alert tone="warn"    text="WO-2026-0214 behind schedule by 3 hours" />
                <Alert tone="warn"    text="Raw material shortage: SKU-44210 (steel)" />
                <Alert tone="warn"    text="Scheduled maintenance overdue: 2 machines" />
              </div>
            </Card>
          </div>

          {/* Work orders in flight — ties the "6 Active WOs" KPI to real rows */}
          <Card>
            <CardHeader>
              <div>
                <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Work orders</div>
                <div className="font-serif text-[18px] text-ink mt-0.5">In flight today</div>
              </div>
              <Button variant="ghost" size="sm" trailingIcon="arrow-right">View all</Button>
            </CardHeader>
            <div className="grid grid-cols-[130px_1.7fr_1.1fr_160px_110px_110px] gap-x-4 px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-y border-divider">
              <span>WO #</span><span>Product</span><span>Plant / Line</span><span>Progress</span><span>Due</span><span>Status</span>
            </div>
            {[
              { id: "WO-2026-0210", product: "Steel reinforcement bars · 12mm", line: "Calamba — Line A", done: 184, qty: 200, due: "Today 18:00", status: "running" },
              { id: "WO-2026-0211", product: "Steel reinforcement bars · 16mm", line: "Calamba — Line B", done: 142, qty: 150, due: "Today 20:00", status: "running" },
              { id: "WO-2026-0212", product: "Modular warehouse panels (40ft)", line: "Pune — Line 1",    done: 312, qty: 300, due: "Today 16:00", status: "ahead"   },
              { id: "WO-2026-0213", product: "Insulated wall panels — Type A",   line: "Pune — Line 2",    done: 256, qty: 280, due: "Tomorrow",    status: "running" },
              { id: "WO-2026-0214", product: "HVAC ductwork assemblies",         line: "Pune — Line 3",    done: 0,   qty: 240, due: "Overdue 3h",  status: "blocked" },
              { id: "WO-2026-0215", product: "QC rework · panel batch R-19",     line: "Sydney",           done: 0,   qty: 64,  due: "W20 May",     status: "queued"  },
            ].map(w => {
              const rate = w.qty ? w.done / w.qty : 0
              const bar = w.status === "blocked" ? "bg-danger" : rate >= 1 ? "bg-success" : rate >= 0.9 ? "bg-info" : "bg-warn"
              return (
                <div key={w.id} className="grid grid-cols-[130px_1.7fr_1.1fr_160px_110px_110px] gap-x-4 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-brand-mid">{w.id}</span>
                  <span className="text-ink font-medium truncate">{w.product}</span>
                  <span className="text-ink-soft truncate">{w.line}</span>
                  <span>
                    <div className="flex items-center gap-2">
                      <div className="flex-1 h-1.5 bg-cream rounded-full overflow-hidden">
                        <div className={cn("h-full", bar)} style={{ width: `${Math.min(rate, 1) * 100}%` }} />
                      </div>
                      <span className="font-mono text-[10px] text-ink-mute whitespace-nowrap">{w.done}/{w.qty}</span>
                    </div>
                  </span>
                  <span className={cn("text-ink-soft", w.status === "blocked" && "text-danger font-semibold")}>{w.due}</span>
                  {w.status === "ahead"   ? <Badge variant="success" dot>Ahead</Badge>
                  : w.status === "running" ? <Badge variant="info" dot>Running</Badge>
                  : w.status === "blocked" ? <Badge variant="danger" dot>Blocked</Badge>
                                           : <Badge variant="neutral">Queued</Badge>}
                </div>
              )
            })}
          </Card>
        </div>
      </div>
    </>
  )
}

function Alert({ tone, text }: { tone: "danger" | "warn" | "info"; text: string }) {
  const cls = tone === "danger" ? "bg-danger-soft text-danger" : tone === "warn" ? "bg-warn-soft text-warn" : "bg-info-soft text-info"
  const icon = tone === "danger" ? "alert-triangle" : "info"
  return (
    <div className={cn("p-2.5 rounded-md flex items-center gap-2 text-[11.5px] font-medium", cls)}>
      <Icon name={icon as any} size={12} />
      <span className="flex-1">{text}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/production-planning.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/manufacturing/production-planning.tsx
 * Manufacturing — Production planning (weekly forecast + routings absorbed).
 */

function ProductionPlanning() {
  const plans = [
    { id: "PP-2026-W19-001", product: "Steel reinforcement bars · 12mm",   plant: "Calamba",  week: "W19 May", planned: 1200, lineHours: 96,  routing: "RT-101 · Cut → Weld → QC",        status: "released" },
    { id: "PP-2026-W19-002", product: "Steel reinforcement bars · 16mm",   plant: "Calamba",  week: "W19 May", planned: 800,  lineHours: 64,  routing: "RT-102 · Cut → Weld → QC",        status: "released" },
    { id: "PP-2026-W19-003", product: "Modular warehouse panels (40ft)",   plant: "Pune",     week: "W19 May", planned: 24,   lineHours: 120, routing: "RT-208 · Form → Weld → Paint → QC", status: "planned"  },
    { id: "PP-2026-W19-004", product: "Insulated wall panels — Type A",    plant: "Pune",     week: "W19 May", planned: 180,  lineHours: 80,  routing: "RT-218 · Press → Cure → QC",      status: "planned"  },
    { id: "PP-2026-W19-005", product: "HVAC ductwork assemblies",          plant: "Sydney",   week: "W19 May", planned: 64,   lineHours: 40,  routing: "RT-302 · Sheet → Form → QC",      status: "draft"    },
    { id: "PP-2026-W20-001", product: "Steel reinforcement bars · 12mm",   plant: "Calamba",  week: "W20 May", planned: 1400, lineHours: 112, routing: "RT-101 · Cut → Weld → QC",        status: "draft"    },
  ]
  return (
    <>
      <TopBar breadcrumb={[{ label: "Manufacturing" }, { label: "Production Planning" }]} />
      <ActionBar
        title="Production Planning"
        status={`${plans.length} plans · ${plans.filter(p => p.status === "released").length} released to floor · 3 plants`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New plan</Button>}
        secondary={<Button variant="ghost" leadingIcon="calendar" size="sm">Capacity view</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Forecast demand</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1280px] mx-auto p-6 space-y-4">
          <div className="bg-info-soft border border-info/20 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">Routings absorbed into Production Planning.</span> Standard routing templates still live in setup, but operational routing now happens here per plan. Each released plan generates downstream Work Orders.
            </div>
          </div>
          <Card>
            <div className="grid grid-cols-[180px_2fr_120px_120px_120px_2fr_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
              <span>Plan #</span><span>Product</span><span>Plant</span><span>Week</span><span className="text-right">Planned qty</span><span>Routing</span><span>Status</span>
            </div>
            {plans.map(p => (
              <div key={p.id} className="grid grid-cols-[180px_2fr_120px_120px_120px_2fr_120px] 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-brand-mid">{p.id}</span>
                <span className="text-ink font-medium">{p.product}</span>
                <span className="text-ink-soft">{p.plant}</span>
                <span className="font-mono text-ink-soft">{p.week}</span>
                <span className="font-mono text-right font-bold">{p.planned.toLocaleString()}<div className="text-[10px] font-normal text-ink-mute">{p.lineHours}h line time</div></span>
                <span className="text-[10.5px] text-ink-mute">{p.routing}</span>
                {p.status === "released" ? <Badge variant="success">Released</Badge>
                : p.status === "planned"  ? <Badge variant="info" dot>Planned</Badge>
                                          : <Badge variant="neutral">Draft</Badge>}
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/shop-floor.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/manufacturing/shop-floor.tsx
 * Manufacturing — Shop Floor real-time view.
 */

function ShopFloor() {
  const lines = [
    { line: "Calamba A", op: "Cutting",  station: "ST-101", operator: "Romulo C.", initials: "RC", currentWO: "WO-2026-0214", product: "Rebar 12mm", produced: 184, target: 200, status: "running",     uptime: 0.92 },
    { line: "Calamba A", op: "Welding",  station: "ST-102", operator: "Joseph R.", initials: "JR", currentWO: "WO-2026-0214", product: "Rebar 12mm", produced: 168, target: 200, status: "running",     uptime: 0.88 },
    { line: "Calamba A", op: "QC",       station: "ST-103", operator: "Karla M.",  initials: "KM", currentWO: "WO-2026-0214", product: "Rebar 12mm", produced: 162, target: 200, status: "running",     uptime: 0.90 },
    { line: "Calamba B", op: "Cutting",  station: "ST-201", operator: "Daniel R.", initials: "DR", currentWO: "WO-2026-0212", product: "Rebar 16mm", produced: 142, target: 150, status: "running",     uptime: 0.95 },
    { line: "Pune 1",    op: "Forming",  station: "ST-PN-1", operator: "Anand K.",  initials: "AK", currentWO: "WO-2026-0218", product: "Wall panels", produced: 312, target: 300, status: "ahead",       uptime: 0.98 },
    { line: "Pune 2",    op: "Press",    station: "ST-PN-2", operator: "Suresh P.", initials: "SP", currentWO: "WO-2026-0220", product: "Insulation",  produced: 256, target: 280, status: "running",     uptime: 0.91 },
    { line: "Pune 3",    op: "Press",    station: "ST-PN-3", operator: "—",         initials: "—",  currentWO: "—",            product: "—",            produced: 0,   target: 240, status: "down",        uptime: 0    },
  ]
  return (
    <>
      <TopBar breadcrumb={[{ label: "Manufacturing" }, { label: "Shop Floor" }]} />
      <ActionBar
        title="Shop Floor"
        status={`${lines.length} stations · ${lines.filter(l => l.status === "running" || l.status === "ahead").length} running · ${lines.filter(l => l.status === "down").length} DOWN`}
        secondary={<Button variant="ghost" leadingIcon="refresh" size="sm">Refresh</Button>}
        primary={<Button variant="primary" leadingIcon="megaphone" size="sm">Announce</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6">
          <div className="grid grid-cols-2 xl:grid-cols-3 gap-3">
            {lines.map(l => {
              const rate = l.produced / l.target
              return (
                <Card key={l.station} className={cn("hover:shadow-md transition-shadow", l.status === "down" && "ring-2 ring-danger/40")}>
                  <div className="p-4">
                    <div className="flex items-center gap-2 mb-2">
                      <span className={cn("size-2.5 rounded-full",
                        l.status === "down" ? "bg-danger animate-pulse" : l.status === "ahead" ? "bg-success" : "bg-success"
                      )} />
                      <span className="font-mono text-[10.5px] font-bold text-brand-mid">{l.station}</span>
                      <span className="text-[10px] text-ink-mute">· {l.line} · {l.op}</span>
                    </div>
                    <div className="text-[13px] text-ink font-semibold">{l.product}</div>
                    <div className="text-[10.5px] text-ink-mute mt-0.5 font-mono">{l.currentWO}</div>

                    <div className="mt-3">
                      <div className="flex items-end justify-between gap-2 mb-1">
                        <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Output</span>
                        <span className="font-serif text-[20px] text-ink leading-none">
                          {l.produced} <span className="text-[12px] text-ink-mute">/ {l.target}</span>
                        </span>
                      </div>
                      <div className="h-2 bg-cream rounded-full overflow-hidden">
                        <div className={cn("h-full", l.status === "down" ? "bg-danger" : rate >= 1 ? "bg-success" : rate >= 0.9 ? "bg-info" : "bg-warn")}
                          style={{ width: `${Math.min(rate, 1.1) * 100}%` }} />
                      </div>
                      <div className="text-[10px] text-ink-mute mt-1 font-mono">Uptime · {Math.round(l.uptime * 100)}%</div>
                    </div>

                    {l.operator !== "—" && (
                      <div className="mt-3 pt-3 border-t border-divider-soft flex items-center gap-2">
                        <Avatar initials={l.initials} tone="sand" size="sm" />
                        <div className="text-[10.5px] text-ink-soft">{l.operator}</div>
                      </div>
                    )}
                  </div>
                </Card>
              )
            })}
          </div>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/manufacturing/quality.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/manufacturing/quality.tsx
 * Manufacturing — Quality Control inspections + defect tracking.
 */

function QualityControl() {
  const inspections = [
    { id: "QC-2026-0488", wo: "WO-2026-0214", item: "Rebar 12mm",      sampled: 50,  passed: 49, failed: 1, defectRate: 0.02, status: "complete",  inspector: "Karla M.", date: "Today 11:14" },
    { id: "QC-2026-0487", wo: "WO-2026-0214", item: "Rebar 12mm",      sampled: 50,  passed: 50, failed: 0, defectRate: 0.00, status: "complete",  inspector: "Karla M.", date: "Today 09:08" },
    { id: "QC-2026-0486", wo: "WO-2026-0212", item: "Rebar 16mm",      sampled: 40,  passed: 39, failed: 1, defectRate: 0.025, status: "complete", inspector: "Daniel R.", date: "Today 08:24" },
    { id: "QC-2026-0485", wo: "WO-2026-0218", item: "Wall panels",    sampled: 24,  passed: 23, failed: 1, defectRate: 0.042, status: "in-review", inspector: "Anand K.",  date: "Today 07:30" },
    { id: "QC-2026-0484", wo: "WO-2026-0220", item: "Insulation panels", sampled: 30, passed: 26, failed: 4, defectRate: 0.133, status: "in-review", inspector: "Suresh P.", date: "Yesterday" },
    { id: "QC-2026-0483", wo: "WO-2026-0212", item: "Rebar 16mm",      sampled: 40,  passed: 40, failed: 0, defectRate: 0.00, status: "complete",  inspector: "Daniel R.", date: "Yesterday" },
    { id: "QC-2026-0482", wo: "WO-2026-0210", item: "HVAC ductwork",   sampled: 16,  passed: 14, failed: 2, defectRate: 0.125, status: "complete", inspector: "Tom N.",    date: "2d ago" },
  ]
  const passing  = inspections.filter(i => i.defectRate < 0.03).length
  const flagged  = inspections.filter(i => i.defectRate >= 0.03).length
  return (
    <>
      <TopBar breadcrumb={[{ label: "Manufacturing" }, { label: "Quality Control" }]} />
      <ActionBar
        title="Quality Control"
        status={`${inspections.length} inspections this week · ${passing} passing · ${flagged} flagged for review`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New inspection</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Defect trends</Button>}
      />
      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1280px] mx-auto p-6 space-y-4">
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Avg defect rate"   value="3.4%"  sub="↓ from 4.2% in March" accent="success" deltaDir="up" delta="-0.8pp" />
            <KpiTile label="Inspections / wk"  value={inspections.length.toString()} sub="On track" accent="brand" />
            <KpiTile label="Flagged"            value={flagged.toString()} sub="Need engineering review" accent="warn" />
            <KpiTile label="First-pass yield"  value="96.2%" sub="Across all WOs" accent="info" />
          </div>

          <Card>
            <CardHeader>
              <div>
                <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Inspection log</div>
                <div className="font-serif text-[18px] text-ink mt-0.5">Recent inspections</div>
              </div>
            </CardHeader>
            <div className="grid grid-cols-[150px_120px_2fr_120px_100px_100px_120px_120px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute border-y border-divider">
              <span>QC #</span><span>WO</span><span>Item</span><span className="text-right">Sampled</span><span className="text-right">Passed</span><span className="text-right">Failed</span><span>Defect rate</span><span>Status</span>
            </div>
            {inspections.map(i => (
              <div key={i.id} className="grid grid-cols-[150px_120px_2fr_120px_100px_100px_120px_120px] 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-brand-mid">{i.id}</span>
                <span className="font-mono text-ink-mute">{i.wo}</span>
                <div><div className="text-ink">{i.item}</div><div className="text-[10px] text-ink-mute">Inspector: {i.inspector} · {i.date}</div></div>
                <span className="font-mono text-right">{i.sampled}</span>
                <span className="font-mono text-right text-success font-bold">{i.passed}</span>
                <span className={cn("font-mono text-right font-bold", i.failed > 0 && "text-danger")}>{i.failed}</span>
                <span className={cn("font-mono", i.defectRate >= 0.05 ? "text-danger font-bold" : i.defectRate >= 0.03 ? "text-warn font-bold" : "text-ink-mute")}>{(i.defectRate * 100).toFixed(1)}%</span>
                {i.status === "complete"  ? <Badge variant="success">✓ Complete</Badge>
                                          : <Badge variant="warn" dot>In review</Badge>}
              </div>
            ))}
          </Card>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/projects/portfolio.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/projects/portfolio.tsx
 * ----------------------------------------------------------------------------
 * Project Accounting hero — portfolio view. Cards per project with WIP,
 * margin, completion bar, and team. Highlights at-risk projects.
 * ============================================================================
 */

function ProjectPortfolio() {
  const { push } = useRouter()
  const [statusFilter, setStatusFilter] = React.useState<"active" | "all" | Project["status"]>("active")
  const [typeFilter, setTypeFilter] = React.useState<"all" | Project["type"]>("all")
  const [search, setSearch] = React.useState("")

  const fxToUSD = (v: number, c: Project["currency"]) => c === "PHP" ? v / 56.95 : c === "SGD" ? v / 1.35 : c === "HKD" ? v / 7.81 : v

  const counts: Record<string, number> = { all: PROJECTS.length, active: 0 }
  for (const p of PROJECTS) {
    counts[p.status] = (counts[p.status] ?? 0) + 1
    if (p.status === "active" || p.status === "at-risk") counts.active++
  }

  const filtered = React.useMemo(() => {
    let r = PROJECTS
    if (statusFilter === "active") r = r.filter(p => p.status === "active" || p.status === "at-risk")
    else if (statusFilter !== "all") r = r.filter(p => p.status === statusFilter)
    if (typeFilter !== "all") r = r.filter(p => p.type === typeFilter)
    if (search.trim()) {
      const q = search.toLowerCase()
      r = r.filter(p => p.name.toLowerCase().includes(q) || p.client.toLowerCase().includes(q) || p.id.toLowerCase().includes(q))
    }
    return r
  }, [statusFilter, typeFilter, search])

  /* Portfolio totals — USD-equivalent */
  const activeProjs = PROJECTS.filter(p => p.status === "active" || p.status === "at-risk")
  const totalBudget   = activeProjs.reduce((s, p) => s + fxToUSD(p.budget, p.currency), 0)
  const totalBilled   = activeProjs.reduce((s, p) => s + fxToUSD(p.billed, p.currency), 0)
  const totalWIP      = activeProjs.reduce((s, p) => s + fxToUSD(p.wipUnbilled, p.currency), 0)
  const blendedMargin = activeProjs.reduce((s, p) => s + p.marginPct, 0) / activeProjs.length
  const atRisk = PROJECTS.filter(p => p.status === "at-risk")

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Project Accounting" }, { label: "Portfolio" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="grid">Timeline view</Button>
            <Button variant="primary" leadingIcon="plus">New Project</Button>
          </>
        }
      />

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

          {/* Portfolio totals */}
          <div className="grid grid-cols-[2fr_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">Active Portfolio · USD equivalent</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">${(totalBudget / 1000).toFixed(0)}k</div>
              <div className="text-[12px] text-white/70 mt-1.5">Budget across {activeProjs.length} active projects · {atRisk.length > 0 && <span className="text-warn font-semibold">{atRisk.length} at risk</span>}</div>
            </div>
            <KpiTile label="Billed YTD" value={`$${(totalBilled / 1000).toFixed(0)}k`} sub={`${Math.round((totalBilled / totalBudget) * 100)}% of plan`} accent="success" />
            <KpiTile label="Unbilled WIP" value={`$${(totalWIP / 1000).toFixed(0)}k`} sub="Ready to invoice" accent="warn" />
            <KpiTile label="Blended margin" value={`${blendedMargin.toFixed(0)}%`} sub="Gross" accent="accent" deltaDir={blendedMargin > 25 ? "up" : "down"} delta={blendedMargin > 25 ? "+2pp" : "-1pp"} />
          </div>

          {/* Filters */}
          <div className="flex items-center justify-between gap-3 flex-wrap">
            <div className="flex items-center gap-1.5 flex-wrap">
              <FilterChip active={statusFilter === "active"} onClick={() => setStatusFilter("active")} count={counts.active}>Active + At Risk</FilterChip>
              <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")} count={counts.all}>All</FilterChip>
              <span className="w-px h-5 bg-divider mx-1" />
              {(["at-risk", "on-hold", "completed"] as const).map(s => {
                const c = counts[s] ?? 0
                if (c === 0) return null
                return (
                  <FilterChip key={s} active={statusFilter === s} onClick={() => setStatusFilter(s)} count={c} tone={PRJ_STATUS_TONE[s] as any}>
                    {PRJ_STATUS_LABEL[s]}
                  </FilterChip>
                )
              })}
              <span className="w-px h-5 bg-divider mx-1" />
              {(["all", "Fixed Bid", "T&M", "Retainer"] as const).map(t => (
                <FilterChip key={t} active={typeFilter === t} onClick={() => setTypeFilter(t)} tone="info">
                  {t === "all" ? "Any type" : t}
                </FilterChip>
              ))}
            </div>
            <div className="relative">
              <input
                value={search} onChange={e => setSearch(e.target.value)}
                placeholder="Search projects…"
                className="w-64 h-9 pl-9 pr-3 bg-surface border border-divider rounded-md text-[13px] placeholder:text-ink-faint focus:border-brand-mid"
              />
              <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-mute" />
            </div>
          </div>

          {/* Project cards */}
          <div className="grid grid-cols-2 gap-3">
            {filtered.map(p => <ProjectCard key={p.id} p={p} />)}
            {filtered.length === 0 && (
              <div className="col-span-2 bg-surface border border-divider rounded-lg p-12 text-center text-ink-mute text-[12px]">
                No projects match these filters.
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  )
}

/* ─── Project card ──────────────────────────────────────────────────── */
function ProjectCard({ p }: { p: Project }) {
  const symbol = p.currency === "PHP" ? "₱" : p.currency === "SGD" ? "S$" : p.currency === "HKD" ? "HK$" : "$"
  const spendPct = Math.min(100, Math.round((p.spent / p.budget) * 100))
  const burnAhead = spendPct > p.completionPct + 8
  return (
    <div className={cn(
      "bg-surface border rounded-lg p-4 transition-shadow hover:shadow-md",
      p.status === "at-risk" ? "border-warn/40" : "border-divider"
    )}>
      {/* Header */}
      <div className="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">{p.id}</span>
            <Badge variant={PRJ_STATUS_TONE[p.status] as any} dot>{PRJ_STATUS_LABEL[p.status]}</Badge>
            <Badge variant="neutral">{p.type}</Badge>
            {p.starred && <Icon name="star" size={11} className="text-accent fill-accent" />}
          </div>
          <h3 className="font-serif text-[16px] text-ink leading-tight">{p.name}</h3>
          <div className="text-[11px] text-ink-mute mt-1 flex items-center gap-1.5">
            <Avatar initials={p.clientInitials} tone={p.clientTone as any} size="xs" />
            {p.client}
            <span className="text-ink-faint">·</span>
            <span className="font-mono">{p.startDate.replace(", 2025", "")} → {p.targetEnd}</span>
          </div>
        </div>
      </div>

      {/* Money metrics */}
      <div className="grid grid-cols-4 gap-2 mt-3 pt-3 border-t border-divider-soft">
        <Metric label="Budget" value={`${symbol}${(p.budget / 1000).toFixed(0)}k`} />
        <Metric label="Spent"  value={`${symbol}${(p.spent / 1000).toFixed(0)}k`} sub={`${spendPct}%`} tone={burnAhead ? "warn" : "default"} />
        <Metric label="Billed" value={`${symbol}${(p.billed / 1000).toFixed(0)}k`} />
        <Metric label="WIP"    value={`${symbol}${(p.wipUnbilled / 1000).toFixed(0)}k`} tone={p.wipUnbilled > 0 ? "accent" : "default"} />
      </div>

      {/* Progress + margin */}
      <div className="mt-3 space-y-2">
        <div>
          <div className="flex items-baseline justify-between text-[10px] mb-1">
            <span className="text-ink-mute font-mono uppercase tracking-wider font-bold">Completion · {p.completionPct}%</span>
            <span className="text-ink-mute font-mono">{p.hoursLogged.toLocaleString()} / {p.hoursTotal.toLocaleString()} hrs</span>
          </div>
          <div className="h-1.5 bg-cream-deep rounded-full overflow-hidden">
            <div className={cn("h-full rounded-full", p.status === "at-risk" ? "bg-warn" : "bg-brand-mid")} style={{ width: `${p.completionPct}%` }} />
          </div>
        </div>

        {burnAhead && (
          <div className="flex items-center gap-1.5 bg-warn-soft/60 text-warn text-[10.5px] px-2 py-1 rounded">
            <Icon name="alert-triangle" size={10} />
            Spend ({spendPct}%) is running ahead of completion ({p.completionPct}%) — margin compression risk
          </div>
        )}
      </div>

      {/* Team + margin */}
      <div className="flex items-center justify-between mt-3 pt-3 border-t border-divider-soft">
        <div className="flex items-center gap-2">
          <AvatarStack people={p.team.map(t => ({ initials: t.initials, tone: t.tone }))} max={4} />
          <span className="text-[10px] text-ink-mute">{p.team.length} on team</span>
        </div>
        <div className="flex items-center gap-3">
          <div className="text-right">
            <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Margin</div>
            <div className={cn("font-mono font-bold text-[13px]", p.marginPct >= 30 ? "text-success" : p.marginPct >= 20 ? "text-warn" : "text-danger")}>{p.marginPct}%</div>
          </div>
          <Button variant="ghost" size="sm" trailingIcon="arrow-right">Open</Button>
        </div>
      </div>
    </div>
  )
}

function Metric({ label, value, sub, tone = "default" }: { label: string; value: string; sub?: string; tone?: "default" | "warn" | "accent" }) {
  return (
    <div>
      <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">{label}</div>
      <div className={cn("font-mono font-semibold text-[13px] mt-0.5", tone === "warn" ? "text-warn" : tone === "accent" ? "text-accent-deep" : "text-ink")}>
        {value}{sub && <span className="ml-1 text-[10px] text-ink-mute font-sans">{sub}</span>}
      </div>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/projects/timesheets.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/projects/timesheets.tsx
 * ----------------------------------------------------------------------------
 * Project Accounting — Timesheets approval inbox + week grid.
 *
 * Layout: weekly grid (Mon-Sun) with each row a person + project. Left column
 * is filter chips + status. Click an entry → slideout with timecard detail.
 * AI callout flags missing days &amp; mis-coded hours.
 * ============================================================================
 */

interface TimeEntry {
  id: string
  person: { name: string; initials: string; tone: any; role: string }
  project: { id: string; name: string }
  hours: number[]              /* Mon..Sun = 7 numbers */
  status: "draft" | "submitted" | "approved" | "rejected"
  billable: boolean
  task: string
  notes?: string
}

const WEEK_LABEL = "Week 16 · Apr 13 — Apr 19, 2026"
const WEEK_DAYS  = ["Mon 13", "Tue 14", "Wed 15", "Thu 16", "Fri 17", "Sat 18", "Sun 19"]

const TIMESHEETS: TimeEntry[] = [
  { id: "TS-2026-W16-001", person: { name: "Ben L.",   initials: "BL", tone: "teal",  role: "PM" },           project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" },  hours: [8, 8, 6, 8, 8, 0, 0], status: "submitted", billable: true,  task: "Onsite supervision + status reporting",                notes: "2hr lost Wed to crane outage — should it bill?" },
  { id: "TS-2026-W16-002", person: { name: "Warren C.", initials: "WC", tone: "amber", role: "Foreman" },     project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" },  hours: [10, 10, 9, 10, 8, 4, 0], status: "submitted", billable: true,  task: "Precast install · slab levels 4-6" },
  { id: "TS-2026-W16-003", person: { name: "Joel D.",  initials: "JD", tone: "amber", role: "QC" },           project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" },  hours: [6, 7, 8, 4, 0, 0, 0], status: "submitted", billable: true,  task: "Strength test + acceptance" },
  { id: "TS-2026-W16-004", person: { name: "Maria A.", initials: "MA", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" },  hours: [8, 8, 8, 8, 8, 0, 0], status: "submitted", billable: true,  task: "Structural QC review" },

  { id: "TS-2026-W16-010", person: { name: "Ben L.",   initials: "BL", tone: "teal",  role: "PM" },           project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },     hours: [0, 0, 2, 0, 0, 0, 0], status: "submitted", billable: true,  task: "Stakeholder sync · variance budget" },
  { id: "TS-2026-W16-011", person: { name: "Warren C.", initials: "WC", tone: "amber", role: "Foreman" },     project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },     hours: [0, 0, 0, 0, 0, 6, 8], status: "submitted", billable: true,  task: "Weekend pour · concrete delivery" },
  { id: "TS-2026-W16-012", person: { name: "Pedro S.", initials: "PS", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },     hours: [8, 8, 8, 8, 6, 0, 0], status: "approved",  billable: true,  task: "Drawings + RFI" },
  { id: "TS-2026-W16-013", person: { name: "Maria A.", initials: "MA", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },     hours: [0, 0, 0, 4, 4, 0, 0], status: "submitted", billable: false, task: "Lessons-learned doc (internal)", notes: "Marking non-billable per project plan" },

  { id: "TS-2026-W16-020", person: { name: "Pedro S.", initials: "PS", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-012", name: "Globe Telecom · Cebu refit" },      hours: [0, 0, 0, 0, 2, 0, 0], status: "submitted", billable: true,  task: "Site walk · scope clarification" },
  { id: "TS-2026-W16-021", person: { name: "Ben L.",   initials: "BL", tone: "teal",  role: "PM" },           project: { id: "PRJ-2026-012", name: "Globe Telecom · Cebu refit" },      hours: [0, 0, 0, 4, 4, 0, 0], status: "submitted", billable: true,  task: "Scope review w/ client" },

  { id: "TS-2026-W16-030", person: { name: "Maria A.", initials: "MA", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-011", name: "SteelMark · Production audit" },    hours: [0, 0, 0, 0, 0, 0, 0], status: "draft",     billable: true,  task: "(empty — retainer)", notes: "Maria did not log retainer hours this week" },
  { id: "TS-2026-W16-031", person: { name: "Rico T.",  initials: "RT", tone: "blue",  role: "Senior Eng" },   project: { id: "PRJ-2026-010", name: "Republic Cement · MEC rollout" },   hours: [4, 4, 4, 6, 4, 0, 0], status: "submitted", billable: true,  task: "Discovery + config" },
  { id: "TS-2026-W16-032", person: { name: "Maria A.", initials: "MA", tone: "blue",  role: "Engineer" },     project: { id: "PRJ-2026-010", name: "Republic Cement · MEC rollout" },   hours: [4, 4, 4, 2, 4, 0, 0], status: "submitted", billable: true,  task: "Config build" },
]

const TS_TONE: Record<TimeEntry["status"], string> = { draft: "neutral", submitted: "info", approved: "success", rejected: "danger" }

function ProjectTimesheets() {
  const [scope, setScope] = React.useState<"all" | "submitted" | "approved" | "missing">("submitted")
  const [proj, setProj]   = React.useState<"all" | string>("all")
  const [selected, setSelected] = React.useState<TimeEntry | null>(null)

  const filtered = React.useMemo(() => {
    let r = TIMESHEETS
    if (scope === "submitted") r = r.filter(t => t.status === "submitted")
    else if (scope === "approved") r = r.filter(t => t.status === "approved")
    else if (scope === "missing") r = r.filter(t => t.hours.reduce((a, b) => a + b, 0) === 0 || t.status === "draft")
    if (proj !== "all") r = r.filter(t => t.project.id === proj)
    return r
  }, [scope, proj])

  /* Aggregate metrics */
  const totalHours      = TIMESHEETS.reduce((s, t) => s + t.hours.reduce((a, b) => a + b, 0), 0)
  const billableHours   = TIMESHEETS.filter(t => t.billable).reduce((s, t) => s + t.hours.reduce((a, b) => a + b, 0), 0)
  const utilization     = totalHours > 0 ? (billableHours / totalHours) * 100 : 0
  const pendingApproval = TIMESHEETS.filter(t => t.status === "submitted").length
  const missingPeople   = TIMESHEETS.filter(t => t.hours.reduce((a, b) => a + b, 0) === 0 || t.status === "draft").length

  const projects = Array.from(new Set(TIMESHEETS.map(t => t.project.id))).map(id => TIMESHEETS.find(t => t.project.id === id)!.project)

  function approveAll() {
    /* In prod this would call the API; here we just visually mark approved. */
    /* (stateful no-op since we don't mutate TIMESHEETS in this prototype) */
  }

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Project Accounting" }, { label: "Timesheets" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="mail">Nudge late submitters</Button>
            <Button variant="primary" leadingIcon="check" onClick={approveAll}>Approve {pendingApproval}</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="Hours logged"  value={totalHours} sub={WEEK_LABEL.split(" · ")[1]} accent="brand" />
            <KpiTile label="Billable"      value={`${billableHours} / ${totalHours}`} sub={`${utilization.toFixed(0)}% utilization`} accent="success" />
            <KpiTile label="Pending approval" value={pendingApproval} sub="from 7 people" accent="warn" />
            <KpiTile label="Missing entries" value={missingPeople} sub="not yet submitted" accent="danger" />
          </div>

          <AICallout
            title="2 entries need a second look"
            body="Ben L. logged 2h on Filinvest Wed (PRJ-2026-013) but the entry note says 'crane outage on Megaworld'. Maria A. didn't log her SteelMark retainer hours this week — retainer auto-bills 10h regardless. Want to re-tag Ben's hours and remind Maria?"
            matches={[
              { code: "TS-2026-W16-010", name: "Ben L. · 2h likely mis-coded (Filinvest → Megaworld)", pct: 92 },
              { code: "TS-2026-W16-030", name: "Maria A. · SteelMark retainer · 0h logged",            pct: 100 },
            ]}
            dismissLabel="I'll review each"
            proceedLabel="Auto-fix + remind"
          />

          <div className="flex items-center gap-2 flex-wrap">
            <FilterChip active={scope === "all"}       onClick={() => setScope("all")}>All {TIMESHEETS.length}</FilterChip>
            <FilterChip active={scope === "submitted"} onClick={() => setScope("submitted")} tone="info">Submitted {pendingApproval}</FilterChip>
            <FilterChip active={scope === "approved"}  onClick={() => setScope("approved")} tone="success">Approved {TIMESHEETS.filter(t => t.status === "approved").length}</FilterChip>
            <FilterChip active={scope === "missing"}   onClick={() => setScope("missing")} tone="danger">Missing {missingPeople}</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={proj === "all"} onClick={() => setProj("all")}>All projects</FilterChip>
            {projects.map(p => (
              <FilterChip key={p.id} active={proj === p.id} onClick={() => setProj(p.id)}>{p.name.split(" · ")[0]}</FilterChip>
            ))}
            <span className="ml-auto text-[11px] text-ink-mute font-mono">{WEEK_LABEL}</span>
          </div>

          {/* Week grid */}
          <Card>
            <div className="grid grid-cols-[260px_repeat(7,1fr)_80px_70px] items-center bg-cream text-[10px] font-bold uppercase tracking-wider text-ink-mute border-b border-divider">
              <div className="px-4 py-2">Person · project</div>
              {WEEK_DAYS.map(d => <div key={d} className="px-2 py-2 text-center">{d}</div>)}
              <div className="px-2 py-2 text-right">Total</div>
              <div className="px-2 py-2">Status</div>
            </div>
            <div className="divide-y divide-divider-soft">
              {filtered.map(t => {
                const total = t.hours.reduce((a, b) => a + b, 0)
                return (
                  <button key={t.id} onClick={() => setSelected(t)}
                    className="grid grid-cols-[260px_repeat(7,1fr)_80px_70px] items-center w-full text-left text-[11.5px] hover:bg-cream/40 transition-colors">
                    <div className="px-4 py-2.5 flex items-center gap-2.5">
                      <Avatar initials={t.person.initials} tone={t.person.tone} size="sm" />
                      <div className="min-w-0">
                        <div className="font-semibold text-ink">{t.person.name} · <span className="text-ink-mute font-normal">{t.person.role}</span></div>
                        <div className="text-[10px] text-ink-mute truncate">{t.project.name}</div>
                      </div>
                    </div>
                    {t.hours.map((h, i) => (
                      <div key={i} className={cn(
                        "px-2 py-2.5 text-center font-mono",
                        h === 0           ? "text-ink-faint"          :
                        h > 9             ? "text-warn font-bold"     :
                        h > 0             ? "text-ink font-semibold"  :
                        "text-ink-faint",
                      )}>
                        {h === 0 ? "—" : h}
                      </div>
                    ))}
                    <div className={cn(
                      "px-2 py-2.5 text-right font-mono font-bold",
                      total === 0  ? "text-danger" :
                      total >= 40  ? "text-success" :
                      total > 0    ? "text-ink"    : "text-ink-mute"
                    )}>{total}h</div>
                    <div className="px-2 py-2.5">
                      <Badge variant={TS_TONE[t.status] as any} dot>{t.status[0].toUpperCase() + t.status.slice(1)}</Badge>
                    </div>
                  </button>
                )
              })}
            </div>
          </Card>

        </div>
      </div>

      {selected && <TimecardSheet entry={selected} onClose={() => setSelected(null)} />}
    </>
  )
}

function TimecardSheet({ entry, onClose }: { entry: TimeEntry; onClose: () => void }) {
  const total = entry.hours.reduce((a, b) => a + b, 0)
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div className="flex items-start gap-3">
          <Avatar initials={entry.person.initials} tone={entry.person.tone} size="lg" />
          <div>
            <div className="font-mono text-[10px] text-ink-mute">{entry.id}</div>
            <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{entry.person.name}</h2>
            <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
              <span>{entry.person.role}</span>
              <span>·</span>
              <Badge variant={TS_TONE[entry.status] as any} dot>{entry.status[0].toUpperCase() + entry.status.slice(1)}</Badge>
              {entry.billable ? <Badge variant="accent">Billable</Badge> : <Badge variant="neutral">Non-billable</Badge>}
            </div>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>
      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Project</div>
              <div className="text-[12.5px] font-semibold text-ink mt-0.5">{entry.project.name}</div>
              <div className="font-mono text-[10px] text-ink-mute mt-0.5">{entry.project.id}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Hours · week</div>
              <div className="font-serif text-xl text-ink mt-0.5">{total}h</div>
              <div className="text-[10px] text-ink-mute mt-0.5">{WEEK_LABEL.split(" · ")[1]}</div>
            </div>
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Task / WBS</div>
              <div className="text-[12px] text-ink-soft mt-0.5">{entry.task}</div>
            </div>
          </div>
        </section>

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Daily breakdown</div>
          <div className="bg-surface border border-divider rounded-md p-3">
            <div className="grid grid-cols-7 gap-1">
              {entry.hours.map((h, i) => (
                <div key={i} className="text-center">
                  <div className="text-[10px] text-ink-mute">{WEEK_DAYS[i]}</div>
                  <div className={cn("mt-1 h-12 rounded-md flex items-end justify-center pb-1 font-mono font-bold text-[12px]",
                    h === 0 ? "bg-cream-deep text-ink-faint" : h > 9 ? "bg-warn-soft/60 text-warn" : "bg-brand-soft text-brand"
                  )}>{h === 0 ? "—" : `${h}h`}</div>
                </div>
              ))}
            </div>
          </div>
        </section>

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

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Cost &amp; revenue impact</div>
          <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[12px]">
            <div className="px-3 py-2 flex items-center justify-between">
              <span className="text-ink-soft">Internal rate</span>
              <span className="font-mono font-semibold text-ink">₱{(entry.person.role === "PM" ? 1100 : entry.person.role === "Senior Eng" ? 1200 : 560).toLocaleString()} / h</span>
            </div>
            <div className="px-3 py-2 flex items-center justify-between">
              <span className="text-ink-soft">Cost · this week</span>
              <span className="font-mono font-semibold text-ink">₱{((entry.person.role === "PM" ? 1100 : entry.person.role === "Senior Eng" ? 1200 : 560) * total).toLocaleString()}</span>
            </div>
            <div className="px-3 py-2 flex items-center justify-between">
              <span className="text-ink-soft">Billable revenue</span>
              <span className={cn("font-mono font-semibold", entry.billable ? "text-success" : "text-ink-faint")}>
                {entry.billable ? `₱${((entry.person.role === "PM" ? 2400 : entry.person.role === "Senior Eng" ? 2800 : 1200) * total).toLocaleString()}` : "—"}
              </span>
            </div>
          </div>
        </section>
      </SheetBody>
      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        <Button variant="danger" leadingIcon="x" className="ml-auto">Reject</Button>
        <Button variant="primary" leadingIcon="check">Approve</Button>
      </SheetFooter>
    </Sheet>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/projects/wip.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/projects/wip.tsx
 * ----------------------------------------------------------------------------
 * Project Accounting — WIP &amp; Billing.
 *
 * Hero: total unbilled WIP, ready-to-bill, draft invoices, oldest unbilled.
 * Body: per-project WIP roll-up table with hours/cost/billing schedule status
 *       and a "Generate invoice" path. Slideout shows project P&L snapshot.
 * ============================================================================
 */

interface BillingMilestone {
  id: string
  project: { id: string; name: string }
  client: string
  description: string
  status: "draft-invoice" | "ready-to-bill" | "blocked" | "billed-this-cycle" | "scheduled"
  amount: number
  currency: "PHP" | "SGD" | "HKD" | "USD"
  scheduledFor?: string
  blockedReason?: string
  daysOld?: number   /* age of WIP for unbilled */
}

const BILLING: BillingMilestone[] = [
  /* Ready */
  { id: "BM-2026-014-04", project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" }, client: "Megaworld Corp",       description: "Milestone 4 · Floors 3-6 acceptance",    status: "ready-to-bill",     amount: 48000,  currency: "USD", daysOld: 2 },
  { id: "BM-2026-013-06", project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },    client: "Filinvest Land",       description: "T&M monthly + variance bill",            status: "ready-to-bill",     amount: 96000,  currency: "USD", daysOld: 14 },
  { id: "BM-2026-012-02", project: { id: "PRJ-2026-012", name: "Globe Telecom · Cebu Branch" },     client: "Globe Telecom",       description: "T&M April · 200hr",                       status: "ready-to-bill",     amount: 14400,  currency: "USD", daysOld: 8 },

  /* Draft */
  { id: "BM-2026-010-03", project: { id: "PRJ-2026-010", name: "Republic Cement · MEC rollout" },  client: "Republic Cement",     description: "Sprint 3 deliverables",                  status: "draft-invoice",     amount: 28800,  currency: "USD", daysOld: 4 },

  /* Blocked */
  { id: "BM-2026-013-05", project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },    client: "Filinvest Land",       description: "Variance request · Mar 2026",            status: "blocked",           amount: 84000,  currency: "USD", daysOld: 22, blockedReason: "Client requested invoice re-issue with new PO number" },

  /* Billed (this cycle) */
  { id: "BM-2026-014-03", project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" }, client: "Megaworld Corp",       description: "Milestone 3 · slab acceptance",          status: "billed-this-cycle", amount: 96000,  currency: "USD" },
  { id: "BM-2026-011-04", project: { id: "PRJ-2026-011", name: "SteelMark · Production audit" },   client: "SteelMark Industries", description: "Retainer · April",                       status: "billed-this-cycle", amount: 8000,   currency: "USD" },

  /* Scheduled (future) */
  { id: "BM-2026-014-05", project: { id: "PRJ-2026-014", name: "Megaworld · Marikina Tower B" }, client: "Megaworld Corp",       description: "Milestone 5 · Topping out",              status: "scheduled",         amount: 96000,  currency: "USD", scheduledFor: "May 22, 2026" },
  { id: "BM-2026-013-07", project: { id: "PRJ-2026-013", name: "Filinvest · Calamba Phase 2" },   client: "Filinvest Land",       description: "Final closeout · variance settlement",   status: "scheduled",         amount: 144000, currency: "USD", scheduledFor: "Jun 18, 2026" },
]
const BM_STATUS_LABEL: Record<BillingMilestone["status"], string> = {
  "ready-to-bill": "Ready to bill", "draft-invoice": "Draft invoice", blocked: "Blocked",
  "billed-this-cycle": "Billed", scheduled: "Scheduled",
}
const BM_STATUS_TONE: Record<BillingMilestone["status"], string> = {
  "ready-to-bill": "warn", "draft-invoice": "info", blocked: "danger",
  "billed-this-cycle": "success", scheduled: "neutral",
}

const symW  = (c: BillingMilestone["currency"]) => c === "PHP" ? "₱" : c === "SGD" ? "S$" : c === "HKD" ? "HK$" : "$"

function ProjectWIP() {
  const { push } = useRouter()
  const [view, setView] = React.useState<"list" | "by-project">("list")
  const [filter, setFilter] = React.useState<"all" | BillingMilestone["status"]>("all")
  const [selected, setSelected] = React.useState<BillingMilestone | null>(null)

  const filtered = filter === "all" ? BILLING : BILLING.filter(b => b.status === filter)

  /* Roll-ups (USD-eq) */
  const fxToUSD = (v: number, c: BillingMilestone["currency"]) => c === "PHP" ? v / 56.95 : c === "SGD" ? v / 1.35 : c === "HKD" ? v / 7.81 : v
  const wipTotal      = PROJECTS.reduce((s, p) => s + fxToUSD(p.wipUnbilled, p.currency), 0)
  const readyValue    = BILLING.filter(b => b.status === "ready-to-bill").reduce((s, b) => s + fxToUSD(b.amount, b.currency), 0)
  const draftValue    = BILLING.filter(b => b.status === "draft-invoice").reduce((s, b) => s + fxToUSD(b.amount, b.currency), 0)
  const billedCycle   = BILLING.filter(b => b.status === "billed-this-cycle").reduce((s, b) => s + fxToUSD(b.amount, b.currency), 0)
  const oldestUnbilled = BILLING.filter(b => b.status !== "billed-this-cycle" && b.status !== "scheduled" && b.daysOld != null).reduce((m, b) => Math.max(m, b.daysOld!), 0)

  /* Per project: WIP, billed YTD, completion */
  const byProject = PROJECTS.filter(p => p.status === "active" || p.status === "at-risk")

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Project Accounting" }, { label: "WIP / Billing" }]}
        leading={
          <ViewToggle value={view} onChange={(v) => setView(v as any)} options={[
            { value: "list",       label: "Billing queue", icon: "list" },
            { value: "by-project", label: "By project",    icon: "grid" },
          ]} />
        }
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export WIP</Button>
            <Button variant="primary" leadingIcon="plus">Generate Invoices · {BILLING.filter(b => b.status === "ready-to-bill").length}</Button>
          </>
        }
      />

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

          {/* KPIs */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Unbilled WIP"      value={`$${(wipTotal / 1000).toFixed(0)}k`}    sub="across 4 active projects"                  accent="brand" />
            <KpiTile label="Ready to bill"     value={`$${(readyValue / 1000).toFixed(0)}k`}  sub={`${BILLING.filter(b => b.status === "ready-to-bill").length} milestones`} accent="warn" />
            <KpiTile label="Billed · this cycle" value={`$${(billedCycle / 1000).toFixed(0)}k`} sub={`${BILLING.filter(b => b.status === "billed-this-cycle").length} invoices issued`} accent="success" />
            <KpiTile label="Oldest unbilled"   value={`${oldestUnbilled}d`}                    sub="WIP age · BM-2026-013-05"                  accent="danger" />
          </div>

          <AICallout
            title="Bill 3 milestones now · save ~$160k DSO"
            body="Megaworld M4 ($48k) and Globe Telecom April ($14.4k) are both blocker-free. Filinvest's variance request ($96k) has been ready for 2 weeks — the only hold is a PO re-issue you can request in 30 seconds. Generating all three now drops weighted DSO by ~5 days."
            matches={[
              { code: "BM-2026-014-04", name: "Megaworld M4 · $48k · ready 2d",      pct: 100 },
              { code: "BM-2026-012-02", name: "Globe Telecom April · $14.4k",        pct: 100 },
              { code: "BM-2026-013-06", name: "Filinvest April T&M · $96k · 14d old", pct: 95  },
            ]}
            dismissLabel="Hold for now"
            proceedLabel="Generate all 3"
          />

          {view === "list" ? (
            <>
              <div className="flex items-center gap-2 flex-wrap">
                <FilterChip active={filter === "all"}                onClick={() => setFilter("all")}>All</FilterChip>
                <FilterChip active={filter === "ready-to-bill"}      onClick={() => setFilter("ready-to-bill")} tone="warn">Ready to bill</FilterChip>
                <FilterChip active={filter === "draft-invoice"}      onClick={() => setFilter("draft-invoice")} tone="info">Draft</FilterChip>
                <FilterChip active={filter === "blocked"}            onClick={() => setFilter("blocked")} tone="danger">Blocked</FilterChip>
                <FilterChip active={filter === "billed-this-cycle"}  onClick={() => setFilter("billed-this-cycle")} tone="success">Billed</FilterChip>
                <FilterChip active={filter === "scheduled"}          onClick={() => setFilter("scheduled")}>Scheduled</FilterChip>
                <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {BILLING.length}</span>
              </div>

              <Card>
                <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">Milestone</th>
                      <th>Project</th>
                      <th>Client</th>
                      <th>Description</th>
                      <th>Status</th>
                      <th className="text-right">Amount</th>
                      <th>Age / schedule</th>
                      <th></th>
                    </tr>
                  </thead>
                  <tbody>
                    {filtered.map(b => (
                      <tr key={b.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(b)}>
                        <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{b.id}</td>
                        <td>
                          <div className="font-semibold text-ink truncate max-w-[220px]">{b.project.name}</div>
                          <div className="font-mono text-[10px] text-ink-mute">{b.project.id}</div>
                        </td>
                        <td className="text-ink-soft">{b.client}</td>
                        <td className="text-ink-soft max-w-[260px] truncate">{b.description}</td>
                        <td>
                          <Badge variant={BM_STATUS_TONE[b.status] as any} dot>{BM_STATUS_LABEL[b.status]}</Badge>
                          {b.blockedReason && <div className="text-[10px] text-danger mt-0.5 max-w-[200px] leading-tight">{b.blockedReason}</div>}
                        </td>
                        <td className="text-right font-mono font-semibold text-ink">{symW(b.currency)}{b.amount.toLocaleString()}</td>
                        <td className="text-[11px] text-ink-soft">
                          {b.daysOld != null && <span className={cn(b.daysOld > 10 ? "text-danger font-semibold" : b.daysOld > 5 ? "text-warn" : "")}>{b.daysOld}d old</span>}
                          {b.scheduledFor && <span>{b.scheduledFor}</span>}
                        </td>
                        <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </Card>
            </>
          ) : (
            /* By-project WIP roll-up */
            <Card>
              <CardHeader>
                <CardTitle>Active project WIP roll-up</CardTitle>
                <span className="text-[10.5px] text-ink-mute">USD-equivalent · all currencies</span>
              </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">Project</th>
                    <th>Client</th>
                    <th className="text-right">Budget</th>
                    <th className="text-right">Spent</th>
                    <th className="text-right">Billed</th>
                    <th className="text-right">WIP</th>
                    <th className="text-right">Margin %</th>
                    <th>Completion</th>
                    <th></th>
                  </tr>
                </thead>
                <tbody>
                  {byProject.map(p => (
                    <tr key={p.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => push("/projects/portfolio")}>
                      <td className="px-4 py-2.5">
                        <div className="font-semibold text-ink">{p.name}</div>
                        <div className="font-mono text-[10px] text-ink-mute">{p.id} · {p.type}</div>
                      </td>
                      <td className="text-ink-soft">{p.client}</td>
                      <td className="text-right font-mono text-ink-soft">{symW(p.currency)}{(p.budget / 1000).toFixed(0)}k</td>
                      <td className="text-right font-mono text-ink-soft">{symW(p.currency)}{(p.spent / 1000).toFixed(0)}k</td>
                      <td className="text-right font-mono text-ink-soft">{symW(p.currency)}{(p.billed / 1000).toFixed(0)}k</td>
                      <td className={cn("text-right font-mono font-semibold", p.wipUnbilled > 0 ? "text-warn" : "text-ink-faint")}>
                        {p.wipUnbilled > 0 ? `${symW(p.currency)}${(p.wipUnbilled / 1000).toFixed(0)}k` : "—"}
                      </td>
                      <td className={cn("text-right font-mono font-semibold", p.marginPct >= 25 ? "text-success" : p.marginPct >= 15 ? "text-warn" : "text-danger")}>{p.marginPct}%</td>
                      <td>
                        <div className="flex items-center gap-2">
                          <div className="h-1.5 w-24 bg-cream-deep rounded-full overflow-hidden">
                            <div className={cn("h-full", p.completionPct >= p.marginPct ? "bg-success" : "bg-brand")} style={{ width: `${p.completionPct}%` }} />
                          </div>
                          <span className="text-[10.5px] font-mono text-ink-soft">{p.completionPct}%</span>
                        </div>
                      </td>
                      <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </Card>
          )}

        </div>
      </div>

      {selected && <MilestoneSheet milestone={selected} onClose={() => setSelected(null)} push={push} />}
    </>
  )
}

function MilestoneSheet({ milestone, onClose, push }: { milestone: BillingMilestone; onClose: () => void; push: (r: string) => void }) {
  const project = PROJECTS.find(p => p.id === milestone.project.id)
  return (
    <Sheet open={true} onOpenChange={onClose} side="right">
      <SheetHeader>
        <div>
          <div className="font-mono text-[10px] text-ink-mute">{milestone.id}</div>
          <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{milestone.description}</h2>
          <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
            <Badge variant={BM_STATUS_TONE[milestone.status] as any} dot>{BM_STATUS_LABEL[milestone.status]}</Badge>
            <span>·</span><span>{milestone.client}</span>
          </div>
        </div>
        <SheetClose onClick={onClose} />
      </SheetHeader>
      <SheetBody>
        <section>
          <div className="grid grid-cols-3 gap-3">
            <div className="bg-cream/60 border border-divider rounded-md p-3">
              <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Amount</div>
              <div className="font-serif text-xl text-ink mt-0.5">{symW(milestone.currency)}{milestone.amount.toLocaleString()}</div>
              <div className="text-[10px] text-ink-mute mt-0.5">{milestone.currency}</div>
            </div>
            {milestone.daysOld != null && (
              <div className="bg-cream/60 border border-divider rounded-md p-3">
                <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">WIP age</div>
                <div className={cn("font-serif text-xl mt-0.5", milestone.daysOld > 10 ? "text-danger" : milestone.daysOld > 5 ? "text-warn" : "text-ink")}>{milestone.daysOld}d</div>
              </div>
            )}
            {milestone.scheduledFor && (
              <div className="bg-cream/60 border border-divider rounded-md p-3">
                <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Scheduled for</div>
                <div className="font-serif text-xl text-ink mt-0.5">{milestone.scheduledFor.split(",")[0]}</div>
              </div>
            )}
          </div>
        </section>

        {milestone.blockedReason && (
          <section className="mt-5">
            <div className="bg-danger-soft border border-danger/30 rounded-md p-3">
              <div className="text-[10px] uppercase tracking-wider font-bold text-danger mb-1">Blocked</div>
              <div className="text-[11.5px] text-ink-soft">{milestone.blockedReason}</div>
            </div>
          </section>
        )}

        {project && (
          <section className="mt-5">
            <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Project P&amp;L · to date</div>
            <div className="bg-surface border border-divider rounded-md divide-y divide-divider-soft text-[12px]">
              <PLRow label="Budget"        value={`${symW(project.currency)}${project.budget.toLocaleString()}`} />
              <PLRow label="Spent (cost)"  value={`${symW(project.currency)}${project.spent.toLocaleString()}`} />
              <PLRow label="Billed"        value={`${symW(project.currency)}${project.billed.toLocaleString()}`} />
              <PLRow label="Unbilled WIP"  value={`${symW(project.currency)}${project.wipUnbilled.toLocaleString()}`} accent="warn" />
              <PLRow label="Forecast margin" value={`${project.marginPct}%`} accent={project.marginPct >= 25 ? "success" : project.marginPct >= 15 ? "warn" : "danger"} />
            </div>
          </section>
        )}

        <section className="mt-5">
          <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Linked records</div>
          <div className="bg-surface border border-divider rounded-md text-[12px]">
            <button onClick={() => { onClose(); push("/projects/portfolio") }} className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-cream/40 border-b border-divider-soft">
              <div className="flex items-center gap-2"><Icon name="grid" size={12} className="text-ink-mute" /><span className="text-ink-soft">Project</span><span className="font-semibold text-ink">{milestone.project.name}</span></div>
              <Icon name="arrow-right" size={12} className="text-ink-faint" />
            </button>
            <button onClick={() => { onClose(); push("/projects/time") }} className="w-full px-3 py-2.5 flex items-center justify-between hover:bg-cream/40">
              <div className="flex items-center gap-2"><Icon name="clock" size={12} className="text-ink-mute" /><span className="text-ink-soft">Underlying timesheets · this period</span></div>
              <Icon name="arrow-right" size={12} className="text-ink-faint" />
            </button>
          </div>
        </section>
      </SheetBody>
      <SheetFooter>
        <Button variant="ghost" onClick={onClose}>Close</Button>
        {milestone.status === "ready-to-bill"  && <Button variant="primary" leadingIcon="dollar" className="ml-auto">Generate invoice</Button>}
        {milestone.status === "draft-invoice"  && <Button variant="primary" leadingIcon="mail"   className="ml-auto">Send to customer</Button>}
        {milestone.status === "blocked"        && <Button variant="primary" leadingIcon="mail"   className="ml-auto">Request unblock</Button>}
      </SheetFooter>
    </Sheet>
  )
}

function PLRow({ label, value, accent }: any) {
  const tones: Record<string, string> = { success: "text-success", warn: "text-warn", danger: "text-danger" }
  return (
    <div className="px-3 py-2 flex items-center justify-between">
      <span className="text-ink-soft">{label}</span>
      <span className={cn("font-mono font-semibold", accent ? tones[accent] : "text-ink")}>{value}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/project-mgmt/projects.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/project-mgmt/projects.tsx
 * Project Management — Projects list. The operational view (not the money).
 */

function PMProjects() {
  const [view, setView] = React.useState<"cards" | "list">("cards")
  const [statusFilter, setStatusFilter] = React.useState<string>("active")
  const [healthFilter, setHealthFilter] = React.useState<string>("all")
  const [search, setSearch] = React.useState("")
  const projs = (PM_PROJECTS as any[])

  let filtered = statusFilter === "all" ? projs : projs.filter(p => p.status === statusFilter)
  if (healthFilter !== "all") filtered = filtered.filter(p => p.health === healthFilter)
  if (search.trim()) {
    const q = search.toLowerCase()
    filtered = filtered.filter(p => p.name.toLowerCase().includes(q) || p.client.toLowerCase().includes(q) || p.id.toLowerCase().includes(q))
  }

  const k = PM_KPIS as any

  return (
    <>
      <TopBar breadcrumb={[{ label: "Project Management" }, { label: "Projects" }]} />
      <ActionBar
        title="Projects"
        status={`${k.active} active · ${k.planning} planning · ${k.red} red · ${k.amber} amber`}
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search project, client…" className="w-[280px]" />}
        view={<ListCardToggle value={view} onChange={setView} />}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New project</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Risk scan</Button>}
        filters={
          <>
            <FilterChip active={statusFilter === "active"} onClick={() => setStatusFilter("active")} count={k.active}>Active</FilterChip>
            <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")} count={projs.length}>All</FilterChip>
            <FilterChip active={statusFilter === "planning"} onClick={() => setStatusFilter("planning")} count={k.planning}>Planning</FilterChip>
            <FilterChip active={statusFilter === "on-hold"} onClick={() => setStatusFilter("on-hold")} count={k.onHold}>On Hold</FilterChip>
            <FilterChip active={statusFilter === "complete"} onClick={() => setStatusFilter("complete")} count={k.complete}>Complete</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={healthFilter === "red"} onClick={() => setHealthFilter(healthFilter === "red" ? "all" : "red")} tone="danger" count={k.red}>Red</FilterChip>
            <FilterChip active={healthFilter === "amber"} onClick={() => setHealthFilter(healthFilter === "amber" ? "all" : "amber")} count={k.amber}>Amber</FilterChip>
            <FilterChip active={healthFilter === "green"} onClick={() => setHealthFilter(healthFilter === "green" ? "all" : "green")} count={k.green}>Green</FilterChip>
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6">
          {/* KPI strip */}
          <div className="grid grid-cols-[1.5fr_1fr_1fr_1fr_1fr] gap-3 mb-4">
            <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">Portfolio</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">${k.budgetTotalM.toFixed(1)}M</div>
              <div className="text-[12px] text-white/70 mt-1.5">Across {projs.length} projects · {Math.round(k.burnRatePct * 100)}% spent</div>
            </div>
            <KpiTile label="Active"     value={k.active}   sub="In flight"           accent="info" />
            <KpiTile label="Milestones" value={k.upcomingMilestones} sub="Next 14 days" accent="warn" />
            <KpiTile label="Overdue tasks" value={k.overdueTasks} sub="Need attention" accent="danger" />
            <KpiTile label="Health" value={`${k.green}/${k.amber}/${k.red}`} sub="green / amber / red" accent="success" />
          </div>

          {view === "cards" ? (
            <div className="grid grid-cols-2 lg:grid-cols-3 gap-3">
              {filtered.map(p => <PMProjectCard key={p.id} p={p} />)}
            </div>
          ) : (
            <div className="bg-surface border border-divider rounded-lg overflow-hidden">
              <div className="grid grid-cols-[120px_1.6fr_1fr_140px_1fr_120px_80px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
                <span>ID</span><span>Project</span><span>Client</span><span>PM</span><span>Progress</span><span>Budget</span><span></span>
              </div>
              {filtered.map(p => (
                <div key={p.id} className="grid grid-cols-[120px_1.6fr_1fr_140px_1fr_120px_80px] px-4 py-2.5 items-center border-t border-divider-soft hover:bg-cream/40 text-[11.5px]">
                  <span className="font-mono text-[10.5px] font-bold text-brand-mid">{p.id}</span>
                  <span>
                    <div className="text-ink font-semibold flex items-center gap-1.5">
                      <span className={cn("size-2 rounded-full", p.health === "red" ? "bg-danger" : p.health === "amber" ? "bg-warn" : "bg-success")} />
                      {p.name}
                    </div>
                    <div className="text-[10px] text-ink-mute">{p.entity} · {p.status}</div>
                  </span>
                  <span className="text-ink-soft">{p.client}</span>
                  <div className="flex items-center gap-1.5"><Avatar initials={p.pmInitials} tone="sand" size="xs" />{p.pm}</div>
                  <div>
                    <div className="h-1.5 bg-cream rounded-full overflow-hidden">
                      <div className="h-full bg-brand" style={{ width: `${p.progress * 100}%` }} />
                    </div>
                    <div className="text-[10px] text-ink-mute mt-1 font-mono">{Math.round(p.progress * 100)}% · {p.tasksDone}/{p.tasksTotal} tasks</div>
                  </div>
                  <span className="font-mono">${(p.spentUsd / 1000).toFixed(0)}k / ${(p.budgetUsd / 1000).toFixed(0)}k</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>
    </>
  )
}

function PMProjectCard({ p }: { p: any }) {
  return (
    <Card className="hover:shadow-md transition-shadow cursor-pointer">
      <div className="p-4">
        <div className="flex items-center gap-2 mb-2">
          <span className={cn("size-2.5 rounded-full",
            p.health === "red" ? "bg-danger animate-pulse" : p.health === "amber" ? "bg-warn" : "bg-success")} />
          <span className="font-mono text-[10px] font-bold text-brand-mid">{p.id}</span>
          <span className="ml-auto">
            {p.status === "active"   && <Badge variant="success">Active</Badge>}
            {p.status === "planning" && <Badge variant="info">Planning</Badge>}
            {p.status === "on-hold"  && <Badge variant="warn">On hold</Badge>}
            {p.status === "complete" && <Badge variant="neutral">Complete</Badge>}
          </span>
        </div>
        <div className="text-[14px] text-ink font-semibold leading-tight">{p.name}</div>
        <div className="text-[11px] text-ink-mute mt-0.5">{p.client} · {p.entity}</div>

        <div className="mt-3">
          <div className="flex items-center justify-between mb-1">
            <span className="text-[10px] uppercase tracking-wider font-bold text-ink-mute">Progress</span>
            <span className="text-[10px] font-mono font-bold text-ink">{Math.round(p.progress * 100)}%</span>
          </div>
          <div className="h-1.5 bg-cream rounded-full overflow-hidden">
            <div className={cn("h-full", p.health === "red" ? "bg-danger" : p.health === "amber" ? "bg-warn" : "bg-brand")} style={{ width: `${p.progress * 100}%` }} />
          </div>
        </div>

        <div className="grid grid-cols-2 gap-3 mt-3 text-[10.5px]">
          <div>
            <div className="text-ink-mute">Tasks</div>
            <div className="font-mono font-bold text-ink">{p.tasksDone}/{p.tasksTotal}</div>
          </div>
          <div>
            <div className="text-ink-mute">Burn</div>
            <div className="font-mono font-bold text-ink">${(p.spentUsd / 1000).toFixed(0)}k / ${(p.budgetUsd / 1000).toFixed(0)}k</div>
          </div>
        </div>

        <div className="mt-3 pt-3 border-t border-divider-soft flex items-center gap-2">
          <Avatar initials={p.pmInitials} tone="sand" size="sm" />
          <div className="text-[10.5px] text-ink-soft flex-1 min-w-0">
            <div className="truncate">{p.pm}</div>
            <div className="text-[9.5px] text-ink-mute">PM · ends {p.endDate}</div>
          </div>
        </div>

        <div className="mt-2.5 px-2.5 py-1.5 bg-cream rounded text-[10.5px] flex items-center gap-1.5">
          <Icon name="flag" size={10} className="text-accent-deep" />
          <span className="text-ink-soft truncate">{p.milestonesNext}</span>
        </div>
      </div>
    </Card>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/project-mgmt/tasks.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/project-mgmt/tasks.tsx
 * PM — task kanban grouped by status.
 */

function PMTasks() {
  const tasks = (PM_TASKS as any[])
  const [projectFilter, setProjectFilter] = React.useState<string>("all")
  const [search, setSearch] = React.useState("")
  let filtered = projectFilter === "all" ? tasks : tasks.filter(t => t.projectId === projectFilter)
  if (search.trim()) {
    const q = search.toLowerCase()
    filtered = filtered.filter(t => t.title.toLowerCase().includes(q) || t.assignee.toLowerCase().includes(q))
  }
  const cols = [
    { id: "todo",        label: "To do",        tone: "neutral" },
    { id: "in-progress", label: "In progress",  tone: "info"    },
    { id: "blocked",     label: "Blocked",      tone: "danger"  },
    { id: "review",      label: "In review",    tone: "warn"    },
    { id: "done",        label: "Done",         tone: "success" },
  ]
  const projects = ["all", ...Array.from(new Set(tasks.map(t => t.projectId)))]

  return (
    <>
      <TopBar breadcrumb={[{ label: "Project Management" }, { label: "Tasks & Milestones" }]} />
      <ActionBar
        title="Tasks & Milestones"
        status={`${tasks.length} tasks across ${new Set(tasks.map(t => t.projectId)).size} projects · ${tasks.filter(t => t.status === "blocked").length} blocked`}
        search={<SearchBox value={search} onChange={setSearch} placeholder="Search tasks…" className="w-[260px]" />}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New task</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">Project</span>
            {projects.map(p => (
              <FilterChip key={p} active={projectFilter === p} onClick={() => setProjectFilter(p)}>{p === "all" ? "All" : p}</FilterChip>
            ))}
          </>
        }
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="p-6">
          <div className="grid grid-cols-5 gap-3 min-w-[1120px]">
            {cols.map(c => {
              const items = filtered.filter(t => t.status === c.id)
              return (
                <div key={c.id} className="bg-cream/40 rounded-lg p-2.5">
                  <div className="flex items-center justify-between px-1.5 py-1 mb-2">
                    <div className="flex items-center gap-1.5">
                      <span className={cn("size-2 rounded-full",
                        c.id === "blocked" ? "bg-danger" : c.id === "in-progress" ? "bg-info" : c.id === "review" ? "bg-warn" : c.id === "done" ? "bg-success" : "bg-ink-faint"
                      )} />
                      <span className="text-[10px] font-bold uppercase tracking-wider text-ink-mute">{c.label}</span>
                    </div>
                    <span className="text-[10.5px] font-mono font-bold text-ink-mute">{items.length}</span>
                  </div>
                  <div className="space-y-2">
                    {items.map(t => <PMTaskCard key={t.id} t={t} />)}
                    <InlineAddButton label={`+ Add task to ${c.label}`} />
                  </div>
                </div>
              )
            })}
          </div>
        </div>
      </div>
    </>
  )
}

function PMTaskCard({ t }: { t: any }) {
  const proj = (PM_PROJECTS as any[]).find(p => p.id === t.projectId)
  return (
    <div className="bg-surface border border-divider rounded-md p-2.5 hover:shadow-sm cursor-pointer">
      <div className="flex items-center gap-1.5 mb-1.5">
        <span className="font-mono text-[9px] text-brand-mid">{t.id}</span>
        <span className="text-[9px] text-ink-mute">· {proj?.id.slice(-3) ?? ""}</span>
        <span className="ml-auto">
          {t.priority === "urgent" && <Badge variant="danger">⚡ Urgent</Badge>}
          {t.priority === "high" && <Badge variant="warn">High</Badge>}
          {t.priority === "med" && <Badge variant="neutral">Med</Badge>}
          {t.priority === "low" && <Badge variant="neutral">Low</Badge>}
        </span>
      </div>
      <div className="text-[12px] text-ink font-semibold leading-tight">{t.title}</div>
      <div className="text-[10px] text-ink-mute mt-1">{proj?.name}</div>
      <div className="flex items-center justify-between mt-2.5 pt-2 border-t border-divider-soft">
        <div className="flex items-center gap-1.5">
          <Avatar initials={t.assigneeInitials} tone="sand" size="xs" />
          <span className="text-[10.5px] text-ink-mute">{t.assignee}</span>
        </div>
        <span className="text-[10px] font-mono text-ink-mute">{t.due}</span>
      </div>
      <div className="mt-1.5 flex items-center gap-1.5">
        <div className="flex-1 h-1 bg-cream rounded-full overflow-hidden">
          <div className="h-full bg-brand" style={{ width: `${Math.min(1, t.hoursLogged / t.hoursPlanned) * 100}%` }} />
        </div>
        <span className="text-[9.5px] font-mono text-ink-mute">{t.hoursLogged}/{t.hoursPlanned}h</span>
      </div>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/project-mgmt/schedule.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/project-mgmt/schedule.tsx
 * PM — Schedule view (gantt-style timeline of projects + milestones).
 */

function PMSchedule() {
  const projs = (PM_PROJECTS as any[]).filter(p => p.status !== "complete")
  /* X axis: 2026 Jan – Dec */
  const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  const todayMonth = 4  /* May = index 4 */

  function monthOf(dateStr: string): number | null {
    const m = dateStr.match(/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i)
    if (!m) return null
    return months.findIndex(x => x === m[1].slice(0, 3))
  }

  return (
    <>
      <TopBar breadcrumb={[{ label: "Project Management" }, { label: "Schedule" }]} />
      <ActionBar
        title="Schedule"
        status="Portfolio gantt · 2026 calendar year · 14 upcoming milestones"
        primary={<Button variant="primary" leadingIcon="plus" size="sm">New project</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export gantt</Button>}
      />

      <div className="flex-1 overflow-auto bg-app">
        <div className="max-w-[1440px] mx-auto p-6">
          <div className="bg-surface border border-divider rounded-lg overflow-hidden">
            {/* Month headers */}
            <div className="grid grid-cols-[300px_repeat(12,1fr)] border-b border-divider bg-cream">
              <div className="px-3 py-2 text-[10px] uppercase tracking-wider font-bold text-ink-mute border-r border-divider">Project</div>
              {months.map((m, i) => (
                <div key={m} className={cn(
                  "py-2 text-[10px] uppercase tracking-wider font-bold text-center border-r border-divider-soft last:border-r-0",
                  i === todayMonth ? "text-brand bg-brand-soft" : "text-ink-mute"
                )}>
                  {m}
                  {i === todayMonth && <div className="text-[8px] text-brand-mid mt-0.5">← Today</div>}
                </div>
              ))}
            </div>

            {/* Rows */}
            {projs.map((p, i) => {
              const start = monthOf(p.startDate) ?? 0
              const end = monthOf(p.endDate) ?? 11
              const len = Math.max(1, end - start + 1)
              const tone = p.health === "red" ? "bg-danger/80" : p.health === "amber" ? "bg-warn/80" : "bg-brand/80"
              return (
                <div key={p.id} className="grid grid-cols-[300px_repeat(12,1fr)] border-t border-divider-soft items-center min-h-[44px] hover:bg-cream/30">
                  {/* Sticky project info */}
                  <div className="px-3 py-2.5 border-r border-divider">
                    <div className="flex items-center gap-1.5">
                      <span className={cn("size-2 rounded-full",
                        p.health === "red" ? "bg-danger" : p.health === "amber" ? "bg-warn" : "bg-success")} />
                      <span className="text-[10.5px] font-mono font-bold text-brand-mid">{p.id}</span>
                    </div>
                    <div className="text-[12px] text-ink font-semibold leading-tight mt-0.5">{p.name}</div>
                    <div className="text-[10px] text-ink-mute">{p.client} · {p.pm}</div>
                  </div>

                  {/* Gantt cells */}
                  <div className="col-span-12 relative h-full px-1">
                    {/* Bar */}
                    <div className="absolute top-1/2 -translate-y-1/2 h-5 rounded"
                      style={{ left: `${(start / 12) * 100}%`, width: `${(len / 12) * 100}%` }}>
                      <div className={cn("h-full rounded relative overflow-hidden border border-black/10", tone)}>
                        <div className="absolute inset-y-0 left-0 bg-white/30" style={{ width: `${p.progress * 100}%` }} />
                        <div className="absolute inset-0 px-2 flex items-center text-[10px] text-white font-bold">
                          {Math.round(p.progress * 100)}%
                        </div>
                      </div>
                    </div>
                    {/* Milestone marker (use end date as terminal milestone) */}
                    <span title={p.milestonesNext}
                      className="absolute top-1/2 -translate-y-1/2 size-3 bg-accent rounded-sm rotate-45 ring-2 ring-surface"
                      style={{ left: `calc(${(Math.min(start + Math.round(p.progress * len), 11) / 12) * 100}% + 14px)` }} />
                  </div>
                </div>
              )
            })}
          </div>

          {/* Legend */}
          <div className="mt-3 flex items-center gap-5 text-[10.5px] text-ink-mute">
            <div className="flex items-center gap-1.5"><span className="w-5 h-2 rounded bg-brand/80" /> Green project</div>
            <div className="flex items-center gap-1.5"><span className="w-5 h-2 rounded bg-warn/80" /> Amber</div>
            <div className="flex items-center gap-1.5"><span className="w-5 h-2 rounded bg-danger/80" /> Red</div>
            <div className="flex items-center gap-1.5"><span className="size-2 bg-accent rotate-45" /> Upcoming milestone</div>
            <div className="ml-auto flex items-center gap-1"><span className="size-2 rounded-full bg-brand-soft" /> Today column highlighted</div>
          </div>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/project-mgmt/resources.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * apps/web/components/project-mgmt/resources.tsx
 * PM — Resources view (people, allocation, utilization).
 */

function PMResources() {
  const res = (PM_RESOURCES as any[])
  const overAllocated = res.filter(r => r.utilization > 1)
  const underUtilized = res.filter(r => r.utilization < 0.7)
  return (
    <>
      <TopBar breadcrumb={[{ label: "Project Management" }, { label: "Resources" }]} />
      <ActionBar
        title="Resource Allocation"
        status={`${res.length} people · ${overAllocated.length} over-allocated · ${underUtilized.length} under-utilized`}
        primary={<Button variant="primary" leadingIcon="plus" size="sm">Add allocation</Button>}
        secondary={<Button variant="ghost" leadingIcon="download" size="sm">Export</Button>}
        ai={<Button variant="subtle" leadingIcon="sparkle" size="sm">Suggest rebalancing</Button>}
      />

      <div className="flex-1 overflow-y-auto bg-app">
        <div className="max-w-[1280px] mx-auto p-6 space-y-4">
          {/* KPI strip */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Avg utilization" value={`${Math.round(res.reduce((s, r) => s + r.utilization, 0) / res.length * 100)}%`} sub="Across all PMs" accent="info" />
            <KpiTile label="Over-allocated" value={String(overAllocated.length)} sub="> 100% capacity" accent="danger" />
            <KpiTile label="Under-utilized" value={String(underUtilized.length)} sub="< 70% capacity" accent="warn" />
            <KpiTile label="Total capacity" value={`${res.reduce((s, r) => s + r.capacityHrs, 0)}h`} sub="Weekly" accent="success" />
          </div>

          <div className="bg-surface border border-divider rounded-lg overflow-hidden">
            <div className="grid grid-cols-[1.5fr_140px_80px_2fr_180px] px-4 py-2 bg-cream text-[10px] uppercase tracking-wider font-bold text-ink-mute">
              <span>Person</span><span>Role</span><span>Entity</span><span>Utilization · Capacity</span><span>Assigned projects</span>
            </div>
            {res.map(r => (
              <div key={r.name} className="grid grid-cols-[1.5fr_140px_80px_2fr_180px] px-4 py-3 items-center border-t border-divider-soft hover:bg-cream/40 text-[11.5px]">
                <div className="flex items-center gap-2.5">
                  <Avatar initials={r.initials} tone="sand" size="md" />
                  <div className="min-w-0">
                    <div className="text-ink font-semibold">{r.name}</div>
                    <div className="text-[10px] text-ink-mute">{r.assignedHrs}h / {r.capacityHrs}h this week</div>
                  </div>
                </div>
                <span className="text-ink-soft">{r.role}</span>
                <span className="font-mono text-ink-mute">{r.entity}</span>
                <div>
                  <div className="flex items-center gap-2">
                    <div className="flex-1 h-2 bg-cream rounded-full overflow-hidden">
                      <div className={cn(
                        "h-full transition-all",
                        r.utilization > 1 ? "bg-danger" : r.utilization > 0.85 ? "bg-warn" : r.utilization < 0.7 ? "bg-info" : "bg-success"
                      )} style={{ width: `${Math.min(r.utilization, 1.5) / 1.5 * 100}%` }} />
                    </div>
                    <span className={cn("font-mono font-bold w-12 text-right",
                      r.utilization > 1 ? "text-danger" : r.utilization < 0.7 ? "text-warn" : "text-ink"
                    )}>{Math.round(r.utilization * 100)}%</span>
                  </div>
                  {r.utilization > 1 && <div className="text-[9.5px] text-danger font-semibold mt-0.5">⚠ Over-allocated by {Math.round((r.utilization - 1) * r.capacityHrs)}h</div>}
                </div>
                <div className="space-y-0.5">
                  {r.projects.map((p: any, i: number) => (
                    <div key={i} className="text-[10.5px] flex items-center gap-1.5">
                      <span className="font-mono text-brand-mid">{p.id.slice(-3)}</span>
                      <span className="text-ink-soft truncate flex-1">{p.name}</span>
                      <span className="font-mono text-ink-mute">{p.hours}h</span>
                    </div>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/assets/register.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/assets/register.tsx
 * ----------------------------------------------------------------------------
 * Fixed Assets hero — Asset Register. Table of fixed assets with category
 * facets, lifecycle status, depreciation snapshot, and total NBV roll-up.
 * ============================================================================
 */

function AssetBranchToggle({ current }: { current: "fixed" | "intangible" }) {
  const { push } = useRouter()
  const opts = [
    { value: "fixed",      label: "Fixed",      icon: "package", href: "/assets/register" },
    { value: "intangible", label: "Intangible", icon: "award",   href: "/assets/intangible" },
  ]
  return (
    <div className="inline-flex items-center gap-0.5 rounded-md bg-cream p-0.5 border border-divider">
      {opts.map(o => (
        <button key={o.value} onClick={() => { if (o.value !== current) push(o.href) }} className={cn("h-7 px-3 rounded text-[11.5px] font-semibold inline-flex items-center gap-1.5 transition-colors", current === o.value ? "bg-surface text-brand shadow-sm" : "text-ink-mute hover:text-ink-soft")}>
          <Icon name={o.icon as any} size={12} />{o.label}
        </button>
      ))}
    </div>
  )
}

function AssetRegister() {
  const { push } = useRouter()
  const [statusFilter, setStatusFilter] = React.useState<"all" | FixedAsset["status"]>("all")
  const [categoryFilter, setCategoryFilter] = React.useState<string | null>(null)
  const [search, setSearch] = React.useState("")

  const categories = Array.from(new Set(FIXED_ASSETS.map(f => f.category)))

  const filtered = React.useMemo(() => {
    let r = FIXED_ASSETS
    if (statusFilter !== "all") r = r.filter(f => f.status === statusFilter)
    if (categoryFilter)         r = r.filter(f => f.category === categoryFilter)
    if (search.trim()) {
      const q = search.toLowerCase()
      r = r.filter(f => f.name.toLowerCase().includes(q) || f.id.toLowerCase().includes(q) || (f.serial?.toLowerCase().includes(q) ?? false))
    }
    return r
  }, [statusFilter, categoryFilter, search])

  const totalCost = FIXED_ASSETS.reduce((s, f) => s + f.cost, 0)
  const totalNBV  = FIXED_ASSETS.reduce((s, f) => s + f.nbv, 0)
  const totalDepr = FIXED_ASSETS.reduce((s, f) => s + f.accumDepr, 0)
  const inUse = FIXED_ASSETS.filter(f => f.status === "in-use").length
  const warrantyExpiring = FIXED_ASSETS.filter(f => {
    if (!f.warrantyEnd) return false
    const end = new Date(f.warrantyEnd)
    const days = (end.getTime() - new Date("2026-04-17").getTime()) / (1000 * 60 * 60 * 24)
    return days > 0 && days < 90
  }).length

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Assets" }, { label: "Fixed Assets" }, { label: "Asset Register" }]}
        leading={<AssetBranchToggle current="fixed" />}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="upload">Bulk import</Button>
            <Button variant="primary" leadingIcon="plus">New Asset</Button>
          </>
        }
      />

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

          {/* KPI strip */}
          <div className="grid grid-cols-[2fr_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">Net Book Value · Apr 17, 2026</div>
              <div className="font-serif text-[36px] leading-none mt-1.5">₱{(totalNBV / 1000000).toFixed(2)}M</div>
              <div className="text-[12px] text-white/70 mt-1.5">
                {FIXED_ASSETS.length} assets · ₱{(totalCost / 1000000).toFixed(2)}M acquisition · ₱{(totalDepr / 1000000).toFixed(2)}M depreciated
              </div>
            </div>
            <KpiTile label="In use" value={inUse.toString()} sub={`${FIXED_ASSETS.length - inUse} idle/retired`} accent="success" />
            <KpiTile label="This month" value="₱62.4k" sub="Depreciation expense" accent="accent" />
            <KpiTile label="Warranty soon" value={warrantyExpiring.toString()} sub="Expiring < 90d" accent={warrantyExpiring > 0 ? "warn" : "neutral"} />
          </div>

          {/* Filters */}
          <div className="flex items-center justify-between gap-3 flex-wrap">
            <div className="flex items-center gap-1.5 flex-wrap">
              <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")} count={FIXED_ASSETS.length}>All</FilterChip>
              {(["in-use", "idle", "under-maintenance", "retired"] as const).map(s => {
                const c = FIXED_ASSETS.filter(f => f.status === s).length
                if (c === 0) return null
                return <FilterChip key={s} active={statusFilter === s} onClick={() => setStatusFilter(s)} count={c} tone={FA_STATUS_TONE[s] as any}>{FA_STATUS_LABEL[s]}</FilterChip>
              })}
              <span className="w-px h-5 bg-divider mx-1" />
              <FilterChip active={categoryFilter === null} onClick={() => setCategoryFilter(null)}>Any category</FilterChip>
              {categories.map(c => {
                const count = FIXED_ASSETS.filter(f => f.category === c).length
                return <FilterChip key={c} active={categoryFilter === c} onClick={() => setCategoryFilter(c)} count={count}>{c.split(" & ")[0]}</FilterChip>
              })}
            </div>
            <div className="relative">
              <input
                value={search} onChange={e => setSearch(e.target.value)}
                placeholder="Search asset, serial…"
                className="w-64 h-9 pl-9 pr-3 bg-surface border border-divider rounded-md text-[13px] placeholder:text-ink-faint focus:border-brand-mid"
              />
              <Icon name="search" size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-ink-mute" />
            </div>
          </div>

          {/* Table */}
          <div className="bg-surface border border-divider rounded-lg overflow-hidden">
            <header className="px-4 py-2.5 border-b border-divider bg-cream/50 flex items-center justify-between">
              <div className="text-[11px] font-bold uppercase tracking-wider text-ink-mute">
                Assets · {filtered.length} of {FIXED_ASSETS.length}
              </div>
              <div className="text-[10px] text-ink-mute">Synced from <span className="font-mono">cnr_fixedasset</span> · 4 min ago</div>
            </header>
            <table className="w-full text-[12px]">
              <thead className="text-[10px] uppercase tracking-wider text-ink-mute font-sans">
                <tr className="border-b border-divider-soft [&>th]:px-3 [&>th]:py-2 [&>th]:text-left [&>th]:font-bold">
                  <th>Asset ID</th>
                  <th>Name · Category</th>
                  <th>Custodian</th>
                  <th>Location</th>
                  <th className="text-right">Cost</th>
                  <th className="text-right">Accum. Depr.</th>
                  <th className="text-right">NBV</th>
                  <th>Status</th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(f => <AssetRow key={f.id} f={f} />)}
              </tbody>
              {filtered.length > 0 && (
                <tfoot className="bg-cream/40 font-semibold text-ink">
                  <tr className="border-t border-divider [&>td]:px-3 [&>td]:py-2.5">
                    <td colSpan={4} className="text-[11px] uppercase tracking-wider font-bold text-ink-mute">
                      Subtotal ({filtered.length} assets)
                    </td>
                    <td className="text-right font-mono">₱{filtered.reduce((s, f) => s + f.cost, 0).toLocaleString()}</td>
                    <td className="text-right font-mono text-danger">₱{filtered.reduce((s, f) => s + f.accumDepr, 0).toLocaleString()}</td>
                    <td className="text-right font-mono text-brand-mid">₱{filtered.reduce((s, f) => s + f.nbv, 0).toLocaleString()}</td>
                    <td></td>
                  </tr>
                </tfoot>
              )}
            </table>
            {filtered.length === 0 && (
              <div className="p-12 text-center text-ink-mute text-[12px]">No assets match these filters.</div>
            )}
          </div>
        </div>
      </div>
    </>
  )
}

/* ─── Row ────────────────────────────────────────────────────────────── */
function AssetRow({ f }: { f: FixedAsset }) {
  const symbol = f.currency === "PHP" ? "₱" : f.currency === "SGD" ? "S$" : f.currency === "HKD" ? "HK$" : "$"
  const deprPct = Math.round((f.accumDepr / f.cost) * 100)
  return (
    <tr className="border-t border-divider-soft hover:bg-cream/60 cursor-pointer [&>td]:px-3 [&>td]:py-2.5 align-top transition-colors">
      <td>
        <div className="font-mono font-bold text-brand-mid text-[11px]">{f.id}</div>
        {f.serial && <div className="font-mono text-[9px] text-ink-mute mt-0.5">{f.serial}</div>}
      </td>
      <td>
        <div className="font-semibold text-ink leading-tight max-w-sm">{f.name}</div>
        <div className="text-[10.5px] text-ink-mute mt-0.5">{f.category}</div>
      </td>
      <td>
        <div className="flex items-center gap-1.5">
          <Avatar initials={f.custodian.initials} tone={f.custodian.tone as any} size="xs" />
          <span className="text-[11px] text-ink-soft">{f.custodian.name}</span>
        </div>
      </td>
      <td><span className="text-[11px] text-ink-soft">{f.location}</span></td>
      <td className="text-right font-mono text-ink-soft">{symbol}{f.cost.toLocaleString()}</td>
      <td className="text-right">
        <div className="font-mono text-danger">{symbol}{f.accumDepr.toLocaleString()}</div>
        <div className="text-[9.5px] text-ink-mute font-mono">{deprPct}% · {f.usefulLifeYrs}y SL</div>
      </td>
      <td className="text-right font-mono font-bold text-brand-mid">{symbol}{f.nbv.toLocaleString()}</td>
      <td><Badge variant={FA_STATUS_TONE[f.status] as any} dot>{FA_STATUS_LABEL[f.status]}</Badge></td>
    </tr>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/assets/depreciation.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/assets/depreciation.tsx
 * ----------------------------------------------------------------------------
 * Fixed Assets — Monthly depreciation run console.
 *
 * Workflow: preview the upcoming run, review per-asset amounts and totals,
 * compare against last month, then post to GL. Surface anomalies (assets
 * already fully depreciated, idle assets, retired assets in the queue).
 * ============================================================================
 */

interface DeprecLine {
  fa: any                          /* FixedAsset reference */
  monthlyDepr: number              /* PHP */
  ytdDepr: number                  /* PHP */
  nbvBefore: number
  nbvAfter: number
  flag?: "fully-depreciated" | "idle" | "retired"
}

/* Build a synthetic run from FIXED_ASSETS. Each asset's monthly depr =
 * cost / (usefulLife * 12); guarded so retired / fully-depreciated assets
 * surface as flags rather than valid lines. */
function buildRun(): DeprecLine[] {
  return FIXED_ASSETS.map((fa: any) => {
    const monthly = fa.method === "Straight-line" ? fa.cost / (fa.usefulLifeYrs * 12) : fa.nbv * 0.02
    let flag: DeprecLine["flag"] | undefined
    if (fa.nbv <= 0)               flag = "fully-depreciated"
    else if (fa.status === "retired") flag = "retired"
    else if (fa.status === "idle")    flag = "idle"
    const real = (flag === "fully-depreciated" || flag === "retired") ? 0 : Math.round(monthly)
    return {
      fa,
      monthlyDepr: real,
      ytdDepr: real * 4,            /* Apr = month 4 */
      nbvBefore: fa.nbv,
      nbvAfter: fa.nbv - real,
      flag,
    }
  })
}

function AssetDepreciation() {
  const { push } = useRouter()
  const run = React.useMemo(buildRun, [])
  const [category, setCategory] = React.useState<"all" | string>("all")
  const [showFlags, setShowFlags] = React.useState(true)
  const [selected, setSelected] = React.useState<DeprecLine | null>(null)

  const filtered = React.useMemo(() => {
    let r = run
    if (category !== "all") r = r.filter(l => l.fa.category === category)
    return r
  }, [run, category])

  const total       = run.reduce((s, l) => s + l.monthlyDepr, 0)
  const ytdTotal    = run.reduce((s, l) => s + l.ytdDepr, 0)
  const flagged     = run.filter(l => l.flag !== undefined).length
  const lastMonth   = Math.round(total * 0.96)        /* synthetic comp */
  const monthDelta  = total - lastMonth
  const grossNBV    = run.reduce((s, l) => s + l.nbvBefore, 0)
  const postNBV     = run.reduce((s, l) => s + l.nbvAfter, 0)

  const byCat: Record<string, { count: number; amt: number }> = {}
  for (const l of run) {
    if (l.flag === "fully-depreciated" || l.flag === "retired") continue
    if (!byCat[l.fa.category]) byCat[l.fa.category] = { count: 0, amt: 0 }
    byCat[l.fa.category].count++
    byCat[l.fa.category].amt += l.monthlyDepr
  }
  const cats = Object.entries(byCat).sort((a, b) => b[1].amt - a[1].amt)
  const catMax = cats[0]?.[1].amt ?? 1

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Assets" }, { label: "Fixed Assets" }, { label: "Depreciation" }]}
        leading={<AssetBranchToggle current="fixed" />}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export schedule</Button>
            <Button variant="ghost" leadingIcon="refresh">Recalculate</Button>
            <Button variant="primary" leadingIcon="check">Post April run</Button>
          </>
        }
      />

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

          {/* Hero — preview the run */}
          <Card>
            <div className="p-5 bg-gradient-to-br from-brand to-brand-light text-white relative overflow-hidden">
              <div className="absolute top-3 right-4 inline-flex items-center gap-1.5 text-[10px] font-bold uppercase tracking-wider text-accent">
                <Icon name="clock" size={11} /> Preview · not yet posted
              </div>
              <div className="text-[10px] font-bold uppercase tracking-wider text-accent">April 2026 depreciation run</div>
              <div className="font-serif text-[40px] leading-none mt-1.5">₱{Math.round(total).toLocaleString()}</div>
              <div className="text-[12px] text-white/70 mt-1.5">
                {run.filter(l => !l.flag).length} of {run.length} assets · {flagged} flagged for review
              </div>

              <div className="mt-5 grid grid-cols-4 gap-4">
                <HeroStat label="MoM Δ"        value={`${monthDelta >= 0 ? "+" : "−"}₱${Math.abs(monthDelta).toLocaleString()}`} sub={`vs ₱${lastMonth.toLocaleString()} March`} />
                <HeroStat label="YTD posted"   value={`₱${(ytdTotal / 1_000_000).toFixed(2)}M`} sub="Jan-Mar (3 months)" />
                <HeroStat label="Gross NBV"    value={`₱${(grossNBV / 1_000_000).toFixed(1)}M`} sub="before this run" />
                <HeroStat label="NBV after"    value={`₱${(postNBV / 1_000_000).toFixed(1)}M`} sub="if posted today" />
              </div>
            </div>
          </Card>

          {/* AI callout */}
          <AICallout
            title={`${flagged} flagged assets — auto-handle?`}
            body="The April run includes 1 retired vehicle (auto-disposed), 1 idle excavator under maintenance, and 1 fully-depreciated rebar truck. Standard policy is: skip retired, continue idle (with a note), and stop posting on fully-depreciated. Want to apply policy and remove them from this run?"
            matches={run.filter(l => l.flag).map(l => ({ code: l.fa.id, name: `${l.fa.name} · ${l.flag}`, pct: 100 }))}
            dismissLabel="Review each"
            proceedLabel="Apply policy"
          />

          {/* Category waterfall */}
          <div className="grid grid-cols-[1fr_360px] gap-4">
            <Card>
              <CardHeader>
                <CardTitle>By category</CardTitle>
                <span className="text-[10.5px] text-ink-mute">monthly depr · PHP</span>
              </CardHeader>
              <div className="p-4 space-y-2.5">
                {cats.map(([cat, info]) => (
                  <div key={cat}>
                    <div className="flex items-baseline justify-between mb-0.5">
                      <span className="text-[12px] font-semibold text-ink">{cat}</span>
                      <span className="font-mono text-[11px] text-ink-soft"><strong>₱{Math.round(info.amt).toLocaleString()}</strong> · {info.count} asset{info.count === 1 ? "" : "s"}</span>
                    </div>
                    <div className="h-2 rounded-full bg-cream-deep overflow-hidden">
                      <div className="h-full rounded-full bg-brand" style={{ width: `${(info.amt / catMax) * 100}%` }} />
                    </div>
                  </div>
                ))}
              </div>
            </Card>

            <Card>
              <CardHeader>
                <CardTitle>GL impact preview</CardTitle>
                <Badge variant="info">JE-2026-04-0118</Badge>
              </CardHeader>
              <div className="p-3 text-[11.5px] divide-y divide-divider-soft">
                <GLRow ac="6610" name="Depreciation expense · P&amp;L"        dr={Math.round(total)} cr={0} />
                <GLRow ac="1520" name="Accum depr · Plant &amp; Machinery"     dr={0}                cr={Math.round((byCat["Plant & Machinery"]?.amt ?? 0))} />
                <GLRow ac="1521" name="Accum depr · Vehicles"                  dr={0}                cr={Math.round((byCat["Vehicles"]?.amt ?? 0))} />
                <GLRow ac="1522" name="Accum depr · IT Equipment"              dr={0}                cr={Math.round((byCat["IT Equipment"]?.amt ?? 0))} />
                <GLRow ac="1523" name="Accum depr · Buildings"                 dr={0}                cr={Math.round((byCat["Buildings"]?.amt ?? 0))} />
                <GLRow ac="1524" name="Accum depr · F&amp;F"                    dr={0}                cr={Math.round((byCat["Furniture & Fixtures"]?.amt ?? 0))} />
                <div className="px-3 py-2 flex items-center justify-between font-bold bg-cream/60">
                  <span className="text-ink">Total</span>
                  <div className="font-mono"><span className="text-ink">₱{Math.round(total).toLocaleString()}</span></div>
                </div>
              </div>
            </Card>
          </div>

          {/* Filter */}
          <div className="flex items-center gap-2 flex-wrap">
            <FilterChip active={category === "all"} onClick={() => setCategory("all")}>All categories</FilterChip>
            {Object.keys(byCat).map(c => (
              <FilterChip key={c} active={category === c} onClick={() => setCategory(c)}>{c}</FilterChip>
            ))}
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={showFlags} onClick={() => setShowFlags(!showFlags)} tone="warn">Show flagged ({flagged})</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {run.length}</span>
          </div>

          {/* Detail table */}
          <Card>
            <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">Asset</th>
                  <th>Category</th>
                  <th className="text-right">Cost</th>
                  <th className="text-right">NBV before</th>
                  <th className="text-right">April depr</th>
                  <th className="text-right">NBV after</th>
                  <th className="text-right">YTD depr</th>
                  <th>Flag</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.filter(l => showFlags || !l.flag).map(l => (
                  <tr key={l.fa.id} className={cn("border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors", l.flag && "bg-warn-soft/30")} onClick={() => setSelected(l)}>
                    <td className="px-4 py-2.5">
                      <div className="font-semibold text-ink truncate max-w-[280px]">{l.fa.name}</div>
                      <div className="font-mono text-[10px] text-ink-mute">{l.fa.id}</div>
                    </td>
                    <td className="text-ink-soft text-[11px]">{l.fa.category}</td>
                    <td className="text-right font-mono text-ink-soft">₱{l.fa.cost.toLocaleString()}</td>
                    <td className="text-right font-mono text-ink-soft">₱{l.nbvBefore.toLocaleString()}</td>
                    <td className="text-right font-mono font-semibold text-ink">{l.monthlyDepr > 0 ? `₱${l.monthlyDepr.toLocaleString()}` : "—"}</td>
                    <td className="text-right font-mono text-ink">₱{l.nbvAfter.toLocaleString()}</td>
                    <td className="text-right font-mono text-ink-mute text-[11px]">₱{l.ytdDepr.toLocaleString()}</td>
                    <td>
                      {l.flag === "fully-depreciated" && <Badge variant="neutral" dot>Fully depr.</Badge>}
                      {l.flag === "retired"           && <Badge variant="danger"  dot>Retired</Badge>}
                      {l.flag === "idle"              && <Badge variant="warn"    dot>Idle</Badge>}
                      {!l.flag                        && <span className="text-success">✓</span>}
                    </td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </div>
      </div>

      {selected && (
        <Sheet open={true} onOpenChange={() => setSelected(null)} side="right">
          <SheetHeader>
            <div>
              <div className="font-mono text-[10px] text-ink-mute">{selected.fa.id}</div>
              <h2 className="font-serif text-xl text-ink mt-1 leading-tight">{selected.fa.name}</h2>
              <div className="text-[11px] text-ink-soft mt-1.5">{selected.fa.category} · {selected.fa.location}</div>
            </div>
            <SheetClose onClick={() => setSelected(null)} />
          </SheetHeader>
          <SheetBody>
            <section>
              <div className="grid grid-cols-2 gap-3">
                <div className="bg-cream/60 border border-divider rounded-md p-3">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Cost</div>
                  <div className="font-serif text-xl text-ink mt-0.5">₱{selected.fa.cost.toLocaleString()}</div>
                  <div className="text-[10px] text-ink-mute mt-0.5">acquired {selected.fa.acquiredDate}</div>
                </div>
                <div className="bg-cream/60 border border-divider rounded-md p-3">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Useful life</div>
                  <div className="font-serif text-xl text-ink mt-0.5">{selected.fa.usefulLifeYrs}y</div>
                  <div className="text-[10px] text-ink-mute mt-0.5">{selected.fa.method}</div>
                </div>
              </div>
            </section>
            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">This-period schedule</div>
              <div className="bg-surface border border-divider rounded-md text-[12px] divide-y divide-divider-soft">
                <PLine label="NBV before"    value={`₱${selected.nbvBefore.toLocaleString()}`} />
                <PLine label="April depr"    value={`₱${selected.monthlyDepr.toLocaleString()}`} accent="warn" />
                <PLine label="NBV after"     value={`₱${selected.nbvAfter.toLocaleString()}`} />
                <PLine label="YTD depr"      value={`₱${selected.ytdDepr.toLocaleString()}`} />
                <PLine label="Months to zero" value={selected.monthlyDepr > 0 ? `${Math.ceil(selected.nbvAfter / selected.monthlyDepr)}` : "—"} />
              </div>
            </section>
          </SheetBody>
          <SheetFooter>
            <Button variant="ghost" onClick={() => setSelected(null)}>Close</Button>
            <Button variant="ghost" leadingIcon="external" className="ml-auto" onClick={() => { setSelected(null); push("/assets/register") }}>Open asset</Button>
            <Button variant="primary" leadingIcon="check">Approve this line</Button>
          </SheetFooter>
        </Sheet>
      )}
    </>
  )
}

function HeroStat({ label, value, sub }: any) {
  return (
    <div>
      <div className="text-[10px] font-bold uppercase tracking-wider text-accent">{label}</div>
      <div className="font-serif text-[20px] leading-none mt-1">{value}</div>
      <div className="text-[10.5px] text-white/65 mt-1">{sub}</div>
    </div>
  )
}
function GLRow({ ac, name, dr, cr }: any) {
  return (
    <div className="grid grid-cols-[60px_1fr_90px_90px] gap-2 px-3 py-2 items-center">
      <span className="font-mono font-bold text-brand-mid text-[10.5px]">{ac}</span>
      <span className="text-ink-soft truncate">{name}</span>
      <span className="text-right font-mono text-ink">{dr > 0 ? `₱${dr.toLocaleString()}` : ""}</span>
      <span className="text-right font-mono text-ink">{cr > 0 ? `₱${cr.toLocaleString()}` : ""}</span>
    </div>
  )
}
function PLine({ label, value, accent }: any) {
  const tones: Record<string, string> = { warn: "text-warn" }
  return (
    <div className="px-3 py-2 flex items-center justify-between">
      <span className="text-ink-soft">{label}</span>
      <span className={cn("font-mono font-semibold", accent ? tones[accent] : "text-ink")}>{value}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/assets/disposals.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/assets/disposals.tsx
 * ----------------------------------------------------------------------------
 * Fixed Assets — Disposals. Assets pending or recently disposed: sold,
 * scrapped, traded-in. Each disposal computes gain/loss vs NBV, journal
 * entry posted on approval.
 * ============================================================================
 */

interface Disposal {
  id: string
  fa: any                                   /* FixedAsset */
  method: "Sale" | "Scrap" | "Trade-in" | "Donation"
  proceeds: number                          /* PHP */
  costToDispose: number                     /* PHP — transport / certification */
  nbvAtDisposal: number                     /* PHP */
  buyer?: string
  status: "proposed" | "approved" | "in-progress" | "posted" | "cancelled"
  initiated: string
  initiator: { name: string; initials: string; tone: any }
  reason: string
  attachments: number
  notes?: string
}

const DISPOSALS: Disposal[] = [
  /* Proposed */
  { id: "DISP-2026-014", fa: FIXED_ASSETS.find((f: any) => f.id === "FA-HVPH-0182"), method: "Sale",      proceeds: 380000, costToDispose: 22000, nbvAtDisposal: 540000, buyer: "Wong Trading Phils.",   status: "proposed",   initiated: "Apr 12, 2026", initiator: { name: "Warren C.", initials: "WC", tone: "amber" }, reason: "Replaced by FA-HVPH-0231 (new CAT 320D). Idle since Jan.", attachments: 4 },
  { id: "DISP-2026-013", fa: FIXED_ASSETS.find((f: any) => f.id === "FA-HVPH-0207"), method: "Trade-in",  proceeds: 540000, costToDispose: 0,     nbvAtDisposal: 770000, buyer: "Toyota Manila — Bonifacio", status: "proposed", initiated: "Apr 14, 2026", initiator: { name: "Olivia P.", initials: "OP", tone: "amber" }, reason: "Trade-in toward new fleet Hilux. Body damage from Mar accident assessed.", attachments: 6 },

  /* Approved (in progress) */
  { id: "DISP-2026-012", fa: FIXED_ASSETS.find((f: any) => f.id === "FA-HVPH-0224"), method: "Sale",      proceeds: 4800000, costToDispose: 280000, nbvAtDisposal: 14106667, buyer: "Megaworld Calamba (internal transfer)", status: "approved", initiated: "Apr 04, 2026", initiator: { name: "Pedro S.", initials: "PS", tone: "blue" }, reason: "Tower crane redeployed to Calamba Phase 2 site (internal). Recognising at fair value.", attachments: 12, notes: "Treated as internal transfer · gain/loss eliminated on consolidation" },

  /* Posted */
  { id: "DISP-2026-011", fa: FIXED_ASSETS.find((f: any) => f.id === "FA-HVPH-0192"), method: "Sale",      proceeds: 220000, costToDispose: 4500,  nbvAtDisposal: 0,      buyer: "Carmudi Auctions PH",          status: "posted",     initiated: "Mar 18, 2026", initiator: { name: "Olivia P.", initials: "OP", tone: "amber" }, reason: "Fully depreciated sedan · auction sale.", attachments: 8 },
  { id: "DISP-2026-010", fa: { id: "FA-HVPH-0298", name: "Toyota Vios 2017 — sales fleet", category: "Vehicles" },                method: "Scrap",     proceeds: 0,      costToDispose: 18000, nbvAtDisposal: 0,      buyer: undefined,                       status: "posted",     initiated: "Mar 10, 2026", initiator: { name: "Olivia P.", initials: "OP", tone: "amber" }, reason: "Flood-damaged · uneconomic to repair. LTO de-registered.", attachments: 5 },

  /* Cancelled */
  { id: "DISP-2026-009", fa: FIXED_ASSETS.find((f: any) => f.id === "FA-HVPH-0218"), method: "Sale",      proceeds: 3800000, costToDispose: 80000, nbvAtDisposal: 5123333, buyer: "—",                            status: "cancelled",  initiated: "Mar 05, 2026", initiator: { name: "Warren C.", initials: "WC", tone: "amber" }, reason: "Buyer withdrew · welding cell returned to service after warranty repair.", attachments: 3 },
]

const DISP_STATUS_LABEL: Record<Disposal["status"], string> = { proposed: "Proposed", approved: "Approved", "in-progress": "In progress", posted: "Posted", cancelled: "Cancelled" }
const DISP_STATUS_TONE: Record<Disposal["status"], string> = { proposed: "warn", approved: "info", "in-progress": "accent", posted: "success", cancelled: "neutral" }

function gainLoss(d: Disposal) {
  return d.proceeds - d.costToDispose - d.nbvAtDisposal
}

function AssetDisposals() {
  const { push } = useRouter()
  const [status, setStatus] = React.useState<"all" | Disposal["status"]>("all")
  const [selected, setSelected] = React.useState<Disposal | null>(null)

  const filtered = status === "all" ? DISPOSALS : DISPOSALS.filter(d => d.status === status)

  const pendingCount   = DISPOSALS.filter(d => d.status === "proposed").length
  const inFlightCount  = DISPOSALS.filter(d => d.status === "approved" || d.status === "in-progress").length
  const postedYTD      = DISPOSALS.filter(d => d.status === "posted").length
  const totalProceeds  = DISPOSALS.filter(d => d.status === "posted").reduce((s, d) => s + d.proceeds, 0)
  const totalGainLoss  = DISPOSALS.filter(d => d.status === "posted").reduce((s, d) => s + gainLoss(d), 0)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Assets" }, { label: "Fixed Assets" }, { label: "Disposals" }]}
        leading={<AssetBranchToggle current="fixed" />}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="primary" leadingIcon="plus">Propose disposal</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="Awaiting approval" value={pendingCount}                                       sub={pendingCount === 0 ? "None pending" : "Routed to CFO"} accent="warn" />
            <KpiTile label="In flight"          value={inFlightCount}                                       sub="approved · paperwork pending"  accent="info" />
            <KpiTile label="Posted · YTD"       value={postedYTD}                                            sub="recognised in GL"              accent="success" />
            <KpiTile label="Net gain/(loss)"    value={`${totalGainLoss >= 0 ? "+" : "−"}₱${Math.abs(Math.round(totalGainLoss / 1000)).toLocaleString()}k`} sub="from posted disposals" accent={totalGainLoss >= 0 ? "success" : "danger"} />
          </div>

          <AICallout
            title="Cebu Pacific lease event · 3 assets impacted"
            body="Their Apr 9 site visit memo identifies 3 fleet vehicles for return-to-lessor in May. Want to draft disposal proposals with the lessor return values from the original master agreement (saved in HR · Contracts vault)?"
            matches={[
              { code: "FA-HVPH-0210", name: "Isuzu N-Series · scheduled return May 12", pct: 100 },
              { code: "FA-HVPH-0207", name: "Toyota Hilux · lease ends May 18",         pct: 100 },
            ]}
            dismissLabel="Defer"
            proceedLabel="Draft 2 proposals"
          />

          {/* Filters */}
          <div className="flex items-center gap-2 flex-wrap">
            <FilterChip active={status === "all"}        onClick={() => setStatus("all")}>All</FilterChip>
            <FilterChip active={status === "proposed"}   onClick={() => setStatus("proposed")} tone="warn">Proposed</FilterChip>
            <FilterChip active={status === "approved"}   onClick={() => setStatus("approved")} tone="info">Approved</FilterChip>
            <FilterChip active={status === "posted"}     onClick={() => setStatus("posted")} tone="success">Posted</FilterChip>
            <FilterChip active={status === "cancelled"}  onClick={() => setStatus("cancelled")}>Cancelled</FilterChip>
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {DISPOSALS.length}</span>
          </div>

          {/* List */}
          <Card>
            <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">Disposal</th>
                  <th>Asset</th>
                  <th>Method</th>
                  <th className="text-right">NBV</th>
                  <th className="text-right">Proceeds</th>
                  <th className="text-right">Gain/(Loss)</th>
                  <th>Status</th>
                  <th>Initiated</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map(d => {
                  const gl = gainLoss(d)
                  return (
                    <tr key={d.id} className="border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors" onClick={() => setSelected(d)}>
                      <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{d.id}</td>
                      <td>
                        <div className="font-semibold text-ink truncate max-w-[280px]">{d.fa?.name ?? "—"}</div>
                        <div className="font-mono text-[10px] text-ink-mute">{d.fa?.id ?? ""} · {d.fa?.category ?? ""}</div>
                      </td>
                      <td><Badge variant="neutral">{d.method}</Badge></td>
                      <td className="text-right font-mono text-ink-soft">₱{d.nbvAtDisposal.toLocaleString()}</td>
                      <td className="text-right font-mono text-ink-soft">₱{d.proceeds.toLocaleString()}</td>
                      <td className={cn("text-right font-mono font-semibold", gl > 0 ? "text-success" : gl < 0 ? "text-danger" : "text-ink-faint")}>
                        {gl === 0 ? "—" : `${gl > 0 ? "+" : "−"}₱${Math.abs(gl).toLocaleString()}`}
                      </td>
                      <td><Badge variant={DISP_STATUS_TONE[d.status] as any} dot>{DISP_STATUS_LABEL[d.status]}</Badge></td>
                      <td className="text-ink-soft text-[11px]">
                        <div>{d.initiated}</div>
                        <div className="text-ink-mute text-[10px]">by {d.initiator.name}</div>
                      </td>
                      <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </Card>

        </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.fa?.name ?? "—"}</h2>
              <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
                <Badge variant={DISP_STATUS_TONE[selected.status] as any} dot>{DISP_STATUS_LABEL[selected.status]}</Badge>
                <span>·</span><span>{selected.method}</span>
                {selected.buyer && <><span>·</span><span>{selected.buyer}</span></>}
              </div>
            </div>
            <SheetClose onClick={() => setSelected(null)} />
          </SheetHeader>
          <SheetBody>
            {/* Reason */}
            <section>
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Reason</div>
              <div className="bg-cream/60 border border-divider rounded-md p-3 text-[11.5px] text-ink-soft">{selected.reason}</div>
              {selected.notes && (
                <div className="mt-2 bg-warn-soft/40 border border-warn/30 rounded-md p-3 text-[11px] text-ink-soft">{selected.notes}</div>
              )}
            </section>

            {/* Financial */}
            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Financial impact</div>
              <div className="bg-surface border border-divider rounded-md text-[12px] divide-y divide-divider-soft">
                <Row label="Proceeds"             value={`₱${selected.proceeds.toLocaleString()}`} />
                <Row label="Cost to dispose"      value={`(₱${selected.costToDispose.toLocaleString()})`} />
                <Row label="Net proceeds"         value={`₱${(selected.proceeds - selected.costToDispose).toLocaleString()}`} bold />
                <Row label="NBV at disposal"      value={`(₱${selected.nbvAtDisposal.toLocaleString()})`} />
                <div className={cn("px-3 py-2 flex items-center justify-between bg-cream/60 font-bold",
                  gainLoss(selected) > 0 ? "text-success" : gainLoss(selected) < 0 ? "text-danger" : "text-ink-mute")}>
                  <span>Gain / (Loss) on disposal</span>
                  <span className="font-mono">{gainLoss(selected) === 0 ? "—" : `${gainLoss(selected) > 0 ? "+" : "−"}₱${Math.abs(gainLoss(selected)).toLocaleString()}`}</span>
                </div>
              </div>
            </section>

            {/* JE preview */}
            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Journal entry preview · JE-2026-{(parseInt(selected.id.slice(-3), 10)).toString().padStart(2, "0")}</div>
              <div className="bg-surface border border-divider rounded-md text-[11.5px] divide-y divide-divider-soft">
                <JEr ac="1010" name="Cash / Bank (proceeds)"     dr={selected.proceeds}                    cr={0} />
                <JEr ac="1520" name="Accum depr · category"      dr={selected.fa.cost - selected.nbvAtDisposal} cr={0} />
                <JEr ac="1510" name="Asset cost · category"      dr={0}                                     cr={selected.fa.cost} />
                {gainLoss(selected) > 0 && <JEr ac="7110" name="Gain on disposal"               dr={0}     cr={gainLoss(selected)} />}
                {gainLoss(selected) < 0 && <JEr ac="6710" name="Loss on disposal"               dr={Math.abs(gainLoss(selected))} cr={0} />}
                {selected.costToDispose > 0 && <JEr ac="6720" name="Disposal cost"              dr={selected.costToDispose} cr={0} />}
              </div>
            </section>

            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Approval chain</div>
              <div className="bg-surface border border-divider rounded-md text-[11.5px] divide-y divide-divider-soft">
                <ApproveRow when={selected.initiated} who={selected.initiator.name} role="Initiator"     status="done" />
                <ApproveRow when={selected.status === "posted" || selected.status === "approved" ? "Apr 16" : "—"} who="Olivia P." role="Asset Manager" status={selected.status === "posted" || selected.status === "approved" || selected.status === "in-progress" ? "done" : "pending"} />
                <ApproveRow when={selected.status === "posted" ? "Apr 17" : "—"} who="Aldo K." role="CFO"          status={selected.status === "posted" ? "done" : selected.status === "approved" || selected.status === "in-progress" ? "current" : "pending"} />
              </div>
            </section>

            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Attachments</div>
              <div className="bg-surface border border-divider rounded-md p-3 flex items-center gap-3">
                <div className="size-9 rounded-md bg-cream-deep text-ink-mute flex items-center justify-center"><Icon name="paperclip" size={14} /></div>
                <div className="text-[11.5px] text-ink-soft">{selected.attachments} document{selected.attachments === 1 ? "" : "s"} attached</div>
                <Button variant="ghost" size="sm" leadingIcon="external" className="ml-auto">View</Button>
              </div>
            </section>
          </SheetBody>
          <SheetFooter>
            <Button variant="ghost" onClick={() => setSelected(null)}>Close</Button>
            <Button variant="ghost" leadingIcon="external" className="ml-auto" onClick={() => { setSelected(null); push("/assets/register") }}>Open asset</Button>
            {selected.status === "proposed" && <><Button variant="danger" leadingIcon="x">Reject</Button><Button variant="primary" leadingIcon="check">Approve</Button></>}
            {selected.status === "approved" && <Button variant="primary" leadingIcon="check">Post journal entry</Button>}
          </SheetFooter>
        </Sheet>
      )}
    </>
  )
}

function Row({ label, value, bold }: any) {
  return (
    <div className="px-3 py-2 flex items-center justify-between">
      <span className={cn("text-ink-soft", bold && "font-semibold text-ink")}>{label}</span>
      <span className={cn("font-mono", bold ? "font-bold text-ink" : "text-ink")}>{value}</span>
    </div>
  )
}
function JEr({ ac, name, dr, cr }: any) {
  return (
    <div className="grid grid-cols-[50px_1fr_90px_90px] gap-2 px-3 py-2 items-center text-[11px]">
      <span className="font-mono font-bold text-brand-mid">{ac}</span>
      <span className="text-ink-soft truncate">{name}</span>
      <span className="text-right font-mono text-ink">{dr > 0 ? `₱${dr.toLocaleString()}` : ""}</span>
      <span className="text-right font-mono text-ink">{cr > 0 ? `₱${cr.toLocaleString()}` : ""}</span>
    </div>
  )
}
function ApproveRow({ when, who, role, status }: any) {
  return (
    <div className="px-3 py-2 flex items-center gap-2.5">
      <span className={cn(
        "size-6 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",
        status === "done"    ? "bg-success text-white" :
        status === "current" ? "bg-warn text-white"    :
        "bg-cream-deep text-ink-mute"
      )}>
        {status === "done"    ? <Icon name="check" size={11} strokeWidth={3} /> :
         status === "current" ? "•" : "·"}
      </span>
      <div className="flex-1 min-w-0">
        <div className="text-[11.5px] font-semibold text-ink">{who}</div>
        <div className="text-[10px] text-ink-mute">{role}</div>
      </div>
      <span className="text-[10.5px] font-mono text-ink-faint">{when}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/tax/calendar.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/tax/calendar.tsx
 * ----------------------------------------------------------------------------
 * Tax & Compliance hero — Compliance Calendar. Multi-jurisdiction filing
 * tracker grouped by due-date bucket. Overdue items raise visual alarm;
 * AI brief at top summarises priorities.
 * ============================================================================
 */

function ComplianceCalendar() {
  const { push } = useRouter()
  const [jurFilter, setJurFilter] = React.useState<"all" | TaxFiling["jurisdiction"]>("all")
  const [statusFilter, setStatusFilter] = React.useState<"open" | "all" | TaxFiling["status"]>("open")

  const filtered = React.useMemo(() => {
    let r = TAX_FILINGS
    if (jurFilter !== "all") r = r.filter(t => t.jurisdiction === jurFilter)
    if (statusFilter === "open") r = r.filter(t => t.status !== "accepted")
    else if (statusFilter !== "all") r = r.filter(t => t.status === statusFilter)
    return [...r].sort((a, b) => a.dueISO.localeCompare(b.dueISO))
  }, [jurFilter, statusFilter])

  /* Bucket by due date */
  const buckets: { label: string; tone: string; items: TaxFiling[] }[] = [
    { label: "Overdue",        tone: "danger",  items: filtered.filter(t => t.daysUntilDue < 0 && t.status !== "accepted") },
    { label: "Due this week",  tone: "warn",    items: filtered.filter(t => t.daysUntilDue >= 0 && t.daysUntilDue <= 7 && t.status !== "accepted") },
    { label: "Due this month", tone: "accent",  items: filtered.filter(t => t.daysUntilDue > 7 && t.daysUntilDue <= 30 && t.status !== "accepted") },
    { label: "Upcoming",       tone: "info",    items: filtered.filter(t => t.daysUntilDue > 30 && t.status !== "accepted") },
    { label: "Recently filed", tone: "success", items: filtered.filter(t => t.status === "accepted") },
  ]

  const overdueCount = TAX_FILINGS.filter(t => t.daysUntilDue < 0 && t.status !== "accepted").length
  const dueThisWeek  = TAX_FILINGS.filter(t => t.daysUntilDue >= 0 && t.daysUntilDue <= 7 && t.status !== "accepted").length
  const totalDueAmt  = TAX_FILINGS.filter(t => t.daysUntilDue <= 7 && t.daysUntilDue >= -30 && t.status !== "accepted" && t.amountDue && t.currency === "PHP").reduce((s, t) => s + (t.amountDue ?? 0), 0)

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Tax & Compliance" }, { label: "Compliance Calendar" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="calendar">Calendar view</Button>
            <Button variant="primary" leadingIcon="plus">New Filing</Button>
          </>
        }
      />

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

          {/* AI brief */}
          {overdueCount > 0 && (
            <div className="bg-danger-soft/60 border border-danger/30 rounded-lg p-4 flex items-start gap-3">
              <div className="size-8 rounded-full bg-danger/15 text-danger flex items-center justify-center shrink-0">
                <Icon name="alert-triangle" size={14} />
              </div>
              <div className="flex-1">
                <div className="text-[10px] uppercase tracking-wider font-bold text-danger mb-0.5">Cenora AI · Compliance brief</div>
                <div className="text-[13px] text-ink leading-snug">
                  <strong>{overdueCount} filing{overdueCount > 1 ? "s" : ""} overdue, {dueThisWeek} due this week.</strong>{" "}
                  BIR 1601-C is 7 days past due and accruing daily penalty ≈ ₱1,200/day.{" "}
                  All compensation data is ready — one-click submit to eBIR.
                </div>
              </div>
              <Button variant="primary" size="sm" leadingIcon="sparkle">Submit BIR 1601-C</Button>
            </div>
          )}

          {/* KPI strip */}
          <div className="grid grid-cols-4 gap-3">
            <KpiTile label="Overdue" value={overdueCount.toString()} sub={overdueCount > 0 ? "Penalty accruing" : "All clear"} accent={overdueCount > 0 ? "danger" : "success"} />
            <KpiTile label="Due this week" value={dueThisWeek.toString()} sub="Action required" accent="warn" />
            <KpiTile label="Cash due 30d" value={`₱${(totalDueAmt / 1000).toFixed(0)}k`} sub="PH filings" accent="brand" />
            <KpiTile label="On-time YTD" value="96%" sub="Last 12 months" accent="success" deltaDir="up" delta="+2pp" />
          </div>

          {/* Filters */}
          <div className="flex items-center gap-1.5 flex-wrap">
            <FilterChip active={statusFilter === "open"} onClick={() => setStatusFilter("open")}>Open</FilterChip>
            <FilterChip active={statusFilter === "all"} onClick={() => setStatusFilter("all")}>All</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            {(["overdue", "draft", "ready", "submitted", "accepted"] as const).map(s => {
              const c = TAX_FILINGS.filter(t => t.status === s).length
              if (c === 0) return null
              return <FilterChip key={s} active={statusFilter === s} onClick={() => setStatusFilter(s)} count={c} tone={TAX_STATUS_TONE[s] as any}>{TAX_STATUS_LABEL[s]}</FilterChip>
            })}
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={jurFilter === "all"} onClick={() => setJurFilter("all")}>All Jurisdictions</FilterChip>
            {(["PH", "SG", "HK"] as const).map(j => (
              <FilterChip key={j} active={jurFilter === j} onClick={() => setJurFilter(j)}>{JURISDICTION_LABEL[j]}</FilterChip>
            ))}
          </div>

          {/* Buckets */}
          <div className="space-y-4">
            {buckets.map(b => b.items.length > 0 && (
              <div key={b.label}>
                <div className="flex items-baseline justify-between mb-2 px-1">
                  <div className="flex items-baseline gap-2">
                    <Badge variant={b.tone as any} dot>{b.label}</Badge>
                    <span className="text-[11px] text-ink-mute">{b.items.length} {b.items.length === 1 ? "filing" : "filings"}</span>
                  </div>
                </div>
                <div className="bg-surface border border-divider rounded-lg overflow-hidden divide-y divide-divider-soft">
                  {b.items.map(t => <FilingRow key={t.id} t={t} />)}
                </div>
              </div>
            ))}
            {buckets.every(b => b.items.length === 0) && (
              <div className="bg-surface border border-divider rounded-lg p-12 text-center text-ink-mute text-[12px]">
                No filings match these filters.
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  )
}

/* ─── Filing row ─────────────────────────────────────────────────────── */
function FilingRow({ t }: { t: TaxFiling }) {
  const symbol = t.currency === "PHP" ? "₱" : t.currency === "SGD" ? "S$" : "HK$"
  const isOverdue = t.daysUntilDue < 0 && t.status !== "accepted"
  return (
    <div className={cn("px-4 py-3 flex items-center gap-4 hover:bg-cream/60 transition-colors", isOverdue && "bg-danger-soft/30")}>
      {/* Jurisdiction chip */}
      <div className="shrink-0 w-12 text-center">
        <div className="text-[18px] leading-none">{t.jurisdiction === "PH" ? "🇵🇭" : t.jurisdiction === "SG" ? "🇸🇬" : "🇭🇰"}</div>
        <div className="text-[9px] text-ink-mute font-mono mt-0.5">{t.entity}</div>
      </div>

      {/* Main */}
      <div className="flex-1 min-w-0">
        <div className="flex items-baseline gap-2 flex-wrap">
          <h3 className="font-semibold text-ink text-[13.5px] leading-snug">{t.form}</h3>
          <Badge variant="neutral">{t.category}</Badge>
        </div>
        <div className="flex items-center gap-3 mt-0.5 text-[11px] text-ink-mute">
          <span>Period <strong className="text-ink-soft">{t.period}</strong></span>
          <span>·</span>
          <span className="inline-flex items-center gap-1.5">
            <Avatar initials={t.preparer.initials} tone={t.preparer.tone as any} size="xs" />
            {t.preparer.name}
          </span>
          {t.notes && (<><span>·</span><span className="italic">{t.notes}</span></>)}
        </div>
      </div>

      {/* Due */}
      <div className="text-right shrink-0 w-32">
        <div className={cn("text-[12px] font-semibold", isOverdue ? "text-danger" : t.daysUntilDue <= 7 ? "text-warn" : "text-ink")}>{t.dueDate.replace(", 2026", "")}</div>
        <div className={cn("text-[10px]", isOverdue ? "text-danger font-semibold" : "text-ink-mute")}>
          {isOverdue ? `${Math.abs(t.daysUntilDue)}d overdue` : t.daysUntilDue === 0 ? "Today" : `in ${t.daysUntilDue}d`}
        </div>
      </div>

      {/* Amount */}
      <div className="text-right shrink-0 w-28">
        {t.amountDue != null && t.amountDue > 0 ? (
          <>
            <div className="font-mono font-semibold text-[12px] text-ink">{symbol}{t.amountDue.toLocaleString()}</div>
            <div className="text-[10px] text-ink-mute">{t.currency}</div>
          </>
        ) : t.status === "accepted" && t.amountDue ? (
          <>
            <div className="font-mono text-[12px] text-ink-mute line-through">{symbol}{t.amountDue.toLocaleString()}</div>
            <div className="text-[10px] text-ink-mute">paid</div>
          </>
        ) : <span className="text-[10px] text-ink-faint">—</span>}
      </div>

      {/* Status + action */}
      <div className="shrink-0 w-32 flex items-center justify-end gap-2">
        <Badge variant={TAX_STATUS_TONE[t.status] as any} dot>{TAX_STATUS_LABEL[t.status]}</Badge>
      </div>
      <Button variant="ghost" size="sm" trailingIcon="arrow-right" className="shrink-0">Open</Button>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/

/* ═════ apps/web/components/tax/filings.tsx ═════ */
;/*__IIFE_WRAP_START__*/(function(){
/**
 * ============================================================================
 * apps/web/components/tax/filings.tsx
 * ----------------------------------------------------------------------------
 * Tax &amp; Compliance — All filings across PH / SG / HK jurisdictions. The
 * Compliance Calendar (module hero) is the rolling 90-day view; this is the
 * full searchable table including historical filings.
 * ============================================================================
 */

function TaxFilings() {
  const { push } = useRouter()
  const [jur,    setJur]    = React.useState<"all" | "PH" | "SG" | "HK">("all")
  const [status, setStatus] = React.useState<"all" | "open" | "due-this-week" | "submitted" | "accepted">("all")
  const [cat,    setCat]    = React.useState<"all" | string>("all")
  const [q,      setQ]      = React.useState("")
  const [selected, setSelected] = React.useState<any | null>(null)

  const filtered = React.useMemo(() => {
    let r: any[] = TAX_FILINGS
    if (jur !== "all")    r = r.filter(t => t.jurisdiction === jur)
    if (cat !== "all")    r = r.filter(t => t.category === cat)
    if (status === "open")           r = r.filter(t => t.status === "draft" || t.status === "ready" || t.status === "overdue")
    if (status === "due-this-week")  r = r.filter(t => t.daysUntilDue >= 0 && t.daysUntilDue <= 7)
    if (status === "submitted")      r = r.filter(t => t.status === "submitted")
    if (status === "accepted")       r = r.filter(t => t.status === "accepted")
    const s = q.trim().toLowerCase()
    if (s) r = r.filter(t => t.form.toLowerCase().includes(s) || t.id.toLowerCase().includes(s) || t.entity.toLowerCase().includes(s))
    return r.sort((a, b) => a.daysUntilDue - b.daysUntilDue)
  }, [jur, status, cat, q])

  const overdueAmt = TAX_FILINGS.filter((t: any) => t.status === "overdue").reduce((s: number, t: any) => s + (t.amountDue ?? 0), 0)
  const dueAmt    = TAX_FILINGS.filter((t: any) => t.daysUntilDue >= 0 && t.daysUntilDue <= 7 && t.status !== "submitted" && t.status !== "accepted").reduce((s: number, t: any) => s + (t.amountDue ?? 0), 0)
  const overdueCount = TAX_FILINGS.filter((t: any) => t.status === "overdue").length
  const cats: string[] = Array.from(new Set(TAX_FILINGS.map((t: any) => t.category)))

  return (
    <>
      <TopBar
        breadcrumb={[{ label: "Tax & Compliance" }, { label: "Filings" }]}
        actions={
          <>
            <Button variant="ghost" leadingIcon="download">Export</Button>
            <Button variant="ghost" leadingIcon="calendar" onClick={() => push("/tax/calendar")}>Calendar view</Button>
            <Button variant="primary" leadingIcon="plus">New Filing</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="Overdue"            value={overdueCount}                                       sub={overdueCount > 0 ? "penalties accruing" : "All clear"} accent={overdueCount > 0 ? "danger" : "success"} delta={overdueCount > 0 ? "+1 vs Mar" : undefined} deltaDir="down" />
            <KpiTile label="Overdue amount"     value={`₱${Math.round(overdueAmt / 1000).toLocaleString()}k`} sub="net tax payable"             accent="danger" />
            <KpiTile label="Due ≤ 7 days"       value={TAX_FILINGS.filter((t: any) => t.daysUntilDue >= 0 && t.daysUntilDue <= 7 && t.status !== "submitted" && t.status !== "accepted").length} sub={`₱${Math.round(dueAmt / 1000).toLocaleString()}k payable`} accent="warn" />
            <KpiTile label="On-time rate · YTD" value="94%"                                                 sub="3 late filings YTD"          accent="success" delta="+2pp" deltaDir="up" />
          </div>

          <AICallout
            title="3 PH forms can be filed together · BIR e-FPS bundle"
            body="BIR 2550M (Apr 20), 1601-E (Apr 20), and 0619-E (Apr 20) all share the same payee schedules. The Form 2307 batch from yesterday's payment run reconciles all three. Want to bundle and submit on the BIR e-FPS portal in one session?"
            matches={[
              { code: "FIL-2026-0418", name: "BIR 2550M · ₱248k", pct: 100 },
              { code: "FIL-2026-0420", name: "BIR 1601-E · ₱32.4k", pct: 100 },
              { code: "FIL-2026-0421", name: "BIR 0619-E · ₱12.8k", pct: 100 },
            ]}
            dismissLabel="File separately"
            proceedLabel="Bundle &amp; submit"
          />

          {/* Filters */}
          <div className="flex items-center gap-2 flex-wrap">
            <SearchBox value={q} onChange={setQ} placeholder="Search form, ID, entity…" className="max-w-[280px]" />
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={jur === "all"} onClick={() => setJur("all")}>All</FilterChip>
            <FilterChip active={jur === "PH"}  onClick={() => setJur("PH")}>🇵🇭 PH</FilterChip>
            <FilterChip active={jur === "SG"}  onClick={() => setJur("SG")}>🇸🇬 SG</FilterChip>
            <FilterChip active={jur === "HK"}  onClick={() => setJur("HK")}>🇭🇰 HK</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={status === "all"}            onClick={() => setStatus("all")}>All</FilterChip>
            <FilterChip active={status === "open"}           onClick={() => setStatus("open")} tone="warn">Open</FilterChip>
            <FilterChip active={status === "due-this-week"}  onClick={() => setStatus("due-this-week")} tone="danger">≤7d</FilterChip>
            <FilterChip active={status === "submitted"}      onClick={() => setStatus("submitted")} tone="info">Submitted</FilterChip>
            <FilterChip active={status === "accepted"}       onClick={() => setStatus("accepted")} tone="success">Accepted</FilterChip>
            <span className="w-px h-5 bg-divider mx-1" />
            <FilterChip active={cat === "all"} onClick={() => setCat("all")}>All categories</FilterChip>
            {cats.map(c => <FilterChip key={c} active={cat === c} onClick={() => setCat(c)}>{c}</FilterChip>)}
            <span className="ml-auto text-[11px] text-ink-mute">{filtered.length} of {TAX_FILINGS.length}</span>
          </div>

          <Card>
            <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">Filing</th>
                  <th>Form</th>
                  <th>Jurisdiction</th>
                  <th>Entity</th>
                  <th>Period</th>
                  <th>Due</th>
                  <th>Status</th>
                  <th className="text-right">Amount</th>
                  <th>Preparer</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {filtered.map((t: any) => (
                  <tr key={t.id} className={cn("border-b border-divider-soft hover:bg-cream/40 cursor-pointer transition-colors", t.status === "overdue" && "bg-danger-soft/30")} onClick={() => setSelected(t)}>
                    <td className="px-4 py-2.5 font-mono font-bold text-brand-mid text-[11px]">{t.id}</td>
                    <td>
                      <div className="font-semibold text-ink">{t.form}</div>
                      <div className="text-[10px] text-ink-mute">{t.category}</div>
                    </td>
                    <td className="text-[11px]">{JURISDICTION_LABEL[t.jurisdiction as keyof typeof JURISDICTION_LABEL]}</td>
                    <td className="font-mono text-[11px] text-ink-soft">{t.entity}</td>
                    <td className="text-ink-soft text-[11px]">{t.period}</td>
                    <td>
                      <div className="text-[11px] text-ink-soft">{t.dueDate}</div>
                      <div className={cn("text-[10px] font-semibold",
                        t.daysUntilDue < 0  ? "text-danger" :
                        t.daysUntilDue <= 3 ? "text-warn"   :
                        t.daysUntilDue <= 7 ? "text-accent-deep" : "text-ink-mute"
                      )}>
                        {t.daysUntilDue < 0 ? `${Math.abs(t.daysUntilDue)}d late` : t.daysUntilDue === 0 ? "today" : `${t.daysUntilDue}d`}
                      </div>
                    </td>
                    <td><Badge variant={TAX_STATUS_TONE[t.status as keyof typeof TAX_STATUS_TONE] as any} dot>{TAX_STATUS_LABEL[t.status as keyof typeof TAX_STATUS_LABEL]}</Badge></td>
                    <td className="text-right font-mono text-ink">{t.amountDue ? `${t.currency === "PHP" ? "₱" : t.currency === "SGD" ? "S$" : "HK$"}${t.amountDue.toLocaleString()}` : "—"}</td>
                    <td>
                      <div className="flex items-center gap-1.5">
                        <Avatar initials={t.preparer.initials} tone={t.preparer.tone} size="xs" />
                        <span className="text-[10.5px] text-ink-soft">{t.preparer.name}</span>
                      </div>
                    </td>
                    <td><Icon name="chevron-right" size={12} className="text-ink-faint" /></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </Card>

        </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.form}</h2>
              <div className="text-[11px] text-ink-soft mt-1.5 flex items-center gap-2">
                <Badge variant={TAX_STATUS_TONE[selected.status] as any} dot>{TAX_STATUS_LABEL[selected.status]}</Badge>
                <span>·</span><span>{JURISDICTION_LABEL[selected.jurisdiction]}</span>
                <span>·</span><span>{selected.entity}</span>
              </div>
            </div>
            <SheetClose onClick={() => setSelected(null)} />
          </SheetHeader>
          <SheetBody>
            <section>
              <div className="grid grid-cols-3 gap-3">
                <div className="bg-cream/60 border border-divider rounded-md p-3">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Period</div>
                  <div className="text-[12.5px] font-semibold text-ink mt-0.5">{selected.period}</div>
                </div>
                <div className="bg-cream/60 border border-divider rounded-md p-3">
                  <div className="text-[9px] uppercase tracking-wider font-bold text-ink-mute">Due</div>
                  <div className="text-[12.5px] font-semibold text-ink mt-0.5">{selected.dueDate}</div>
                  <div className={cn("text-[10px] font-semibold mt-0.5",
                    selected.daysUntilDue < 0  ? "text-danger" :
                    selected.daysUntilDue <= 3 ? "text-warn"   : "text-ink-mute"
                  )}>{selected.daysUntilDue < 0 ? `${Math.abs(selected.daysUntilDue)}d overdue` : `${selected.daysUntilDue}d remaining`}</div>
                </div>
                <div className={cn("border rounded-md p-3", selected.amountDue ? "bg-warn-soft/40 border-warn/30" : "bg-cream/60 border-divider")}>
                  <div className={cn("text-[9px] uppercase tracking-wider font-bold", selected.amountDue ? "text-warn" : "text-ink-mute")}>Tax payable</div>
                  <div className={cn("font-serif text-xl mt-0.5", selected.amountDue ? "text-warn" : "text-ink-faint")}>
                    {selected.amountDue ? `${selected.currency === "PHP" ? "₱" : selected.currency === "SGD" ? "S$" : "HK$"}${selected.amountDue.toLocaleString()}` : "—"}
                  </div>
                </div>
              </div>
            </section>

            {selected.notes && (
              <section className="mt-5">
                <div className={cn("rounded-md border p-3 text-[11.5px]",
                  selected.status === "overdue" ? "bg-danger-soft border-danger/30 text-ink-soft" : "bg-cream/60 border-divider text-ink-soft"
                )}>
                  <div className={cn("text-[10px] uppercase tracking-wider font-bold mb-1",
                    selected.status === "overdue" ? "text-danger" : "text-ink-mute"
                  )}>Preparer note</div>
                  {selected.notes}
                </div>
              </section>
            )}

            <section className="mt-5">
              <div className="text-[10px] uppercase tracking-wider font-bold text-ink-mute mb-2">Workflow</div>
              <div className="bg-surface border border-divider rounded-md text-[11.5px] divide-y divide-divider-soft">
                <Step done label="Data pulled from GL / payroll"               by="Auto" when={selected.daysUntilDue > 10 ? selected.dueDate : "Apr 14"} />
                <Step done={selected.status !== "draft"} label="Form prepared" by={selected.preparer.name} when="Apr 15" />
                <Step done={["submitted", "accepted"].includes(selected.status)} current={selected.status === "ready"} label="Submitted to authority" by={selected.status === "submitted" || selected.status === "accepted" ? selected.preparer.name : "—"} when={selected.status === "submitted" || selected.status === "accepted" ? "Apr 16" : "—"} />
                <Step done={selected.status === "accepted"} current={selected.status === "submitted"} label="Acceptance received" by={selected.status === "accepted" ? "Authority" : "—"} when={selected.status === "accepted" ? "Apr 17" : "—"} />
              </div>
            </section>
          </SheetBody>
          <SheetFooter>
            <Button variant="ghost" onClick={() => setSelected(null)}>Close</Button>
            <Button variant="ghost" leadingIcon="printer" className="ml-auto">Print form</Button>
            {selected.status === "draft" && <Button variant="primary" leadingIcon="check">Mark ready</Button>}
            {selected.status === "ready" && <Button variant="primary" leadingIcon="upload">Submit to authority</Button>}
            {selected.status === "overdue" && <Button variant="danger" leadingIcon="alert-triangle">File late (with penalty)</Button>}
          </SheetFooter>
        </Sheet>
      )}
    </>
  )
}

function Step({ done, current, label, by, when }: any) {
  return (
    <div className="px-3 py-2 flex items-center gap-2.5">
      <span className={cn(
        "size-6 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0",
        done    ? "bg-success text-white" :
        current ? "bg-warn text-white"    :
        "bg-cream-deep text-ink-mute"
      )}>
        {done ? <Icon name="check" size={11} strokeWidth={3} /> : current ? "•" : "·"}
      </span>
      <div className="flex-1 min-w-0">
        <div className={cn("text-[11.5px] font-semibold", done ? "text-ink" : current ? "text-ink" : "text-ink-mute")}>{label}</div>
        <div className="text-[10px] text-ink-mute">{by}</div>
      </div>
      <span className="text-[10.5px] font-mono text-ink-faint">{when}</span>
    </div>
  )
}

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

})();/*__IIFE_WRAP_END__*/
