<script setup lang="ts">
// Partner reports. Strict port of PartnerReportsScreen
// (platform-partner-depth.jsx lines 22-318 + 559-852). Four tabs:
//   • Customer health · Stats + per-customer health table (Escalate / Check in)
//   • Revenue        · Stats + MRR sparkline + By plan + Top customers
//   • Churn          · Stats + cohort retention heatmap + exit reasons
//   • Custom reports · Saved reports table + create modal



import type { CustomerOrg, CustomerStatus } from '~/types/partner'
import type { TaskContext } from '~/components/partner/CustomerTaskPanel.vue'

const toast = useToast()
const { request } = useApiFetch()

// Decorative MRR sparkline shape only — historical MRR isn't stored yet (a
// daily-snapshot job lands later; see useMrrTrendline). The live numbers
// below are all real.
const mrrTrend = useMrrTrendline()

// ── Real data sources ─────────────────────────────────────────────────────
const { tenants } = usePartnerTenants()
const { mrrByTenant } = usePartnerMrr()

interface ReportsData {
  health: { healthy: number; watch: number; atRisk: number; total: number; avgScore: number }
  revenueByPlan: Array<{ plan: 'mvp' | 'pro' | 'enterprise'; currency: 'DKK' | 'EUR' | 'USD'; monthlyMinor: number; count: number }>
  topCustomers: Array<{ tenantId: string; tenantName: string; currency: 'DKK' | 'EUR' | 'USD'; monthlyMinor: number; custom: boolean }>
  churnCohorts: Array<{ month: string; total: number; retained: number; retentionPct: number }>
  totals: Array<{ currency: 'DKK' | 'EUR' | 'USD'; monthlyMinor: number }>
  marginPct: number
}
const { data: reports } = useFetch<ReportsData>('/api/partner/reports', {
  key: 'partner-reports',
  default: () => ({
    health: { healthy: 0, watch: 0, atRisk: 0, total: 0, avgScore: 0 },
    revenueByPlan: [],
    topCustomers: [],
    churnCohorts: [],
    totals: [],
    marginPct: 0,
  }),
})

interface SavedReport {
  _id: string
  name: string
  kind: string
  description?: string
  definition?: Record<string, unknown>
  createdByEmail?: string
  createdAt?: string
}
const { data: savedRaw, refresh: refreshSaved } = useFetch<SavedReport[]>(
  '/api/partner/reports/saved',
  { key: 'partner-reports-saved', default: () => [] },
)

const PLAN_INFO: Record<'mvp' | 'pro' | 'enterprise', { slug: CustomerOrg['plan']; label: CustomerOrg['planLabel'] }> = {
  mvp: { slug: 'starter', label: 'Starter' },
  pro: { slug: 'business', label: 'Business' },
  enterprise: { slug: 'enterprise', label: 'Enterprise' },
}
const PLAN_COLOR: Record<'mvp' | 'pro' | 'enterprise', string> = {
  enterprise: 'var(--text)',
  pro: 'var(--info)',
  mvp: 'var(--text-mute)',
}
function mapStatus(s: 'active' | 'pending' | 'suspended' | 'deleted'): CustomerStatus {
  if (s === 'active') return 'healthy'
  if (s === 'pending') return 'trial'
  return 'suspended'
}

const tab = ref<'health' | 'revenue' | 'churn' | 'custom'>('health')
const period = ref<'30d' | '90d' | '12mo' | 'ytd'>('90d')

const tabs = computed(() => [
  { value: 'health',  label: 'Customer health' },
  { value: 'revenue', label: 'Revenue' },
  { value: 'churn',   label: 'Churn' },
  { value: 'custom',  label: 'Custom reports', count: savedRaw.value?.length ?? 0 },
])

const periodOpts = [
  { value: '30d',  label: '30 days' },
  { value: '90d',  label: '90 days' },
  { value: '12mo', label: '12 months' },
  { value: 'ytd',  label: 'Year-to-date' },
] as const

const exportOpen = ref(false)
const newReportOpen = ref(false)

// HEALTH ─────────────────────────────────────────────────────────────────────
// Per-customer rows from real tenants (server-computed healthScore), shaped as
// CustomerOrg so the table + task panel consume them unchanged.
const scored = computed<Array<CustomerOrg & { score: number }>>(() =>
  (tenants.value ?? [])
    .filter((t) => t.status !== 'deleted')
    .map((t) => {
      const info = PLAN_INFO[t.plan ?? 'pro']
      const sub = mrrByTenant.value.get(t._id)
      const score = t.healthScore ?? 100
      return {
        id: t._id,
        name: t.name,
        domain: t.domains?.[0] ?? `${t.slug}.dezky.com`,
        plan: info.slug,
        planLabel: info.label,
        seats: { used: t.userCount ?? 0, total: t.seats ?? 0 },
        health: score,
        score,
        status: mapStatus(t.status),
        mrrDkk: sub ? Math.round(sub.monthlyMinor / 100) : 0,
        brandColor: t.brandColor || '#3F6BFF',
        industry: t.industry ?? '—',
        createdOn: t.createdAt ?? '',
        since: t.createdAt ?? '',
      }
    }),
)

// Cohort counts + average from the server reports endpoint (single source of
// truth for the health buckets).
const cohort = computed(() => ({
  healthy: reports.value?.health.healthy ?? 0,
  watch: reports.value?.health.watch ?? 0,
  risk: reports.value?.health.atRisk ?? 0,
}))
const avgHealth = computed(() => reports.value?.health.avgScore ?? 0)

function healthColor(h: number) {
  if (h >= 80) return 'var(--ok)'
  if (h >= 60) return 'var(--warn)'
  return 'var(--bad)'
}

const taskCtx = ref<TaskContext | null>(null)
function openTask(c: CustomerOrg & { score: number }, mode: 'escalate' | 'checkin') {
  taskCtx.value = { customer: c, score: c.score, mode }
}

// Deterministic mini trend sparkline (30 points) for the per-customer row.
function miniTrend(seed: number) {
  return Array.from({ length: 30 }, (_, i) => 60 + Math.sin((i + seed) / 4) * 12 + ((i * seed) % 5))
}

// REVENUE ────────────────────────────────────────────────────────────────────
// Totals summed across currencies into one headline figure (internal view —
// per-currency totals are in reports.totals). Margin/ARR/ARPU derive from MRR.
const totalMrr = computed(() =>
  Math.round((reports.value?.totals ?? []).reduce((s, t) => s + t.monthlyMinor, 0) / 100),
)
const custCount = computed(() => reports.value?.health.total ?? 0)
const marginMrr = computed(() => Math.round((totalMrr.value * (reports.value?.marginPct ?? 0)) / 100))
const arr = computed(() => totalMrr.value * 12)
const arpu = computed(() => (custCount.value ? Math.round(totalMrr.value / custCount.value) : 0))

const PLAN_LABEL: Record<'mvp' | 'pro' | 'enterprise', string> = { mvp: 'Starter', pro: 'Business', enterprise: 'Enterprise' }
const revenueMix = computed(() => {
  const byPlan = new Map<'mvp' | 'pro' | 'enterprise', number>()
  for (const r of reports.value?.revenueByPlan ?? []) byPlan.set(r.plan, (byPlan.get(r.plan) ?? 0) + r.monthlyMinor)
  const grand = [...byPlan.values()].reduce((a, b) => a + b, 0) || 1
  return [...byPlan.entries()]
    .sort((a, b) => b[1] - a[1])
    .map(([plan, minor]) => ({
      n: PLAN_LABEL[plan],
      v: Math.round(minor / 100),
      p: Math.round((minor / grand) * 100),
      c: PLAN_COLOR[plan],
    }))
})

const topByMrr = computed(() => {
  const colorOf = (id: string) => (tenants.value ?? []).find((t) => t._id === id)?.brandColor || '#3F6BFF'
  return (reports.value?.topCustomers ?? []).slice(0, 5).map((r) => ({
    id: r.tenantId,
    name: r.tenantName,
    brandColor: colorOf(r.tenantId),
    mrrDkk: Math.round(r.monthlyMinor / 100),
  }))
})

// CHURN — signup-month cohorts with (approximate) current retention. Real
// month-over-month retention needs cancellation dates (Phase 3 billing).
const churnRows = computed(() =>
  (reports.value?.churnCohorts ?? []).map((c) => ({
    label: new Date(`${c.month}-01`).toLocaleDateString('da-DK', { month: 'short', year: 'numeric' }),
    total: c.total,
    retained: c.retained,
    retentionPct: c.retentionPct,
  })),
)
const avgRetention = computed(() => {
  const rows = churnRows.value
  return rows.length ? Math.round(rows.reduce((s, r) => s + r.retentionPct, 0) / rows.length) : 0
})

// CUSTOM REPORTS — real saved definitions from /api/partner/reports/saved.
function fmtDate(iso?: string) {
  return iso
    ? new Date(iso).toLocaleDateString('da-DK', { day: '2-digit', month: 'short', year: 'numeric' })
    : '—'
}
const savedReports = computed(() =>
  (savedRaw.value ?? []).map((r) => {
    const def = r.definition ?? {}
    const recips = (def as { recipients?: unknown[] }).recipients
    return {
      id: r._id,
      name: r.name,
      owner: r.createdByEmail || '—',
      schedule: String((def as { schedule?: string }).schedule ?? 'On-demand'),
      last: fmtDate(r.createdAt),
      recipients: Array.isArray(recips) ? recips.length : 0,
      format: String((def as { format?: string }).format ?? 'PDF').toUpperCase(),
    }
  }),
)

const running = ref<string | null>(null)
const reportMenuFor = ref<string | null>(null)
const reportMenuPos = ref<{ top: number; right: number }>({ top: 0, right: 0 })
const confirmDeleteId = ref<string | null>(null)

function runReport(id: string) {
  running.value = id
  const r = savedReports.value.find((x) => x.id === id)
  setTimeout(() => { running.value = null }, 1800)
  if (r) toast.info(`Running ${r.name}`, 'You will be emailed when ready')
}

function openReportMenu(rId: string, e: MouseEvent) {
  e.stopPropagation()
  const btn = (e.currentTarget as HTMLElement).getBoundingClientRect()
  reportMenuPos.value = { top: btn.bottom + 4, right: window.innerWidth - btn.right }
  reportMenuFor.value = reportMenuFor.value === rId ? null : rId
}

function reportActions(r: typeof savedReports.value[number]) {
  return [
    { i: 'external', l: 'Run again',                       fn: () => runReport(r.id) },
    { i: 'download', l: `Download last (${r.format})`,     fn: () => toast.ok('Downloading', `${r.name}.${r.format.toLowerCase()}`) },
    { i: 'mail',     l: 'Send to recipients now',           fn: () => toast.info('Sending', `${r.recipients} recipients`) },
    { i: 'copy',     l: 'Copy shareable link',             fn: () => toast.ok('Link copied') },
    { sep: true },
    { i: 'brush',    l: 'Edit report…',                    fn: () => toast.info('Editing', r.name) },
    { i: 'copy',     l: 'Duplicate',                       fn: () => toast.ok('Duplicated', r.name) },
    { i: 'calendar', l: r.schedule === 'On-demand' ? 'Add schedule…' : 'Pause schedule', fn: () => toast.info('Schedule', r.schedule) },
    { sep: true },
    { i: 'trash',    l: 'Delete report', danger: true,    fn: () => { confirmDeleteId.value = r.id } },
  ] as Array<{ i?: string; l?: string; danger?: boolean; sep?: boolean; fn?: () => void }>
}

const confirmDeleteReport = computed(() => savedReports.value.find((r) => r.id === confirmDeleteId.value))

async function deleteReport() {
  const r = savedReports.value.find((x) => x.id === confirmDeleteId.value)
  if (!r) {
    confirmDeleteId.value = null
    return
  }
  try {
    await request(`/api/partner/reports/saved/${r.id}`, { method: 'DELETE' })
    toast.bad('Report deleted', r.name)
    confirmDeleteId.value = null
    await Promise.all([refreshSaved(), refreshNuxtData('partner-reports-saved')])
  } catch (e: unknown) {
    const err = e as { data?: { message?: string }; statusMessage?: string }
    toast.bad('Delete failed', err.data?.message || err.statusMessage || 'Could not delete report')
  }
}

async function onCreated(payload: {
  name: string
  description: string
  metrics: string[]
  filterPlan: string
  filterStatus: string
  groupBy: string
  schedule: string
  recipients: string[]
  format: string
}) {
  try {
    await request('/api/partner/reports/saved', {
      method: 'POST',
      body: {
        name: payload.name,
        kind: 'custom',
        description: payload.description,
        definition: {
          metrics: payload.metrics,
          filterPlan: payload.filterPlan,
          filterStatus: payload.filterStatus,
          groupBy: payload.groupBy,
          schedule: payload.schedule,
          recipients: payload.recipients,
          format: payload.format,
        },
      },
    })
    toast.ok('Report created', payload.name)
    await Promise.all([refreshSaved(), refreshNuxtData('partner-reports-saved')])
  } catch (e: unknown) {
    const err = e as { data?: { message?: string }; statusMessage?: string }
    toast.bad('Create failed', err.data?.message || err.statusMessage || 'Could not create report')
  }
}

function closeMenu() { reportMenuFor.value = null }

onMounted(() => {
  const onScroll = () => closeMenu()
  document.addEventListener('click', closeMenu)
  window.addEventListener('scroll', onScroll, true)
  window.addEventListener('resize', onScroll)
  onBeforeUnmount(() => {
    document.removeEventListener('click', closeMenu)
    window.removeEventListener('scroll', onScroll, true)
    window.removeEventListener('resize', onScroll)
  })
})
</script>

<template>
  <div>
    <PageHeader
      eyebrow="Analytics"
      title="Partner reports"
      subtitle="Health, revenue, churn, and custom rollups across your customer portfolio."
    >
      <template #actions>
        <UiButton variant="secondary" @click="exportOpen = true">
          <template #leading><UiIcon name="download" :size="14" /></template>
          Export PDF
        </UiButton>
        <UiButton variant="primary" @click="newReportOpen = true">
          <template #leading><UiIcon name="plus" :size="14" /></template>
          New report
        </UiButton>
      </template>
    </PageHeader>

    <div class="tabs-bar">
      <Tabs v-model="tab" :items="tabs" class="tabs-stretch" />
      <div class="period-chip">
        <span class="seg-label">Period</span>
        <select v-model="period">
          <option v-for="o in periodOpts" :key="o.value" :value="o.value">{{ o.label }}</option>
        </select>
      </div>
    </div>

    <!-- HEALTH -->
    <div v-if="tab === 'health'" class="content">
      <div class="stat-strip">
        <Card>
          <Stat label="Healthy" :value="cohort.healthy" :hint="`of ${cohort.healthy + cohort.watch + cohort.risk} customers`" />
        </Card>
        <Card>
          <Stat label="Watch" :value="cohort.watch" />
        </Card>
        <Card>
          <Stat label="At risk" :value="cohort.risk" :delta-tone="cohort.risk > 0 ? 'down' : undefined" />
        </Card>
        <Card>
          <Stat label="Avg health" :value="avgHealth" hint="0–100 score" />
        </Card>
      </div>

      <Card :pad="0">
        <div class="card-head">
          <div>
            <Eyebrow>Per customer</Eyebrow>
            <div class="card-title">Health scores</div>
          </div>
        </div>
        <table class="dtable">
          <thead>
            <tr>
              <th>Customer</th>
              <th>Plan</th>
              <th>Health</th>
              <th>Seat usage</th>
              <th class="num">MRR</th>
              <th>Trend · 90d</th>
              <th class="action-col" />
            </tr>
          </thead>
          <tbody>
            <tr v-for="(c, i) in scored" :key="c.id">
              <td>
                <div class="cust-cell">
                  <div class="cust-swatch" :style="{ background: c.brandColor }" />
                  <div>
                    <div class="cust-name">{{ c.name }}</div>
                    <Mono dim>{{ c.domain }}</Mono>
                  </div>
                </div>
              </td>
              <td>
                <Badge :tone="c.plan === 'enterprise' ? 'invert' : 'neutral'">{{ c.planLabel }}</Badge>
              </td>
              <td>
                <div class="health-cell">
                  <div class="hbar">
                    <div class="hfill" :style="{ width: c.score + '%', background: healthColor(c.score) }" />
                  </div>
                  <Mono>{{ c.score }}</Mono>
                </div>
              </td>
              <td><Mono dim>{{ c.seats.used }}/{{ c.seats.total }} · {{ Math.round(c.seats.used/c.seats.total*100) }}%</Mono></td>
              <td class="num"><Mono>{{ c.mrrDkk > 0 ? c.mrrDkk.toLocaleString('da-DK') + ' DKK' : '—' }}</Mono></td>
              <td>
                <PartnerSparkline :values="miniTrend(i + 1)" :width="80" :height="22" stroke="var(--text)" fill="transparent" :show-dot="false" />
              </td>
              <td class="action-col">
                <UiButton v-if="c.score < 50" size="sm" variant="primary" @click="openTask(c, 'escalate')">Escalate</UiButton>
                <UiButton v-else-if="c.score < 75" size="sm" variant="secondary" @click="openTask(c, 'checkin')">Check in</UiButton>
                <Mono v-else dim>—</Mono>
              </td>
            </tr>
          </tbody>
        </table>
      </Card>
    </div>

    <!-- REVENUE -->
    <div v-if="tab === 'revenue'" class="content">
      <div class="stat-strip">
        <Card>
          <Stat label="MRR · current" :value="`${totalMrr.toLocaleString('da-DK')} DKK`" hint="across all customers" />
        </Card>
        <Card>
          <Stat label="Partner margin" :value="`${marginMrr.toLocaleString('da-DK')} DKK`" :hint="`${reports?.marginPct ?? 0}% of MRR`" />
        </Card>
        <Card>
          <Stat label="ARR · projected" :value="`${arr.toLocaleString('da-DK')} DKK`" />
        </Card>
        <Card>
          <Stat label="ARPU" :value="`${arpu.toLocaleString('da-DK')} DKK`" hint="per customer / mo" />
        </Card>
      </div>

      <Card :pad="0">
        <div class="card-head">
          <div>
            <Eyebrow>MRR · last 90 days</Eyebrow>
            <div class="card-title">Trend</div>
          </div>
        </div>
        <div class="big-chart">
          <PartnerSparkline :values="mrrTrend" :width="1080" :height="160" stroke="var(--text)" fill="var(--row-hover)" />
          <div class="chart-foot">
            <span>90-day trend · illustrative</span>
            <span>{{ totalMrr.toLocaleString('da-DK') }} DKK / mo now</span>
          </div>
        </div>
      </Card>

      <div class="grid-2">
        <Card>
          <Eyebrow>By plan</Eyebrow>
          <div class="card-title">Revenue mix</div>
          <div class="plan-mix">
            <div v-for="p in revenueMix" :key="p.n" class="mix-row">
              <div class="mix-head">
                <span class="mix-name">{{ p.n }}</span>
                <Mono>{{ p.v.toLocaleString('da-DK') }} DKK · {{ p.p }}%</Mono>
              </div>
              <div class="mix-bar"><div class="mix-fill" :style="{ width: p.p + '%', background: p.c }" /></div>
            </div>
          </div>
        </Card>
        <Card>
          <Eyebrow>Top customers</Eyebrow>
          <div class="card-title">By MRR</div>
          <div class="top-list">
            <div v-for="c in topByMrr" :key="c.id" class="top-row">
              <div class="top-swatch" :style="{ background: c.brandColor }" />
              <span class="top-name">{{ c.name }}</span>
              <Mono>{{ c.mrrDkk.toLocaleString('da-DK') }} DKK</Mono>
            </div>
          </div>
        </Card>
      </div>
    </div>

    <!-- CHURN -->
    <div v-if="tab === 'churn'" class="content">
      <div class="stat-strip">
        <Card>
          <Stat label="Signup cohorts" :value="churnRows.length" />
        </Card>
        <Card>
          <Stat label="Avg retention" :value="`${avgRetention}%`" :delta-tone="avgRetention >= 80 ? 'up' : 'down'" />
        </Card>
        <Card>
          <Stat label="Active customers" :value="cohort.healthy + cohort.watch" />
        </Card>
        <Card>
          <Stat label="At risk" :value="cohort.risk" :delta-tone="cohort.risk > 0 ? 'down' : undefined" />
        </Card>
      </div>

      <Card :pad="0">
        <div class="card-head">
          <div>
            <Eyebrow>Cohort retention</Eyebrow>
            <div class="card-title">Customers by signup month</div>
          </div>
        </div>
        <div class="cohort-wrap">
          <table class="cohort">
            <thead>
              <tr>
                <th>Cohort</th>
                <th>Customers</th>
                <th>Retained</th>
                <th>Retention</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="(c, i) in churnRows" :key="i">
                <td><Mono>{{ c.label }}</Mono></td>
                <td class="cohort-size"><Mono>{{ c.total }}</Mono></td>
                <td><Mono>{{ c.retained }}</Mono></td>
                <td class="cell">
                  <span
                    class="heat"
                    :style="{
                      background: c.retentionPct >= 90 ? 'rgba(31,138,91,0.16)' : c.retentionPct >= 70 ? 'rgba(232,154,31,0.16)' : 'rgba(226,48,48,0.16)',
                      color: c.retentionPct >= 90 ? 'var(--ok)' : c.retentionPct >= 70 ? 'var(--warn)' : 'var(--bad)',
                    }"
                  >{{ c.retentionPct }}%</span>
                </td>
              </tr>
              <tr v-if="!churnRows.length">
                <td colspan="4" class="cohort-empty"><Mono dim>// no signup cohorts yet</Mono></td>
              </tr>
            </tbody>
          </table>
        </div>
      </Card>

      <Card>
        <Eyebrow>Why customers leave</Eyebrow>
        <div class="card-title">Top exit reasons (last 12 months)</div>
        <p class="exit-empty">
          No churn yet. When customers do leave, exit reasons will surface here automatically (from cancel/pause flow inputs).
        </p>
      </Card>
    </div>

    <!-- CUSTOM REPORTS -->
    <div v-if="tab === 'custom'" class="content">
      <div class="custom-head">
        <p class="custom-blurb">
          Build a report once, schedule or run on demand. We'll email a PDF to the recipients you specify.
        </p>
        <UiButton variant="primary" @click="newReportOpen = true">
          <template #leading><UiIcon name="plus" :size="14" /></template>
          New custom report
        </UiButton>
      </div>

      <Card :pad="0">
        <table class="dtable">
          <thead>
            <tr>
              <th>Report</th>
              <th>Schedule</th>
              <th>Owner</th>
              <th>Last run</th>
              <th class="action-col" />
            </tr>
          </thead>
          <tbody>
            <tr v-for="r in savedReports" :key="r.id">
              <td><span class="cust-name">{{ r.name }}</span></td>
              <td><Mono>{{ r.schedule }}</Mono></td>
              <td>
                <div class="owner-cell">
                  <Avatar :name="r.owner" :size="20" />
                  <span>{{ r.owner }}</span>
                </div>
              </td>
              <td><Mono dim>{{ r.last }}</Mono></td>
              <td class="action-col" @click.stop>
                <div class="actions-row">
                  <UiButton size="sm" variant="ghost" @click="runReport(r.id)">
                    <template #leading>
                      <UiIcon :name="running === r.id ? 'refresh' : 'external'" :size="13" />
                    </template>
                    {{ running === r.id ? 'Running…' : 'Run' }}
                  </UiButton>
                  <button class="kebab" @click="openReportMenu(r.id, $event)">
                    <UiIcon name="more" :size="13" />
                  </button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </Card>
    </div>

    <!-- Portaled custom report row actions menu -->
    <Teleport to="body">
      <div
        v-if="reportMenuFor"
        class="menu"
        :style="{ top: reportMenuPos.top + 'px', right: reportMenuPos.right + 'px' }"
        @click.stop
      >
        <template v-for="(it, i) in reportActions(savedReports.find(r => r.id === reportMenuFor)!)" :key="i">
          <div v-if="it.sep" class="menu-sep" />
          <button
            v-else
            class="menu-item"
            :class="{ danger: it.danger }"
            @click="(it.fn?.(), closeMenu())"
          >
            <UiIcon :name="(it.i as any)" :size="14" />
            <span>{{ it.l }}</span>
          </button>
        </template>
      </div>
    </Teleport>

    <!-- Confirm delete report modal -->
    <Modal
      :open="!!confirmDeleteId"
      eyebrow="Permanent action"
      title="Delete this report?"
      size="sm"
      @close="confirmDeleteId = null"
    >
      <template v-if="confirmDeleteReport">
        <div class="danger-callout">
          <UiIcon name="trash" :size="16" />
          <p>
            The report configuration and its schedule will be deleted. Past PDFs already delivered to recipients are unaffected.
          </p>
        </div>
        <div class="del-summary">
          <dl class="def">
            <div><dt>Report</dt><dd>{{ confirmDeleteReport.name }}</dd></div>
            <div><dt>Schedule</dt><dd>{{ confirmDeleteReport.schedule }}</dd></div>
            <div><dt>Recipients</dt><dd>{{ confirmDeleteReport.recipients }} addresses</dd></div>
            <div><dt>Last run</dt><dd>{{ confirmDeleteReport.last }}</dd></div>
          </dl>
        </div>
      </template>
      <template #footer>
        <UiButton variant="ghost" @click="confirmDeleteId = null">Cancel</UiButton>
        <UiButton variant="danger" @click="deleteReport">
          <template #leading><UiIcon name="trash" :size="14" /></template>
          Delete report
        </UiButton>
      </template>
    </Modal>

    <PartnerCustomerTaskPanel
      :task="taskCtx"
      @close="taskCtx = null"
      @save="(t) => toast.ok(t.mode === 'escalate' ? 'Escalation created' : 'Check-in scheduled', t.customer.name)"
    />
    <PartnerNewCustomReportModal
      :open="newReportOpen"
      @close="newReportOpen = false"
      @created="onCreated"
    />

    <!-- Export PDF Modal -->
    <Modal
      :open="exportOpen"
      eyebrow="Partner reports · export"
      title="Export reports to PDF"
      size="md"
      @close="exportOpen = false"
    >
      <p class="export-blurb">Select which tabs to include in the PDF, the period, cover style, and how you want it delivered.</p>
      <div class="export-meta">
        <Mono dim>// pdf preview · 3 sections · estimated 11 pages · NordicMSP cover + footer</Mono>
      </div>
      <template #footer>
        <UiButton variant="ghost" @click="exportOpen = false">Cancel</UiButton>
        <UiButton variant="primary" @click="exportOpen = false; toast.ok('PDF queued', 'You will be emailed when ready')">
          <template #leading><UiIcon name="download" :size="14" /></template>
          Download PDF
        </UiButton>
      </template>
    </Modal>
  </div>
</template>

<style scoped>
.tabs-bar {
  padding: 0 40px;
  margin-top: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.tabs-stretch { flex: 1; }
.period-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 0 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.period-chip .seg-label {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.period-chip select {
  border: none;
  background: transparent;
  font-family: inherit;
  font-size: 13px;
  color: var(--text);
  padding: 8px 4px;
  cursor: pointer;
}
.period-chip select:focus { outline: none; }

.content { padding: 20px 40px 64px; display: flex; flex-direction: column; gap: 16px; }

.stat-strip { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }

.card-head {
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.card-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 17px;
  margin-top: 4px;
}

.big-chart { padding: 20px; }
.big-chart :deep(svg) { width: 100%; height: 160px; }
.chart-foot {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-mute);
}

.dtable { width: 100%; border-collapse: collapse; }
.dtable th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  font-weight: 500;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
}
.dtable th.num { text-align: right; }
.dtable th.action-col, .dtable td.action-col { width: 160px; text-align: right; }
.dtable td {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 13px;
  vertical-align: middle;
}
.dtable td.num { text-align: right; }
.dtable tbody tr:hover { background: var(--row-hover); }

.cust-cell { display: flex; align-items: center; gap: 12px; }
.cust-swatch { width: 22px; height: 22px; border-radius: 4px; flex-shrink: 0; }
.cust-name { font-size: 13px; font-weight: 500; }

.owner-cell { display: flex; align-items: center; gap: 8px; }
.owner-cell span { font-size: 12px; }

.health-cell { display: inline-flex; align-items: center; gap: 10px; }
.hbar {
  width: 90px;
  height: 5px;
  background: var(--border);
  border-radius: 999px;
  overflow: hidden;
}
.hfill { height: 100%; }

/* Revenue · plan mix */
.plan-mix { display: flex; flex-direction: column; gap: 12px; margin-top: 4px; }
.mix-head { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 6px; }
.mix-name { font-weight: 500; }
.mix-bar { height: 6px; background: var(--border); border-radius: 999px; overflow: hidden; }
.mix-fill { height: 100%; }

.top-list { display: flex; flex-direction: column; gap: 10px; margin-top: 4px; }
.top-row {
  display: grid;
  grid-template-columns: 20px 1fr 110px;
  gap: 12px;
  align-items: center;
}
.top-swatch { width: 14px; height: 14px; border-radius: 3px; }
.top-name { font-size: 13px; font-weight: 500; }

/* Cohort heatmap */
.cohort-wrap { overflow-x: auto; }
.cohort { width: 100%; border-collapse: collapse; min-width: 600px; }
.cohort th {
  padding: 10px 16px;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
  font-weight: 500;
  border-bottom: 1px solid var(--border);
}
.cohort th:first-child { text-align: left; padding-left: 20px; }
.cohort td { padding: 10px 16px; text-align: center; border-bottom: 1px solid var(--border); }
.cohort td:first-child { text-align: left; padding-left: 20px; }
.cohort-size { font-family: var(--font-mono); font-size: 12px; }
.cohort .cell { padding: 6px; }
.heat {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 22px;
  border-radius: 4px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
}

.exit-empty { font-size: 13px; color: var(--text-mute); line-height: 1.6; margin: 8px 0 0; }

/* Custom reports */
.custom-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 0; }
.custom-blurb { font-size: 13px; color: var(--text-mute); margin: 0; max-width: 540px; line-height: 1.5; }
.actions-row { display: flex; gap: 4px; justify-content: flex-end; align-items: center; }
.kebab {
  background: transparent;
  border: none;
  color: var(--text-mute);
  padding: 4px;
  border-radius: 4px;
  cursor: pointer;
}
.kebab:hover { background: var(--row-hover); color: var(--text); }

/* Confirm delete + Export modals */
.danger-callout {
  padding: 14px;
  background: rgba(226, 48, 48, 0.06);
  border: 1px solid rgba(226, 48, 48, 0.22);
  border-radius: 6px;
  display: flex;
  gap: 10px;
  margin-bottom: 14px;
}
.danger-callout :deep(svg) { color: var(--bad); margin-top: 2px; flex-shrink: 0; }
.danger-callout p { font-size: 13px; color: var(--text-dim); line-height: 1.5; margin: 0; }
.del-summary {
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.def { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 0; }
.def div { display: grid; grid-template-columns: 120px 1fr; gap: 12px; font-size: 13px; }
.def dt { color: var(--text-mute); font-family: var(--font-mono); font-size: 11px; }
.def dd { margin: 0; }

.export-blurb { font-size: 13px; color: var(--text-dim); margin: 0 0 14px; line-height: 1.55; }
.export-meta {
  padding: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
}
</style>

<style>
/* Portaled menu */
.menu {
  position: fixed;
  min-width: 240px;
  padding: 4px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.18);
  z-index: 100;
}
.menu .menu-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border-radius: 5px;
  background: transparent;
  border: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 13px;
  text-align: left;
  color: var(--text);
}
.menu .menu-item:hover { background: var(--row-hover); }
.menu .menu-item.danger { color: var(--bad); }
.menu .menu-item svg { color: var(--text-mute); flex-shrink: 0; }
.menu .menu-item.danger svg { color: var(--bad); }
.menu .menu-item span { flex: 1; }
.menu .menu-sep { height: 1px; background: var(--border); margin: 4px 0; }
</style>
