<script setup lang="ts">
import type { WelcomeDashboardKpi } from '~/types/welcome-dashboard'
import {
  DASHBOARD_PRESET_ORDER,
  DASHBOARD_ROLE_PRESETS,
  DASHBOARD_WIDGETS,
  type DashboardRolePresetId,
  type DashboardWidgetId
} from '~/composables/useDashboardHomeWidgets'

const { saved: homeBranding } = useBrokerageBranding()
const welcome = useWelcomeDashboard()

const {
  widgets,
  widgetOrder,
  layoutUnlocked,
  activePreset,
  isPresetDirty,
  applyPreset,
  setWidget,
  reapplySelectedPreset,
  reorderWidgets
} = useDashboardHomeWidgets()

const dashConfigOpen = ref(false)
const draggingWidget = ref<DashboardWidgetId | null>(null)

function onDragStart(wid: DashboardWidgetId, e: DragEvent) {
  if (!layoutUnlocked.value) { e.preventDefault(); return }
  draggingWidget.value = wid
  try { e.dataTransfer?.setData('text/plain', wid); if (e.dataTransfer) e.dataTransfer.effectAllowed = 'move' } catch { /* */ }
}
function onDragEnd() { draggingWidget.value = null }
function onDropSection(target: DashboardWidgetId, e: DragEvent) {
  e.preventDefault()
  if (!layoutUnlocked.value) return
  const raw = draggingWidget.value ?? e.dataTransfer?.getData('text/plain')
  const from = raw as DashboardWidgetId | undefined
  if (!from || from === target) return
  reorderWidgets(from, target)
  draggingWidget.value = null
}
function toggleLayoutUnlock() { layoutUnlocked.value = !layoutUnlocked.value }

/* ---- Notes scratchpad (sticky notes, persisted locally) ---- */
const NOTES_KEY = 'policy-ui.dashboard.notes.v2'

interface StickyNote {
  id: string
  content: string
  color: string
  expanded: boolean
  pinned: boolean
  createdAt: number
}

const STICKY_COLORS = [
  { id: 'yellow', bg: '#fef9c3', border: '#fde047', label: 'Yellow' },
  { id: 'green', bg: '#dcfce7', border: '#86efac', label: 'Green' },
  { id: 'blue', bg: '#dbeafe', border: '#93c5fd', label: 'Blue' },
  { id: 'pink', bg: '#fce7f3', border: '#f9a8d4', label: 'Pink' },
  { id: 'orange', bg: '#ffedd5', border: '#fdba74', label: 'Orange' },
]

const stickyNotes = ref<StickyNote[]>([
  { id: 'n1', content: 'Follow up with Constructora Delta on fleet renewal — need 3 comparative quotes by Friday.', color: 'yellow', expanded: false, pinned: true, createdAt: Date.now() - 86400000 },
  { id: 'n2', content: 'Ask Marco about the Clínica Norte health plan upsell.', color: 'green', expanded: false, pinned: false, createdAt: Date.now() - 3600000 },
  { id: 'n3', content: 'Board deck due end of month — pull YTD numbers from the pipeline report and retention dashboard.', color: 'blue', expanded: false, pinned: false, createdAt: Date.now() - 7200000 },
  { id: 'n4', content: 'Call Sofía re: grace period.', color: 'pink', expanded: false, pinned: false, createdAt: Date.now() - 1800000 },
])
let nextNoteId = 5

function addStickyNote() {
  const colors = STICKY_COLORS.map(c => c.id)
  const color = colors[stickyNotes.value.length % colors.length] ?? 'yellow'
  const note: StickyNote = { id: `n${nextNoteId++}`, content: '', color, expanded: true, pinned: false, createdAt: Date.now() }
  stickyNotes.value = [note, ...stickyNotes.value]
}

function removeStickyNote(id: string) {
  stickyNotes.value = stickyNotes.value.filter(n => n.id !== id)
}

function toggleStickyExpand(id: string) {
  const note = stickyNotes.value.find(n => n.id === id)
  if (note) note.expanded = !note.expanded
}

function toggleStickyPin(id: string) {
  const note = stickyNotes.value.find(n => n.id === id)
  if (note) note.pinned = !note.pinned
}

function stickyColor(colorId: string) {
  return STICKY_COLORS.find(c => c.id === colorId) ?? STICKY_COLORS[0]!
}

function cycleStickyColor(id: string) {
  const note = stickyNotes.value.find(n => n.id === id)
  if (!note) return
  const idx = STICKY_COLORS.findIndex(c => c.id === note.color)
  note.color = STICKY_COLORS[(idx + 1) % STICKY_COLORS.length]!.id
}

function stickyPreview(content: string) {
  if (!content) return 'Empty note'
  return content.length > 60 ? content.slice(0, 60) + '...' : content
}

const sortedStickyNotes = computed(() => {
  const pinned = stickyNotes.value.filter(n => n.pinned)
  const unpinned = stickyNotes.value.filter(n => !n.pinned)
  return [...pinned, ...unpinned]
})

onMounted(() => {
  if (typeof localStorage !== 'undefined') {
    try {
      const raw = localStorage.getItem(NOTES_KEY)
      if (raw) {
        const parsed = JSON.parse(raw)
        if (Array.isArray(parsed)) stickyNotes.value = parsed
      }
    } catch { /* ignore */ }
  }
})
watch(stickyNotes, (v) => {
  if (typeof localStorage !== 'undefined') {
    try { localStorage.setItem(NOTES_KEY, JSON.stringify(v)) } catch { /* quota */ }
  }
}, { deep: true })

/* ---- Tasks & alerts interactivity ---- */
interface DashTask {
  id: string
  title: string
  emphasis: boolean
  done: boolean
}

const dashTasks = ref<DashTask[]>([])
const dashAlertsDismissed = ref<Set<string>>(new Set())
const newTaskText = ref('')
const addingTask = ref(false)

onMounted(() => {
  // Hydrate tasks from composable data + localStorage state
  const savedDone = new Set<string>()
  if (typeof localStorage !== 'undefined') {
    try {
      const raw = localStorage.getItem('policy-ui.dashboard.tasks-done')
      if (raw) JSON.parse(raw).forEach((id: string) => savedDone.add(id))
    } catch { /* */ }
    try {
      const raw = localStorage.getItem('policy-ui.dashboard.alerts-dismissed')
      if (raw) JSON.parse(raw).forEach((id: string) => dashAlertsDismissed.value.add(id))
    } catch { /* */ }
    try {
      const raw = localStorage.getItem('policy-ui.dashboard.user-tasks')
      if (raw) {
        const userTasks = JSON.parse(raw) as DashTask[]
        // Will merge below
        dashTasks.value = [
          ...welcome.value.dailyTasks.map(t => ({
            id: t.id,
            title: t.title,
            emphasis: t.emphasis,
            done: savedDone.has(t.id),
          })),
          ...userTasks,
        ]
        return
      }
    } catch { /* */ }
  }
  dashTasks.value = welcome.value.dailyTasks.map(t => ({
    id: t.id,
    title: t.title,
    emphasis: t.emphasis,
    done: savedDone.has(t.id),
  }))
})

function toggleTask(id: string) {
  const task = dashTasks.value.find(t => t.id === id)
  if (task) {
    task.done = !task.done
    persistTaskState()
  }
}

function addQuickTask() {
  if (!newTaskText.value.trim()) return
  const task: DashTask = {
    id: `user-task-${Date.now()}`,
    title: newTaskText.value.trim(),
    emphasis: false,
    done: false,
  }
  dashTasks.value = [task, ...dashTasks.value]
  newTaskText.value = ''
  addingTask.value = false
  persistTaskState()
}

function removeTask(id: string) {
  dashTasks.value = dashTasks.value.filter(t => t.id !== id)
  persistTaskState()
}

function dismissAlert(id: string) {
  dashAlertsDismissed.value.add(id)
  if (typeof localStorage !== 'undefined') {
    try { localStorage.setItem('policy-ui.dashboard.alerts-dismissed', JSON.stringify([...dashAlertsDismissed.value])) } catch { /* */ }
  }
}

function persistTaskState() {
  if (typeof localStorage === 'undefined') return
  try {
    const doneIds = dashTasks.value.filter(t => t.done).map(t => t.id)
    localStorage.setItem('policy-ui.dashboard.tasks-done', JSON.stringify(doneIds))
    const userTasks = dashTasks.value.filter(t => t.id.startsWith('user-task-'))
    localStorage.setItem('policy-ui.dashboard.user-tasks', JSON.stringify(userTasks))
  } catch { /* */ }
}

const visibleAlerts = computed(() =>
  alertsWithMeta.value.filter(a => !dashAlertsDismissed.value.has(a.id))
)

const completedTaskCount = computed(() => dashTasks.value.filter(t => t.done).length)
const totalTaskCount = computed(() => dashTasks.value.length)

/* ---- Milestone tracker configurability ---- */
type MilestoneMetric = { id: string; label: string; actual: string; target: string; pct: number; unit: string }

const MILESTONE_METRICS: MilestoneMetric[] = [
  { id: 'premium', label: 'Premium', actual: '$1.24M', target: '$1.18M', pct: 105, unit: '$' },
  { id: 'policies', label: 'Policies', actual: '42', target: '40', pct: 105, unit: '' },
  { id: 'quotes', label: 'Quotes sent', actual: '68', target: '55', pct: 124, unit: '' },
  { id: 'close-rate', label: 'Close rate', actual: '34%', target: '30%', pct: 113, unit: '%' },
  { id: 'new-clients', label: 'New clients', actual: '8', target: '6', pct: 133, unit: '' },
  { id: 'retention', label: 'Retention', actual: '91%', target: '88%', pct: 103, unit: '%' },
]

const MILESTONE_STORAGE_KEY = 'policy-ui.dashboard.milestone-metrics'
const activeMilestoneIds = ref<string[]>(['premium', 'policies'])

onMounted(() => {
  if (typeof localStorage !== 'undefined') {
    try {
      const raw = localStorage.getItem(MILESTONE_STORAGE_KEY)
      if (raw) {
        const parsed = JSON.parse(raw)
        if (Array.isArray(parsed) && parsed.length > 0) activeMilestoneIds.value = parsed
      }
    } catch { /* */ }
  }
})

function toggleMilestoneMetric(id: string) {
  const idx = activeMilestoneIds.value.indexOf(id)
  if (idx >= 0) {
    if (activeMilestoneIds.value.length <= 1) return
    activeMilestoneIds.value = activeMilestoneIds.value.filter(x => x !== id)
  } else {
    if (activeMilestoneIds.value.length >= 4) return
    activeMilestoneIds.value = [...activeMilestoneIds.value, id]
  }
  if (typeof localStorage !== 'undefined') {
    try { localStorage.setItem(MILESTONE_STORAGE_KEY, JSON.stringify(activeMilestoneIds.value)) } catch { /* */ }
  }
}

const activeMilestones = computed(() =>
  activeMilestoneIds.value
    .map(id => MILESTONE_METRICS.find(m => m.id === id))
    .filter((m): m is MilestoneMetric => !!m)
)

const milestoneAvgPct = computed(() => {
  if (!activeMilestones.value.length) return 0
  return Math.round(activeMilestones.value.reduce((s, m) => s + m.pct, 0) / activeMilestones.value.length)
})

const milestoneStatus = computed(() => {
  const avg = milestoneAvgPct.value
  if (avg >= 100) return { label: 'On track', color: 'var(--h2-success)', icon: 'i-heroicons-check-badge' }
  if (avg >= 85) return { label: 'Close', color: 'var(--h2-warning)', icon: 'i-heroicons-exclamation-circle' }
  return { label: 'Behind', color: 'var(--h2-error)', icon: 'i-heroicons-x-circle' }
})

const milestoneConfigOpen = ref(false)
let milestoneConfigSkipClose = false

function onMilestoneConfigClick(e: MouseEvent) {
  e.stopPropagation()
  e.preventDefault()
  milestoneConfigSkipClose = true
  milestoneConfigOpen.value = !milestoneConfigOpen.value
  setTimeout(() => { milestoneConfigSkipClose = false }, 200)
}

/* ---- Chart widget config ---- */
type ChartTimeRange = '3m' | '6m' | '12m'
const chartTimeRange = ref<ChartTimeRange>('6m')

type ChartMetricId = 'gwp' | 'policies' | 'retention' | 'new-biz' | 'loss-ratio' | 'commission'
type ChartTypeId = 'line' | 'bar' | 'area'

interface ChartMetricOption {
  id: ChartMetricId
  label: string
  data6m: { m: string; v: number; display: string }[]
  change: string
  changeTone: 'positive' | 'negative' | 'neutral'
}

const CHART_METRICS: ChartMetricOption[] = [
  { id: 'gwp', label: 'GWP written', change: '+6.2%', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 72, display: '$4.52M' }, { m: 'Nov', v: 68, display: '$4.28M' },
      { m: 'Dec', v: 76, display: '$4.71M' }, { m: 'Jan', v: 74, display: '$4.61M' },
      { m: 'Feb', v: 81, display: '$4.98M' }, { m: 'Mar', v: 88, display: '$5.41M' }
    ] },
  { id: 'policies', label: 'Policies bound', change: '+12', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 32, display: '32' }, { m: 'Nov', v: 28, display: '28' },
      { m: 'Dec', v: 35, display: '35' }, { m: 'Jan', v: 38, display: '38' },
      { m: 'Feb', v: 36, display: '36' }, { m: 'Mar', v: 42, display: '42' }
    ] },
  { id: 'retention', label: 'Retention rate', change: '+1.2%', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 88, display: '88%' }, { m: 'Nov', v: 87, display: '87%' },
      { m: 'Dec', v: 89, display: '89%' }, { m: 'Jan', v: 90, display: '90%' },
      { m: 'Feb', v: 90, display: '90%' }, { m: 'Mar', v: 91, display: '91%' }
    ] },
  { id: 'new-biz', label: 'New business', change: '+$820K', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 52, display: '$2.4M' }, { m: 'Nov', v: 48, display: '$2.2M' },
      { m: 'Dec', v: 62, display: '$2.9M' }, { m: 'Jan', v: 58, display: '$2.7M' },
      { m: 'Feb', v: 64, display: '$3.0M' }, { m: 'Mar', v: 70, display: '$3.2M' }
    ] },
  { id: 'loss-ratio', label: 'Loss ratio', change: '-3.1%', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 68, display: '68%' }, { m: 'Nov', v: 72, display: '72%' },
      { m: 'Dec', v: 65, display: '65%' }, { m: 'Jan', v: 63, display: '63%' },
      { m: 'Feb', v: 60, display: '60%' }, { m: 'Mar', v: 58, display: '58%' }
    ] },
  { id: 'commission', label: 'Commission rev', change: '+8.4%', changeTone: 'positive',
    data6m: [
      { m: 'Oct', v: 55, display: '$680K' }, { m: 'Nov', v: 52, display: '$640K' },
      { m: 'Dec', v: 60, display: '$740K' }, { m: 'Jan', v: 58, display: '$715K' },
      { m: 'Feb', v: 65, display: '$800K' }, { m: 'Mar', v: 72, display: '$886K' }
    ] },
]

const activeChartMetric = ref<ChartMetricId>('gwp')
const activeChartType = ref<ChartTypeId>('area')
const chartMetricMenuOpen = ref(false)

const selectedChartMetric = computed(() =>
  CHART_METRICS.find(m => m.id === activeChartMetric.value) ?? CHART_METRICS[0]!
)

const chartData = computed(() => {
  const metric = selectedChartMetric.value
  const range = chartTimeRange.value
  if (range === '3m') return metric.data6m.slice(-3)
  return metric.data6m // 6m default (we reuse for 12m with a label tweak)
})

const chartLatest = computed(() => chartData.value[chartData.value.length - 1]!)

/* Dynamic chart SVG model */
const chartSvgModel = computed(() => {
  const pts = chartData.value.map(b => b.v)
  const viewW = 400; const viewH = 152; const padX = 8; const padY = 14
  const innerW = viewW - padX * 2; const innerH = viewH - padY * 2
  const maxV = Math.max(...pts, 1); const minV = Math.min(...pts, 0)
  const span = maxV - minV || 1
  const points = pts.map((p, i) => ({
    x: padX + (i / Math.max(1, pts.length - 1)) * innerW,
    y: padY + (1 - (p - minV) / span) * innerH, v: p
  }))
  const bottomY = padY + innerH
  const first = points[0]!; const last = points[points.length - 1]!

  // Line + area paths
  let lineD = `M ${first.x},${first.y}`
  let areaD = `M ${first.x},${bottomY} L ${first.x},${first.y}`
  for (let i = 1; i < points.length; i++) {
    const p0 = points[i - 1]!; const p1 = points[i]!
    const cx = (p0.x + p1.x) / 2
    const seg = ` C ${cx},${p0.y} ${cx},${p1.y} ${p1.x},${p1.y}`
    lineD += seg; areaD += seg
  }
  areaD += ` L ${last.x},${bottomY} Z`

  // Bar geometry
  const barW = Math.min(innerW / pts.length * 0.6, 40)
  const bars = pts.map((p, i) => ({
    x: padX + (i / Math.max(1, pts.length - 1)) * innerW - barW / 2,
    y: padY + (1 - (p - minV) / span) * innerH,
    w: barW,
    h: ((p - minV) / span) * innerH,
  }))

  const gridYs = [0, 0.5, 1].map(t => padY + t * innerH)
  return { lineD, areaD, points, bars, gridYs, viewW, viewH, padX, innerW, bottomY }
})

type PipelineViewId = 'summary' | 'stage' | 'lob'
const pipelineView = ref<PipelineViewId>('summary')
const hoveredSegment = ref<number | null>(null)

const pipelineStages = [
  { label: 'Prospect', count: 24, value: '$2.1M', pct: 34, color: '#01696f' },
  { label: 'Quoted', count: 38, value: '$6.2M', pct: 100, color: '#1a8a8a' },
  { label: 'Negotiating', count: 12, value: '$3.8M', pct: 61, color: '#2dd4bf' },
  { label: 'Closing', count: 8, value: '$2.4M', pct: 39, color: '#0f7b5f' },
  { label: 'Won MTD', count: 14, value: '$1.8M', pct: 29, color: '#065f46' },
]

const pipelineLobBreakdown = [
  { label: 'Auto', count: 18, value: '$1.2M', pct: 19 },
  { label: 'Health', count: 12, value: '$3.4M', pct: 55 },
  { label: 'Life', count: 8, value: '$0.8M', pct: 13 },
  { label: 'Property', count: 6, value: '$0.5M', pct: 8 },
  { label: 'Other', count: 4, value: '$0.3M', pct: 5 },
]

const presetSelectItems = computed(() =>
  DASHBOARD_PRESET_ORDER.map((id) => ({ label: DASHBOARD_ROLE_PRESETS[id].label, value: id }))
)
const activePresetHint = computed(() => DASHBOARD_ROLE_PRESETS[activePreset.value]?.hint ?? '')

watch(() => welcome.value.productName, (name) => {
  if (typeof document !== 'undefined') document.title = name ? `Home \u00b7 ${name}` : 'Home'
}, { immediate: true })

/* ---- sparkline helpers ---- */
const kpiSparkSeries: Record<string, number[]> = {
  ms: [42, 44, 41, 46, 48, 45, 49, 52, 50, 54, 53, 56],
  mr: [58, 59, 57, 61, 62, 60, 63, 65, 64, 67, 66, 68],
  ren: [72, 70, 74, 73, 75, 76, 74, 77, 78, 76, 79, 80],
  late: [55, 52, 54, 50, 48, 47, 45, 44, 43, 42, 40, 38]
}

function smoothSparklinePath(points: number[], w = 112, h = 32, pad = 2) {
  const max = Math.max(...points); const min = Math.min(...points); const r = max - min || 1
  const pts = points.map((p, i, arr) => ({
    x: pad + (i / Math.max(1, arr.length - 1)) * (w - pad * 2),
    y: pad + (1 - (p - min) / r) * (h - pad * 2)
  }))
  if (pts.length < 2) return ''
  let d = `M ${pts[0]!.x},${pts[0]!.y}`
  for (let i = 0; i < pts.length - 1; i++) {
    const p0 = pts[i]!; const p1 = pts[i + 1]!; const cx = (p0.x + p1.x) / 2
    d += ` C ${cx},${p0.y} ${cx},${p1.y} ${p1.x},${p1.y}`
  }
  return d
}
function smoothSparklineArea(points: number[], w = 112, h = 32, pad = 2) {
  const path = smoothSparklinePath(points, w, h, pad)
  if (!path) return ''
  const pts = points.map((p, i, arr) => ({
    x: pad + (i / Math.max(1, arr.length - 1)) * (w - pad * 2),
    y: pad + (1 - (p - Math.min(...points)) / (Math.max(...points) - Math.min(...points) || 1)) * (h - pad * 2)
  }))
  return `${path} L ${pts[pts.length - 1]!.x},${h} L ${pts[0]!.x},${h} Z`
}

/* ---- Pipeline data ---- */
const QUOTED_PIPELINE_PREMIUM_M = 6.2
const quotedPipelineSummaryCards = [
  { label: 'Total book', value: '$42.8M', hint: 'In force' },
  { label: 'Quoted pipeline', value: '$6.2M', hint: 'Open quotes' },
  { label: 'YTD new sales', value: '$18.4M', hint: 'Bound new biz' }
] as const
const pipelineMixSegments = [
  { label: 'Commercial', pct: 38 }, { label: 'Personal', pct: 29 },
  { label: 'Benefits', pct: 22 }, { label: 'Other', pct: 11 }
] as const
const pipelineMixRows = computed(() =>
  pipelineMixSegments.map((row) => ({ ...row, premiumM: (QUOTED_PIPELINE_PREMIUM_M * row.pct) / 100 }))
)
function formatPremiumM(n: number) { return `$${n.toFixed(2)}M` }

/* ---- Quick leads widget (last 10 days) ---- */
const { leads: quickLeadsList, recentLeads: recentQuickLeads } = useQuickLeads()
const dashQuickLeads = computed(() => recentQuickLeads(10).slice(0, 6))

/* ---- Client favorites widget ---- */
const { favoriteIds, removeFavorite: unfavClient } = useClientFavorites()
const _mockModule = ref<typeof import('~/data/mock-customers') | null>(null)
onMounted(async () => {
  _mockModule.value = await import('~/data/mock-customers')
  // Seed some favorites if empty
  if (favoriteIds.value.length === 0 && _mockModule.value) {
    const seedIds = _mockModule.value.MOCK_CUSTOMERS.filter(c => c.policies.length > 0).slice(0, 4).map(c => c.id)
    favoriteIds.value = seedIds
  }
})

const favCustomers = computed(() => {
  const mod = _mockModule.value
  if (!mod) return []
  return favoriteIds.value
    .map(id => mod.MOCK_CUSTOMERS.find(c => c.id === id))
    .filter((c): c is InstanceType<any> => !!c)
    .slice(0, 8)
})

function favTotalPremium(c: any) {
  return (c.policies || []).reduce((s: number, p: any) => s + p.premium, 0)
}

function favTierLabel(c: any) {
  const mod = _mockModule.value
  if (!mod) return ''
  const t = mod.customerTier(c)
  if (t === 'customer') return 'Customer'
  if (t === 'lead') return 'Lead'
  if (t === 'quick_lead') return 'Quick Lead'
  return 'Cancelled'
}

function favTierClass(c: any) {
  const mod = _mockModule.value
  if (!mod) return ''
  const t = mod.customerTier(c)
  if (t === 'customer') return 'h2-fav-tier-customer'
  if (t === 'lead') return 'h2-fav-tier-lead'
  if (t === 'quick_lead') return 'h2-fav-tier-ql'
  return 'h2-fav-tier-cancelled'
}

function favFmtMoney(n: number) {
  return '$' + n.toLocaleString('en-US', { minimumFractionDigits: 0 })
}

function qlPriorityMeta(p: string) {
  if (p === 'urgent') return { label: 'Urgent', cls: 'h2-ql-pri-urgent' }
  if (p === 'high') return { label: 'High', cls: 'h2-ql-pri-high' }
  return { label: 'Normal', cls: 'h2-ql-pri-normal' }
}

function qlTimeAgo(iso: string) {
  const diff = Date.now() - new Date(iso).getTime()
  if (diff < 3600000) return `${Math.max(1, Math.round(diff / 60000))}m ago`
  if (diff < 86400000) return `${Math.round(diff / 3600000)}h ago`
  if (diff < 172800000) return 'Yesterday'
  const d = new Date(iso)
  return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
}

/* ---- Sales Leads widget ---- */
type SalesLeadSource = 'walk_in' | 'instagram' | 'facebook' | 'google' | 'referral' | 'website' | 'phone' | 'campaign'
type SalesLead = {
  id: string
  name: string
  email?: string
  phone?: string
  source: SalesLeadSource
  campaignName?: string
  lob: string
  status: 'new' | 'contacted' | 'qualified' | 'proposal' | 'won' | 'lost'
  assignedTo: string
  createdAt: string
  value?: number
  note?: string
}

const SALES_LEAD_SOURCES: { value: SalesLeadSource | 'all'; label: string; icon: string }[] = [
  { value: 'all', label: 'All sources', icon: 'i-heroicons-funnel' },
  { value: 'walk_in', label: 'Walk-in', icon: 'i-heroicons-building-storefront' },
  { value: 'instagram', label: 'Instagram', icon: 'i-heroicons-camera' },
  { value: 'facebook', label: 'Facebook', icon: 'i-heroicons-chat-bubble-left-right' },
  { value: 'google', label: 'Google', icon: 'i-heroicons-magnifying-glass' },
  { value: 'referral', label: 'Referral', icon: 'i-heroicons-user-group' },
  { value: 'website', label: 'Website', icon: 'i-heroicons-globe-alt' },
  { value: 'phone', label: 'Phone', icon: 'i-heroicons-phone' },
  { value: 'campaign', label: 'Campaigns', icon: 'i-heroicons-megaphone' }
]

const MOCK_SALES_LEADS: SalesLead[] = [
  { id: 'sl-1', name: 'Ana García', email: 'ana.g@mail.com', phone: '+506 7012-3344', source: 'instagram', lob: 'Auto', status: 'new', assignedTo: 'L. Chen', createdAt: '2026-04-07T14:30:00Z', value: 2400, note: 'DM about fleet coverage' },
  { id: 'sl-2', name: 'Marco Rodríguez', phone: '+506 8876-5500', source: 'walk_in', lob: 'Health', status: 'contacted', assignedTo: 'A. Morales', createdAt: '2026-04-07T09:15:00Z', value: 4800, note: 'Family plan, 4 members' },
  { id: 'sl-3', name: 'Construcciones TRC', email: 'info@trc.cr', source: 'google', lob: 'General risk', status: 'qualified', assignedTo: 'R. Vega', createdAt: '2026-04-06T16:00:00Z', value: 18500, campaignName: 'Google Ads — Commercial Q2' },
  { id: 'sl-4', name: 'Isabel Mora', email: 'isa.mora@outlook.com', source: 'facebook', lob: 'Life', status: 'new', assignedTo: 'L. Chen', createdAt: '2026-04-06T11:20:00Z', value: 3200, note: 'Responded to FB carousel ad' },
  { id: 'sl-5', name: 'Hotel Paraíso', phone: '+506 2234-8800', source: 'referral', lob: 'General risk', status: 'proposal', assignedTo: 'A. Morales', createdAt: '2026-04-05T10:00:00Z', value: 12000, note: 'Referred by Transportes Delta' },
  { id: 'sl-6', name: 'Laura Jiménez', email: 'laura.j@gmail.com', source: 'website', lob: 'Auto', status: 'contacted', assignedTo: 'R. Vega', createdAt: '2026-04-05T08:45:00Z', value: 1800, note: 'Online form — comparativo request' },
  { id: 'sl-7', name: 'Farmacia Salud Plus', phone: '+506 2456-7890', source: 'campaign', campaignName: 'Spring Health Push', lob: 'Health', status: 'qualified', assignedTo: 'A. Morales', createdAt: '2026-04-04T15:30:00Z', value: 9600 },
  { id: 'sl-8', name: 'Diego Araya', source: 'phone', phone: '+506 6100-9988', lob: 'Auto', status: 'new', assignedTo: 'L. Chen', createdAt: '2026-04-04T12:10:00Z', value: 1500 },
  { id: 'sl-9', name: 'Retail Express', email: 'ventas@retailexp.cr', source: 'campaign', campaignName: 'Commercial Outreach Mar', lob: 'General risk', status: 'won', assignedTo: 'R. Vega', createdAt: '2026-04-03T09:00:00Z', value: 7200 },
  { id: 'sl-10', name: 'Sofía Vargas', source: 'instagram', phone: '+506 8300-1122', lob: 'Life', status: 'contacted', assignedTo: 'L. Chen', createdAt: '2026-04-02T17:00:00Z', value: 2800, note: 'IG story reply, wants term quote' }
]

const slSourceFilter = ref<SalesLeadSource | 'all'>('all')
const slStatusFilter = ref<string>('all')

const filteredSalesLeads = computed(() => {
  let leads = MOCK_SALES_LEADS
  if (slSourceFilter.value !== 'all') {
    leads = leads.filter(l => l.source === slSourceFilter.value)
  }
  if (slStatusFilter.value !== 'all') {
    leads = leads.filter(l => l.status === slStatusFilter.value)
  }
  return leads
})

const slSourceCounts = computed(() => {
  const counts: Record<string, number> = { all: MOCK_SALES_LEADS.length }
  for (const l of MOCK_SALES_LEADS) {
    counts[l.source] = (counts[l.source] || 0) + 1
  }
  return counts
})

function slStatusBadge(s: SalesLead['status']) {
  switch (s) {
    case 'new': return { label: 'New', cls: 'h2-sl-status-new' }
    case 'contacted': return { label: 'Contacted', cls: 'h2-sl-status-contacted' }
    case 'qualified': return { label: 'Qualified', cls: 'h2-sl-status-qualified' }
    case 'proposal': return { label: 'Proposal', cls: 'h2-sl-status-proposal' }
    case 'won': return { label: 'Won', cls: 'h2-sl-status-won' }
    case 'lost': return { label: 'Lost', cls: 'h2-sl-status-lost' }
  }
}

function slSourceLabel(s: SalesLeadSource) {
  return SALES_LEAD_SOURCES.find(x => x.value === s)?.label ?? s
}

function slSourceIcon(s: SalesLeadSource) {
  return SALES_LEAD_SOURCES.find(x => x.value === s)?.icon ?? 'i-heroicons-question-mark-circle'
}

function slFmtValue(v?: number) {
  if (!v) return ''
  return '$' + v.toLocaleString('en-US', { minimumFractionDigits: 0 })
}

/* ---- Tone helpers ---- */
function changeToneClass(tone: WelcomeDashboardKpi['changeTone']) {
  switch (tone) {
    case 'positive': return 'h2-tone-pos'
    case 'negative': return 'h2-tone-neg'
    default: return 'h2-tone-neutral'
  }
}

type AlertToneMeta = { icon: string; label: string; railStyle: string; iconColor: string; bg: string }
function alertToneMeta(tone: string): AlertToneMeta {
  switch (tone) {
    case 'error': return { icon: 'i-heroicons-exclamation-circle', label: 'Critical', railStyle: 'background:#c13838', iconColor: 'text-rose-600', bg: 'bg-rose-50/60' }
    case 'warning': return { icon: 'i-heroicons-exclamation-triangle', label: 'Attention', railStyle: 'background:#c27b1a', iconColor: 'text-amber-600', bg: 'bg-amber-50/60' }
    case 'success': return { icon: 'i-heroicons-check-circle', label: 'Update', railStyle: 'background:#0f7b5f', iconColor: 'text-emerald-700', bg: 'bg-emerald-50/60' }
    default: return { icon: 'i-heroicons-information-circle', label: 'Notice', railStyle: 'background:#8c857d', iconColor: 'text-stone-500', bg: 'bg-stone-100/60' }
  }
}
const alertsWithMeta = computed(() => welcome.value.alerts.map((a) => ({ ...a, meta: alertToneMeta(a.tone) })))

/* ---- Operations command bar data ---- */
type OpsStatus = 'on-track' | 'attention' | 'warning' | 'neutral'
type OpsMetric = { id: string; label: string; value: string; target: string; status: OpsStatus; icon: string }

const ALL_OPS_INDICATORS: OpsMetric[] = [
  { id: 'prod', label: 'Production MTD', value: '$1.24M', target: '$1.18M', status: 'on-track', icon: 'i-heroicons-banknotes' },
  { id: 'ren', label: 'Renewals due', value: '23', target: 'next 30d', status: 'attention', icon: 'i-heroicons-arrow-path' },
  { id: 'coll', label: 'Collections at risk', value: '$184K', target: '3 accounts', status: 'warning', icon: 'i-heroicons-exclamation-triangle' },
  { id: 'claims', label: 'Claims pending', value: '7', target: 'avg 4.2d open', status: 'neutral', icon: 'i-heroicons-shield-exclamation' },
  { id: 'svc', label: 'Service backlog', value: '12', target: 'SLA: 94%', status: 'on-track', icon: 'i-heroicons-inbox-stack' },
  // Additional indicators available in catalog
  { id: 'gwp', label: 'GWP YTD', value: '$18.4M', target: '$17.2M plan', status: 'on-track', icon: 'i-heroicons-chart-bar' },
  { id: 'nps', label: 'NPS score', value: '72', target: '> 65 target', status: 'on-track', icon: 'i-heroicons-face-smile' },
  { id: 'quotes', label: 'Open quotes', value: '38', target: '$6.2M pipeline', status: 'attention', icon: 'i-heroicons-document-text' },
  { id: 'retention', label: 'Retention rate', value: '91%', target: 'trailing 12m', status: 'on-track', icon: 'i-heroicons-arrow-path-rounded-square' },
  { id: 'ar', label: 'AR aging 60d+', value: '$42K', target: '5 accounts', status: 'warning', icon: 'i-heroicons-clock' },
  { id: 'cancels', label: 'Cancellations MTD', value: '3', target: '$28K premium', status: 'warning', icon: 'i-heroicons-x-circle' },
  { id: 'newbiz', label: 'New business MTD', value: '14', target: '$412K bound', status: 'on-track', icon: 'i-heroicons-sparkles' },
]

const OPS_STORAGE_KEY = 'policy-ui.dashboard.ops-indicators'
const DEFAULT_IDS = ['prod', 'ren', 'coll', 'claims', 'svc']

const activeOpsIds = ref<string[]>([...DEFAULT_IDS])

onMounted(() => {
  if (typeof localStorage !== 'undefined') {
    try {
      const raw = localStorage.getItem(OPS_STORAGE_KEY)
      if (raw) {
        const parsed = JSON.parse(raw)
        if (Array.isArray(parsed) && parsed.length > 0) activeOpsIds.value = parsed
      }
    } catch { /* ignore */ }
  }
})

function persistOpsIds() {
  if (typeof localStorage !== 'undefined') {
    try { localStorage.setItem(OPS_STORAGE_KEY, JSON.stringify(activeOpsIds.value)) } catch { /* */ }
  }
}

const opsMetrics = computed(() =>
  activeOpsIds.value
    .map((id) => ALL_OPS_INDICATORS.find((m) => m.id === id))
    .filter((m): m is OpsMetric => !!m)
)

const opsConfigOpen = ref(false)
const opsRailHover = ref(false)
const opsConfigRef = ref<HTMLElement | null>(null)

function toggleOpsIndicator(id: string) {
  const idx = activeOpsIds.value.indexOf(id)
  if (idx >= 0) {
    if (activeOpsIds.value.length <= 2) return // keep at least 2
    activeOpsIds.value = activeOpsIds.value.filter((x) => x !== id)
  } else {
    if (activeOpsIds.value.length >= 7) return // max 7
    activeOpsIds.value = [...activeOpsIds.value, id]
  }
  persistOpsIds()
}

function resetOpsIndicators() {
  activeOpsIds.value = [...DEFAULT_IDS]
  persistOpsIds()
}

function onOpsConfigClickOutside(e: MouseEvent) {
  const el = opsConfigRef.value
  if (el && opsConfigOpen.value && !el.contains(e.target as Node)) {
    opsConfigOpen.value = false
  }
  // Close milestone config on outside click
  if (milestoneConfigOpen.value && !milestoneConfigSkipClose) {
    const target = e.target as HTMLElement
    if (!target.closest('.h2-mile-popover') && !target.closest('.h2-mile-config-wrap')) {
      milestoneConfigOpen.value = false
    }
  }
  // Close chart metric menu on outside click
  if (chartMetricMenuOpen.value) {
    const target = e.target as HTMLElement
    if (!target.closest('.h2-chart-metric-menu') && !target.closest('.h2-chart-metric-btn')) {
      chartMetricMenuOpen.value = false
    }
  }
}
onMounted(() => document.addEventListener('click', onOpsConfigClickOutside))
onUnmounted(() => document.removeEventListener('click', onOpsConfigClickOutside))

function opsStatusDotStyle(status: OpsStatus) {
  switch (status) {
    case 'on-track': return 'background:#0f7b5f'
    case 'attention': return 'background:#0d5c63'
    case 'warning': return 'background:#c27b1a'
    default: return 'background:#8c857d'
  }
}

function opsCellIconStyle(status: OpsStatus) {
  switch (status) {
    case 'on-track': return 'background: rgba(15,123,95,0.08); color: #0f7b5f'
    case 'attention': return 'background: rgba(1,105,111,0.08); color: #01696f'
    case 'warning': return 'background: rgba(194,123,26,0.08); color: #c27b1a'
    default: return 'background: rgba(0,0,0,0.04); color: #8a8a86'
  }
}

function opsGridCols() {
  const n = opsMetrics.value.length
  if (n <= 3) return 'grid-cols-2 sm:grid-cols-3'
  if (n <= 4) return 'grid-cols-2 sm:grid-cols-4'
  if (n <= 5) return 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5'
  if (n <= 6) return 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6'
  return 'grid-cols-2 sm:grid-cols-4 lg:grid-cols-7'
}

/* ---- Sent quotes list (dashboard widget) ---- */
type SentQuote = {
  id: string
  customer: string
  party: 'corporate' | 'personal'
  lob: string
  lobIcon: string
  premium: number
  commission: number
  carrier: string
  sentDate: string
  status: 'sent' | 'viewed' | 'accepted' | 'expired' | 'declined'
  agent: string
}

const SENT_QUOTES: SentQuote[] = [
  { id: 'QT-2025-0088', customer: 'Transportes Delta S.A.', party: 'corporate', lob: 'Auto', lobIcon: 'i-heroicons-truck', premium: 18400, commission: 2760, carrier: 'ASSA', sentDate: '2025-04-04', status: 'sent', agent: 'Ana R.' },
  { id: 'QT-2025-0087', customer: 'María Elena Pérez', party: 'personal', lob: 'Health', lobIcon: 'i-heroicons-heart', premium: 3200, commission: 480, carrier: 'INS', sentDate: '2025-04-03', status: 'viewed', agent: 'Ana R.' },
  { id: 'QT-2025-0086', customer: 'Holdings Centro', party: 'corporate', lob: 'Life', lobIcon: 'i-heroicons-shield-check', premium: 42000, commission: 5040, carrier: 'Pan-American Life', sentDate: '2025-04-02', status: 'accepted', agent: 'Marco V.' },
  { id: 'QT-2025-0085', customer: 'Roberto Jiménez Mora', party: 'personal', lob: 'Auto', lobIcon: 'i-heroicons-truck', premium: 1520, commission: 228, carrier: 'Qualitas', sentDate: '2025-04-01', status: 'sent', agent: 'Ana R.' },
  { id: 'QT-2025-0084', customer: 'Clínica Norte', party: 'corporate', lob: 'Health', lobIcon: 'i-heroicons-heart', premium: 86000, commission: 10320, carrier: 'Blue Cross', sentDate: '2025-03-31', status: 'accepted', agent: 'Marco V.' },
  { id: 'QT-2025-0083', customer: 'Sofía Campos Rojas', party: 'personal', lob: 'Auto', lobIcon: 'i-heroicons-truck', premium: 1380, commission: 207, carrier: 'INS', sentDate: '2025-03-30', status: 'expired', agent: 'Marco V.' },
  { id: 'QT-2025-0082', customer: 'Retail Plaza', party: 'corporate', lob: 'General Risk', lobIcon: 'i-heroicons-building-office-2', premium: 24600, commission: 3690, carrier: 'ASSA', sentDate: '2025-03-28', status: 'declined', agent: 'Ana R.' },
  { id: 'QT-2025-0081', customer: 'Carolina Fallas Vargas', party: 'personal', lob: 'Renter', lobIcon: 'i-heroicons-home-modern', premium: 320, commission: 48, carrier: 'ASSA', sentDate: '2025-03-27', status: 'accepted', agent: 'Marco V.' },
  { id: 'QT-2025-0080', customer: 'Luis Andrés Solís', party: 'personal', lob: 'Life', lobIcon: 'i-heroicons-shield-check', premium: 4800, commission: 576, carrier: 'Pan-American Life', sentDate: '2025-03-25', status: 'viewed', agent: 'Ana R.' },
  { id: 'QT-2025-0079', customer: 'Startup Labs', party: 'corporate', lob: 'Health', lobIcon: 'i-heroicons-heart', premium: 12800, commission: 1536, carrier: 'INS', sentDate: '2025-03-22', status: 'sent', agent: 'Marco V.' },
]

type QuoteSortKey = 'date-new' | 'date-old' | 'premium-high' | 'premium-low' | 'commission-high' | 'commission-low'
const quoteSortKey = ref<QuoteSortKey>('date-new')
const quoteFilterParty = ref<'all' | 'corporate' | 'personal'>('all')
const quoteFilterStatus = ref<string>('all')
const quoteFilterLob = ref<string>('all')

const quoteSortOptions = [
  { label: 'Newest first', value: 'date-new' as const },
  { label: 'Oldest first', value: 'date-old' as const },
  { label: 'Premium ↓', value: 'premium-high' as const },
  { label: 'Premium ↑', value: 'premium-low' as const },
  { label: 'Commission ↓', value: 'commission-high' as const },
  { label: 'Commission ↑', value: 'commission-low' as const },
]
const quotePartyOptions = [
  { label: 'All', value: 'all' },
  { label: 'Corporate', value: 'corporate' },
  { label: 'Personal', value: 'personal' },
]
const quoteStatusOptions = [
  { label: 'All statuses', value: 'all' },
  { label: 'Sent', value: 'sent' },
  { label: 'Viewed', value: 'viewed' },
  { label: 'Accepted', value: 'accepted' },
  { label: 'Expired', value: 'expired' },
  { label: 'Declined', value: 'declined' },
]
const quoteLobOptions = computed(() => {
  const lobs = [...new Set(SENT_QUOTES.map(q => q.lob))]
  return [{ label: 'All lines', value: 'all' }, ...lobs.map(l => ({ label: l, value: l }))]
})

const filteredSentQuotes = computed(() => {
  let list = [...SENT_QUOTES]

  if (quoteFilterParty.value !== 'all') list = list.filter(q => q.party === quoteFilterParty.value)
  if (quoteFilterStatus.value !== 'all') list = list.filter(q => q.status === quoteFilterStatus.value)
  if (quoteFilterLob.value !== 'all') list = list.filter(q => q.lob === quoteFilterLob.value)

  switch (quoteSortKey.value) {
    case 'date-new': list.sort((a, b) => b.sentDate.localeCompare(a.sentDate)); break
    case 'date-old': list.sort((a, b) => a.sentDate.localeCompare(b.sentDate)); break
    case 'premium-high': list.sort((a, b) => b.premium - a.premium); break
    case 'premium-low': list.sort((a, b) => a.premium - b.premium); break
    case 'commission-high': list.sort((a, b) => b.commission - a.commission); break
    case 'commission-low': list.sort((a, b) => a.commission - b.commission); break
  }

  return list
})

const quoteStatusMeta: Record<string, { label: string; cls: string }> = {
  sent:     { label: 'Sent',     cls: 'h2-qs-sent' },
  viewed:   { label: 'Viewed',   cls: 'h2-qs-viewed' },
  accepted: { label: 'Accepted', cls: 'h2-qs-accepted' },
  expired:  { label: 'Expired',  cls: 'h2-qs-expired' },
  declined: { label: 'Declined', cls: 'h2-qs-declined' },
}

/* ---- Time ---- */
const timeGreeting = computed(() => {
  const h = new Date().getHours()
  if (h < 12) return 'Good morning'
  if (h < 17) return 'Good afternoon'
  return 'Good evening'
})
const currentDate = computed(() =>
  new Intl.DateTimeFormat('en-US', { weekday: 'long', month: 'long', day: 'numeric' }).format(new Date())
)

/* ---- Calendar widget ---- */
type CalEventType = 'meeting' | 'renewal' | 'claim' | 'payment' | 'reminder' | 'threshold'

interface CalEvent {
  id: string
  title: string
  time: string
  type: CalEventType
  source?: 'gmail' | 'system' | 'manual'
  customer?: string
  detail?: string
  urgent?: boolean
}

const calEventTypeMeta: Record<CalEventType, { label: string; color: string; icon: string }> = {
  meeting:   { label: 'Meetings', color: '#01696f', icon: 'i-heroicons-calendar-days' },
  renewal:   { label: 'Renewals', color: '#7c3aed', icon: 'i-heroicons-arrow-path' },
  claim:     { label: 'Claims', color: '#c13838', icon: 'i-heroicons-shield-exclamation' },
  payment:   { label: 'Payments', color: '#c27b1a', icon: 'i-heroicons-banknotes' },
  reminder:  { label: 'Reminders', color: '#0f7b5f', icon: 'i-heroicons-bell' },
  threshold: { label: 'Alerts', color: '#be185d', icon: 'i-heroicons-exclamation-triangle' },
}

const todayEvents: CalEvent[] = [
  { id: 'c1', title: 'Renewal review — Constructora Delta', time: '9:00 AM', type: 'renewal', source: 'system', customer: 'Constructora Delta S.A.', detail: 'Policy expires Jul 15. Premium $55K, 3 yr client.', urgent: true },
  { id: 'c2', title: 'Call with María Pérez', time: '10:30 AM', type: 'meeting', source: 'gmail', customer: 'María Pérez', detail: 'Auto quote follow-up. Synced from Gmail.' },
  { id: 'c3', title: 'Unpaid premium — Farmacia Salud', time: '11:00 AM', type: 'payment', source: 'system', customer: 'Farmacia Salud', detail: '45 days overdue. $12,800 group life.', urgent: true },
  { id: 'c4', title: 'Claim status update — Hotel Pacífico', time: '1:00 PM', type: 'claim', source: 'system', customer: 'Hotel Pacífico', detail: 'Fire claim #CL-2891. Adjuster report due.' },
  { id: 'c5', title: 'Client crossed $100K premium', time: '2:00 PM', type: 'threshold', source: 'system', customer: 'Banco Regional', detail: 'Total book now $186K. VIP follow-up recommended.' },
  { id: 'c6', title: 'Prepare renewal proposal', time: '3:30 PM', type: 'reminder', source: 'manual', detail: 'Transportes del Sur fleet policy. Get 3 comparative quotes.' },
  { id: 'c7', title: 'Team sync — weekly pipeline', time: '4:00 PM', type: 'meeting', source: 'gmail', detail: 'Google Meet link attached. All agents.' },
  { id: 'c8', title: '5 renewals due next 7 days', time: '—', type: 'renewal', source: 'system', detail: 'Batch alert: review upcoming expirations.', urgent: true },
]

const filteredCalEvents = computed(() => todayEvents)

function calEventDotStyle(type: CalEventType) {
  return `background: ${calEventTypeMeta[type].color}`
}

/* ---- Segment colors (petroleum palette) ---- */
/* Segment colors as inline styles (Tailwind v4 doesn't resolve teal) */
const segColorStyles = ['background:#0d5c63', 'background:#1a8a8a', 'background:#2dd4bf', 'background:#a8a29e']
const segDotStyles = ['background:#0d5c63;outline:2px solid rgba(13,92,99,0.2);outline-offset:1px', 'background:#1a8a8a;outline:2px solid rgba(26,138,138,0.2);outline-offset:1px', 'background:#2dd4bf;outline:2px solid rgba(45,212,191,0.2);outline-offset:1px', 'background:#a8a29e;outline:2px solid rgba(168,162,158,0.2);outline-offset:1px']

/* ---- Drafts detection ---- */
interface DraftEntry {
  key: string
  label: string
  icon: string
  route: string
  age: string
}

const DRAFT_SOURCES = [
  { key: 'policy-registration-draft-v1', label: 'Policy registration', icon: 'i-heroicons-document-text', route: '/registration/policy' },
  { key: 'policy-ui-quote-session-v1', label: 'Quote comparison', icon: 'i-heroicons-document-magnifying-glass', route: '/quotes/compare' },
  { key: 'policy-ui-customer-profile-vault-v1', label: 'Customer profile', icon: 'i-heroicons-user-circle', route: '/onboarding/solicitud' },
]

const activeDrafts = ref<DraftEntry[]>([])

function draftAge(raw: any): string {
  const ts = raw?.updatedAt || raw?.touchedAt || raw?.createdAt
  if (!ts || typeof ts !== 'number') return 'Unknown'
  const mins = Math.floor((Date.now() - ts) / 60000)
  if (mins < 1) return 'Just now'
  if (mins < 60) return `${mins}m ago`
  const hrs = Math.floor(mins / 60)
  if (hrs < 24) return `${hrs}h ago`
  const days = Math.floor(hrs / 24)
  return `${days}d ago`
}

onMounted(() => {
  if (typeof localStorage === 'undefined') return
  const found: DraftEntry[] = []
  for (const src of DRAFT_SOURCES) {
    try {
      const raw = localStorage.getItem(src.key)
      if (raw) {
        const parsed = JSON.parse(raw)
        if (parsed && typeof parsed === 'object' && Object.keys(parsed).length > 0) {
          found.push({ ...src, age: draftAge(parsed) })
        }
      }
    } catch { /* ignore corrupt entries */ }
  }
  activeDrafts.value = found
})

function removeDraft(key: string) {
  if (typeof localStorage !== 'undefined') {
    localStorage.removeItem(key)
  }
  activeDrafts.value = activeDrafts.value.filter(d => d.key !== key)
}
</script>

<template>
  <div class="h2 relative min-h-full pb-12">

    <HomeDashboardWidgetBlocks
      :widget-order="widgetOrder"
      :widgets="widgets"
      :layout-unlocked="layoutUnlocked"
      :dragging-widget="draggingWidget"
      @drag-start="onDragStart"
      @drag-end="onDragEnd"
      @drop="onDropSection"
    >
      <!-- ==================== HERO: Operations Command Bar ==================== -->
      <template #hero>
        <div class="space-y-4">
          <!-- Slim greeting strip -->
          <div class="flex flex-wrap items-center justify-between gap-3 px-1">
            <div class="flex items-baseline gap-3">
              <h1 class="text-xl font-semibold tracking-tight text-[var(--h2-fg)]">
                {{ timeGreeting }}, {{ welcome.greetingName }}
              </h1>
              <span class="text-xs text-[var(--h2-muted)]">{{ currentDate }}</span>
            </div>
            <!-- Mobile-only: action buttons (on desktop these live in the topbar) -->
            <div class="flex gap-2 sm:hidden">
              <NuxtLink to="/onboarding">
                <UButton size="sm" color="neutral" variant="outline" class="h2-btn-outline" icon="i-heroicons-arrow-trending-up">
                  Pipeline
                </UButton>
              </NuxtLink>
              <NuxtLink to="/sales/quick-lead">
                <UButton size="sm" color="neutral" variant="soft" class="h2-btn-outline" icon="i-heroicons-bolt">
                  Quick Lead
                </UButton>
              </NuxtLink>
              <NuxtLink to="/quotes">
                <UButton size="sm" color="primary" class="h2-btn-primary" icon="i-heroicons-document-text">
                  New quote
                </UButton>
              </NuxtLink>
            </div>
          </div>

          <!-- Operations rail: configurable indicator strip -->
          <div
            class="h2-ops-rail-wrap relative"
            @mouseenter="opsRailHover = true"
            @mouseleave="opsRailHover = false"
          >
            <div class="h2-card h2-card-flush grid" :class="opsGridCols()">
              <div
                v-for="(op, i) in opsMetrics"
                :key="op.id"
                class="h2-ops-cell"
                :class="[
                  i < opsMetrics.length - 1 ? 'h2-cell-border' : '',
                  i === 0 ? 'h2-ops-cell--lead' : ''
                ]"
              >
                <div class="h2-ops-cell-icon" :style="opsCellIconStyle(op.status)">
                  <UIcon :name="op.icon" style="width: 15px; height: 15px;" />
                </div>
                <div class="h2-ops-cell-body">
                  <p class="h2-ops-cell-label">{{ op.label }}</p>
                  <p class="h2-ops-cell-value">{{ op.value }}</p>
                  <div class="h2-ops-cell-target">
                    <span class="h2-ops-dot" :style="opsStatusDotStyle(op.status)" />
                    <span>{{ op.target }}</span>
                  </div>
                </div>
              </div>
            </div>

            <!-- Widget config button — appears on hover, bottom-right corner -->
            <Transition
              enter-active-class="transition duration-150 ease-out"
              enter-from-class="opacity-0 scale-90"
              enter-to-class="opacity-100 scale-100"
              leave-active-class="transition duration-100 ease-in"
              leave-from-class="opacity-100"
              leave-to-class="opacity-0 scale-90"
            >
              <div
                v-show="opsRailHover || opsConfigOpen"
                ref="opsConfigRef"
                class="absolute -bottom-2 -right-2 z-20"
              >
                <button
                  type="button"
                  class="h2-ops-config-btn"
                  title="Configure indicators"
                  @click.stop="opsConfigOpen = !opsConfigOpen"
                >
                  <UIcon name="i-heroicons-squares-plus" style="width: 14px; height: 14px;" />
                </button>

                <!-- Config popover -->
                <Transition
                  enter-active-class="transition duration-150 ease-out"
                  enter-from-class="opacity-0 scale-95 translate-y-1"
                  enter-to-class="opacity-100 scale-100 translate-y-0"
                  leave-active-class="transition duration-100 ease-in"
                  leave-from-class="opacity-100"
                  leave-to-class="opacity-0 scale-95"
                >
                  <div v-show="opsConfigOpen" class="h2-ops-config-popover">
                    <div class="h2-ops-config-head">
                      <span>Indicators</span>
                      <button type="button" class="h2-ops-config-reset" @click="resetOpsIndicators">Reset</button>
                    </div>
                    <div class="h2-ops-config-hint">
                      Select 2–7 KPIs for your command strip.
                    </div>
                    <ul class="h2-ops-config-list">
                      <li
                        v-for="ind in ALL_OPS_INDICATORS"
                        :key="ind.id"
                        class="h2-ops-config-item"
                        :class="activeOpsIds.includes(ind.id) ? 'h2-ops-config-item-on' : ''"
                        @click="toggleOpsIndicator(ind.id)"
                      >
                        <div class="h2-ops-config-check">
                          <UIcon
                            v-if="activeOpsIds.includes(ind.id)"
                            name="i-heroicons-check"
                            style="width: 12px; height: 12px;"
                          />
                        </div>
                        <UIcon :name="ind.icon" style="width: 14px; height: 14px; opacity: 0.5;" />
                        <div class="min-w-0 flex-1">
                          <p class="text-[12px] font-medium text-[var(--text-primary)] truncate">{{ ind.label }}</p>
                          <p class="text-[10px] text-[var(--text-muted)]">{{ ind.value }} · {{ ind.target }}</p>
                        </div>
                        <span
                          class="h2-ops-config-dot"
                          :style="opsStatusDotStyle(ind.status)"
                        />
                      </li>
                    </ul>
                    <div class="h2-ops-config-footer">
                      {{ activeOpsIds.length }} of {{ ALL_OPS_INDICATORS.length }} selected
                    </div>
                  </div>
                </Transition>
              </div>
            </Transition>
          </div>
        </div>
      </template>

      <!-- ==================== MILESTONE ==================== -->
      <template #milestone>
        <div class="h2-milestone-card relative group/mile">
          <div class="h2-milestone-top">
            <div class="flex items-center gap-2.5">
              <div class="h2-milestone-badge" :style="`--ms-color: ${milestoneStatus.color}`">
                <UIcon :name="milestoneStatus.icon" style="width: 15px; height: 15px;" />
              </div>
              <div>
                <span class="h2-milestone-status" :style="`color: ${milestoneStatus.color}`">{{ milestoneStatus.label }}</span>
                <span class="h2-milestone-tag">MTD plan</span>
              </div>
            </div>

            <!-- Overall progress bar -->
            <div class="h2-milestone-bar-wrap">
              <div class="h2-milestone-bar-track">
                <div class="h2-milestone-bar-fill" :style="`width: ${Math.min(milestoneAvgPct, 120) / 1.2}%; background: ${milestoneStatus.color}`" />
              </div>
              <span class="h2-milestone-bar-pct" :style="`color: ${milestoneStatus.color}`">{{ milestoneAvgPct }}%</span>
            </div>
          </div>

          <!-- Metric pills + config -->
          <div class="h2-milestone-bottom">
            <div class="h2-milestone-metrics">
              <div v-for="metric in activeMilestones" :key="metric.id" class="h2-milestone-pill">
                <span class="h2-milestone-pill-label">{{ metric.label }}</span>
                <span class="h2-milestone-pill-val">{{ metric.actual }}</span>
                <span class="h2-milestone-pill-sep">/</span>
                <span class="h2-milestone-pill-target">{{ metric.target }}</span>
                <span
                  class="h2-milestone-pill-pct"
                  :class="metric.pct >= 100 ? 'h2-milestone-pill-pct--good' : metric.pct >= 85 ? 'h2-milestone-pill-pct--close' : 'h2-milestone-pill-pct--behind'"
                >{{ metric.pct }}%</span>
              </div>
            </div>

            <!-- Config button -->
            <div class="h2-mile-config-wrap relative">
            <button
              type="button"
              class="h2-mile-config-btn"
              title="Configure metrics"
              @mousedown.prevent="onMilestoneConfigClick"
            >
              <UIcon name="i-heroicons-cog-6-tooth" style="width: 12px; height: 12px;" />
            </button>
            <div v-show="milestoneConfigOpen" class="h2-mile-popover">
              <p class="text-[11px] font-semibold uppercase tracking-wider text-[#8a8a86] px-3 pt-3 pb-1">Track (1–4 metrics)</p>
              <ul class="pb-2">
                <li
                  v-for="m in MILESTONE_METRICS"
                  :key="m.id"
                  class="h2-mile-item"
                  :class="activeMilestoneIds.includes(m.id) ? 'h2-mile-item-on' : ''"
                  @click="toggleMilestoneMetric(m.id)"
                >
                  <div class="h2-mile-check" :class="activeMilestoneIds.includes(m.id) ? 'h2-mile-check-on' : ''">
                    <UIcon v-if="activeMilestoneIds.includes(m.id)" name="i-heroicons-check" style="width: 10px; height: 10px;" />
                  </div>
                  <span class="text-[12px] font-medium text-[var(--text-primary)]">{{ m.label }}</span>
                  <span class="ml-auto text-[11px] tabular-nums text-[#8a8a86]">{{ m.actual }} / {{ m.target }}</span>
                </li>
              </ul>
            </div>
          </div>
          </div>
        </div>
      </template>

      <!-- ==================== PERFORMANCE KPIs ==================== -->
      <template #performance>
        <section class="space-y-4" aria-labelledby="perf-h2">
          <div class="h2-section-header">
            <h2 id="perf-h2" class="h2-section-title">Today at a glance</h2>
            <p class="h2-section-sub">Headline operational metrics</p>
          </div>

          <div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
            <div
              v-for="k in welcome.performanceKpis"
              :key="k.id"
              class="h2-card group overflow-hidden p-5 transition-all duration-150 hover:shadow-md"
            >
              <p class="text-[11px] font-semibold uppercase tracking-[0.06em] text-[#8a8a86]">{{ k.label }}</p>
              <div class="mt-2 flex items-end gap-2.5">
                <p class="text-[22px] font-semibold tabular-nums tracking-tight text-[#1a1a18]" style="font-variant-numeric: tabular-nums;">
                  {{ k.value }}
                </p>
                <p v-if="k.change" class="mb-0.5 text-xs font-semibold" :class="changeToneClass(k.changeTone)">
                  {{ k.change }}
                </p>
              </div>
              <p v-if="k.hint" class="mt-1 text-[11px] leading-snug text-[var(--h2-muted)]">{{ k.hint }}</p>

              <div class="mt-3 h-7 w-full">
                <svg class="h-full w-full" viewBox="0 0 112 32" fill="none" aria-hidden="true">
                  <defs>
                    <linearGradient :id="`sg2-${k.id}`" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="0%" stop-color="var(--h2-accent)" stop-opacity="0.16" />
                      <stop offset="100%" stop-color="var(--h2-accent)" stop-opacity="0" />
                    </linearGradient>
                  </defs>
                  <path v-if="kpiSparkSeries[k.id]" :d="smoothSparklineArea(kpiSparkSeries[k.id]!)" :fill="`url(#sg2-${k.id})`" />
                  <path
                    v-if="kpiSparkSeries[k.id]" :d="smoothSparklinePath(kpiSparkSeries[k.id]!)"
                    fill="none" stroke="var(--h2-accent)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"
                    class="opacity-60 transition-opacity group-hover:opacity-100"
                  />
                </svg>
              </div>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== TASKS & ALERTS ==================== -->
      <template #tasks_alerts>
        <div class="grid gap-4 lg:grid-cols-2 lg:items-stretch">
          <!-- Tasks -->
          <div class="h2-card overflow-hidden flex flex-col">
            <div class="h2-card-header">
              <div class="h2-icon-box"><UIcon name="i-heroicons-clipboard-document-check" class="h-4 w-4" /></div>
              <div class="flex-1 min-w-0">
                <p class="text-[14px] font-semibold text-[#1a1a18]">Today's tasks</p>
                <p class="text-[13px] text-[#8a8a86]">{{ completedTaskCount }}/{{ totalTaskCount }} done</p>
              </div>
              <button type="button" class="h2-task-add-btn" title="Add task" @click="addingTask = !addingTask">
                <UIcon name="i-heroicons-plus" style="width: 14px; height: 14px;" />
              </button>
            </div>

            <!-- Progress bar -->
            <div class="h2-task-progress">
              <div
                class="h2-task-progress-fill"
                :style="`width: ${totalTaskCount > 0 ? (completedTaskCount / totalTaskCount * 100) : 0}%`"
              />
            </div>

            <!-- Quick add -->
            <Transition
              enter-active-class="transition-all duration-200 ease-out"
              enter-from-class="opacity-0 max-h-0"
              enter-to-class="opacity-100 max-h-14"
              leave-active-class="transition-all duration-150 ease-in"
              leave-from-class="opacity-100 max-h-14"
              leave-to-class="opacity-0 max-h-0"
            >
              <div v-if="addingTask" class="h2-task-add-row">
                <input
                  v-model="newTaskText"
                  type="text"
                  class="h2-task-add-input"
                  placeholder="What needs doing?"
                  @keydown.enter="addQuickTask"
                  @keydown.escape="addingTask = false"
                />
                <button type="button" class="h2-task-add-submit" :disabled="!newTaskText.trim()" @click="addQuickTask">Add</button>
              </div>
            </Transition>

            <!-- Task list -->
            <ul class="h2-list flex-1">
              <TransitionGroup
                enter-active-class="transition-all duration-200 ease-out"
                enter-from-class="opacity-0 -translate-x-2"
                enter-to-class="opacity-100 translate-x-0"
                leave-active-class="transition-all duration-200 ease-in"
                leave-from-class="opacity-100 max-h-12"
                leave-to-class="opacity-0 max-h-0"
              >
                <li
                  v-for="task in dashTasks"
                  :key="task.id"
                  class="h2-task-item group/task"
                >
                  <button
                    type="button"
                    class="h2-task-checkbox"
                    :class="task.done ? 'h2-task-checkbox-done' : ''"
                    @click="toggleTask(task.id)"
                  >
                    <UIcon v-if="task.done" name="i-heroicons-check" style="width: 10px; height: 10px;" />
                  </button>
                  <span
                    class="h2-task-text flex-1"
                    :class="{
                      'h2-task-done': task.done,
                      'font-medium text-[var(--h2-fg)]': task.emphasis && !task.done,
                      'text-[var(--h2-fg-secondary)]': !task.emphasis && !task.done,
                    }"
                  >{{ task.title }}</span>
                  <button
                    v-if="task.id.startsWith('user-task-')"
                    type="button"
                    class="h2-task-remove"
                    title="Remove"
                    @click="removeTask(task.id)"
                  >
                    <UIcon name="i-heroicons-x-mark" style="width: 12px; height: 12px;" />
                  </button>
                </li>
              </TransitionGroup>
            </ul>
          </div>

          <!-- Alerts -->
          <div class="h2-card overflow-hidden flex flex-col">
            <div class="h2-card-header">
              <div class="h2-icon-box h2-icon-box-error"><UIcon name="i-heroicons-bell-alert" class="h-4 w-4" /></div>
              <div class="flex-1 min-w-0">
                <p class="text-[14px] font-semibold text-[#1a1a18]">Alerts</p>
                <p class="text-[13px] text-[#8a8a86]">{{ visibleAlerts.length }} exceptions</p>
              </div>
              <button
                v-if="dashAlertsDismissed.size > 0"
                type="button"
                class="h2-alert-restore"
                @click="dashAlertsDismissed.clear()"
              >
                Restore all
              </button>
            </div>
            <div v-if="visibleAlerts.length === 0" class="px-6 py-8 text-center flex-1 flex flex-col items-center justify-center">
              <UIcon name="i-heroicons-check-circle" style="width: 28px; height: 28px; color: #0f7b5f; margin: 0 auto 6px;" />
              <p class="text-[13px] text-[var(--h2-muted)]">All clear — no pending alerts</p>
            </div>
            <div v-else class="space-y-px px-3 pb-3 flex-1">
              <TransitionGroup
                enter-active-class="transition-all duration-200 ease-out"
                enter-from-class="opacity-0 translate-x-2"
                enter-to-class="opacity-100 translate-x-0"
                leave-active-class="transition-all duration-200 ease-in"
                leave-from-class="opacity-100 max-h-24"
                leave-to-class="opacity-0 max-h-0"
              >
                <div
                  v-for="alert in visibleAlerts"
                  :key="alert.id"
                  class="h2-alert-row group/alert"
                  :class="alert.meta.bg"
                >
                  <!-- Status rail -->
                  <div class="absolute inset-y-1.5 left-0 rounded-full" :style="alert.meta.railStyle + ';width:3px'" />
                  <UIcon :name="alert.meta.icon" class="mt-0.5 h-4 w-4 shrink-0 pl-2" :class="alert.meta.iconColor" />
                  <div class="min-w-0 flex-1">
                    <p class="text-[10px] font-bold uppercase tracking-wider" :class="alert.meta.iconColor">{{ alert.meta.label }}</p>
                    <p class="mt-0.5 text-[13px] leading-snug text-[var(--h2-fg)]">{{ alert.message }}</p>
                  </div>
                  <button
                    type="button"
                    class="h2-alert-dismiss"
                    title="Dismiss"
                    @click="dismissAlert(alert.id)"
                  >
                    <UIcon name="i-heroicons-x-mark" style="width: 12px; height: 12px;" />
                  </button>
                </div>
              </TransitionGroup>
            </div>
          </div>
        </div>
      </template>

      <!-- ==================== CHARTS ==================== -->
      <template #charts>
        <section class="grid gap-4 lg:grid-cols-5" aria-labelledby="ch-h2">
          <!-- Main chart -->
          <div class="lg:col-span-3 h2-card overflow-hidden">
            <!-- Chart toolbar -->
            <div class="h2-chart-toolbar">
              <div class="flex items-center gap-2 min-w-0">
                <!-- Metric selector -->
                <div class="relative">
                  <button type="button" class="h2-chart-metric-btn" @click.stop="chartMetricMenuOpen = !chartMetricMenuOpen">
                    <span class="font-semibold text-[13px] text-[var(--h2-fg)]">{{ selectedChartMetric.label }}</span>
                    <UIcon name="i-heroicons-chevron-down" style="width: 12px; height: 12px; color: #8a8a86;" />
                  </button>
                  <Transition
                    enter-active-class="transition duration-150 ease-out"
                    enter-from-class="opacity-0 scale-95 -translate-y-1"
                    enter-to-class="opacity-100 scale-100 translate-y-0"
                    leave-active-class="transition duration-100 ease-in"
                    leave-from-class="opacity-100"
                    leave-to-class="opacity-0 scale-95"
                  >
                    <div v-if="chartMetricMenuOpen" class="h2-chart-metric-menu">
                      <button
                        v-for="cm in CHART_METRICS"
                        :key="cm.id"
                        type="button"
                        class="h2-chart-metric-item"
                        :class="activeChartMetric === cm.id ? 'h2-chart-metric-item-on' : ''"
                        @click="activeChartMetric = cm.id; chartMetricMenuOpen = false"
                      >
                        <span class="text-[12px]">{{ cm.label }}</span>
                        <span class="text-[11px] tabular-nums text-[#8a8a86]">{{ cm.data6m[cm.data6m.length - 1]?.display }}</span>
                      </button>
                    </div>
                  </Transition>
                </div>
                <!-- Change badge -->
                <span :class="selectedChartMetric.changeTone === 'positive' ? 'h2-badge-success' : 'h2-badge-warning'">{{ selectedChartMetric.change }}</span>
                <span class="font-mono text-[14px] font-bold tabular-nums text-[var(--h2-fg)] hidden sm:inline">{{ chartLatest.display }}</span>
              </div>
              <div class="flex items-center gap-2">
                <!-- Chart type toggle -->
                <div class="h2-chart-range">
                  <button type="button" class="h2-chart-type-btn" :class="activeChartType === 'area' ? 'h2-chart-range-on' : ''" title="Area" @click="activeChartType = 'area'">
                    <svg width="14" height="10" viewBox="0 0 14 10" fill="none"><path d="M1 8L4 4L7 6L13 1V9H1V8Z" fill="currentColor" opacity="0.3"/><path d="M1 8L4 4L7 6L13 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
                  </button>
                  <button type="button" class="h2-chart-type-btn" :class="activeChartType === 'line' ? 'h2-chart-range-on' : ''" title="Line" @click="activeChartType = 'line'">
                    <svg width="14" height="10" viewBox="0 0 14 10" fill="none"><path d="M1 8L4 4L7 6L13 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
                  </button>
                  <button type="button" class="h2-chart-type-btn" :class="activeChartType === 'bar' ? 'h2-chart-range-on' : ''" title="Bar" @click="activeChartType = 'bar'">
                    <svg width="14" height="10" viewBox="0 0 14 10" fill="none"><rect x="1" y="5" width="2.5" height="5" rx="0.5" fill="currentColor"/><rect x="5" y="2" width="2.5" height="8" rx="0.5" fill="currentColor"/><rect x="9" y="0" width="2.5" height="10" rx="0.5" fill="currentColor"/></svg>
                  </button>
                </div>
                <div class="h2-chart-sep" />
                <!-- Time range -->
                <div class="h2-chart-range">
                  <button type="button" class="h2-chart-range-btn" :class="chartTimeRange === '3m' ? 'h2-chart-range-on' : ''" @click="chartTimeRange = '3m'">3M</button>
                  <button type="button" class="h2-chart-range-btn" :class="chartTimeRange === '6m' ? 'h2-chart-range-on' : ''" @click="chartTimeRange = '6m'">6M</button>
                  <button type="button" class="h2-chart-range-btn" :class="chartTimeRange === '12m' ? 'h2-chart-range-on' : ''" @click="chartTimeRange = '12m'">12M</button>
                </div>
              </div>
            </div>

            <!-- Chart canvas -->
            <div class="px-2 pb-2 pt-1">
              <div class="overflow-hidden rounded-lg bg-[var(--h2-surface-inset)] ring-1 ring-[var(--h2-border-strong)]">
                <svg class="h-auto w-full" :viewBox="`0 0 ${chartSvgModel.viewW} ${chartSvgModel.viewH}`" role="img">
                  <title>{{ selectedChartMetric.label }} trend</title>
                  <defs>
                    <linearGradient id="chartGrad" x1="0" y1="0" x2="0" y2="1">
                      <stop offset="0%" stop-color="var(--h2-accent)" stop-opacity="0.2" />
                      <stop offset="60%" stop-color="var(--h2-accent)" stop-opacity="0.04" />
                      <stop offset="100%" stop-color="var(--h2-accent)" stop-opacity="0" />
                    </linearGradient>
                  </defs>
                  <!-- Grid -->
                  <line v-for="(gy, i) in chartSvgModel.gridYs" :key="'g'+i" class="stroke-[var(--h2-border)]" stroke-width="1"
                    :x1="chartSvgModel.padX" :y1="gy" :x2="chartSvgModel.padX + chartSvgModel.innerW" :y2="gy" />

                  <!-- Area chart -->
                  <template v-if="activeChartType === 'area'">
                    <path :d="chartSvgModel.areaD" fill="url(#chartGrad)" class="transition-all duration-500" />
                    <path :d="chartSvgModel.lineD" fill="none" stroke="var(--h2-accent)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" class="transition-all duration-500" />
                    <g v-for="(pt, i) in chartSvgModel.points" :key="'p'+i">
                      <circle :cx="pt.x" :cy="pt.y" r="4.5" fill="var(--h2-surface)" stroke="var(--h2-accent)" stroke-width="2" class="transition-all duration-500" />
                    </g>
                  </template>

                  <!-- Line chart -->
                  <template v-if="activeChartType === 'line'">
                    <path :d="chartSvgModel.lineD" fill="none" stroke="var(--h2-accent)" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" class="transition-all duration-500" />
                    <g v-for="(pt, i) in chartSvgModel.points" :key="'lp'+i">
                      <circle :cx="pt.x" :cy="pt.y" r="3.5" fill="var(--h2-accent)" class="transition-all duration-500" />
                    </g>
                  </template>

                  <!-- Bar chart -->
                  <template v-if="activeChartType === 'bar'">
                    <rect
                      v-for="(bar, i) in chartSvgModel.bars"
                      :key="'b'+i"
                      :x="bar.x" :y="bar.y" :width="bar.w" :height="bar.h"
                      rx="3"
                      fill="var(--h2-accent)"
                      :opacity="0.5 + (i / chartSvgModel.bars.length) * 0.5"
                      class="transition-all duration-500"
                    />
                  </template>
                </svg>
                <!-- X-axis labels -->
                <div class="flex justify-between border-t border-[var(--h2-border)] px-3 pb-2 pt-1.5">
                  <div v-for="row in chartData" :key="row.m" class="flex-1 text-center">
                    <p class="font-mono text-[10px] font-semibold tabular-nums text-[var(--h2-fg)]">{{ row.display }}</p>
                    <p class="text-[10px] text-[var(--h2-muted)]">{{ row.m }}</p>
                  </div>
                </div>
              </div>
            </div>
          </div>

          <!-- Pipeline -->
          <div class="lg:col-span-2 h2-card flex flex-col overflow-hidden">
            <div class="flex items-center justify-between px-5 pt-5">
              <div>
                <h2 class="text-[14px] font-semibold text-[#1a1a18]">Pipeline</h2>
                <p class="mt-0.5 text-[11px] text-[var(--h2-muted)]">Book, open quotes & YTD</p>
              </div>
              <!-- Pipeline view toggle -->
              <div class="h2-chart-range">
                <button type="button" class="h2-chart-range-btn" :class="pipelineView === 'summary' ? 'h2-chart-range-on' : ''" @click="pipelineView = 'summary'">Mix</button>
                <button type="button" class="h2-chart-range-btn" :class="pipelineView === 'stage' ? 'h2-chart-range-on' : ''" @click="pipelineView = 'stage'">Stage</button>
                <button type="button" class="h2-chart-range-btn" :class="pipelineView === 'lob' ? 'h2-chart-range-on' : ''" @click="pipelineView = 'lob'">LOB</button>
              </div>
            </div>

            <!-- Summary trio (always shown) -->
            <div class="mx-4 mt-4 grid grid-cols-3 overflow-hidden rounded-lg ring-1 ring-[var(--h2-border-strong)]">
              <div v-for="item in quotedPipelineSummaryCards" :key="item.label" class="bg-[var(--h2-surface-inset)] px-3 py-3 text-center ring-1 ring-[var(--h2-border)]">
                <p class="font-mono text-base font-bold tabular-nums text-[var(--h2-fg)]">{{ item.value }}</p>
                <p class="mt-0.5 text-[10px] font-medium uppercase tracking-wider text-[var(--h2-muted)]">{{ item.label }}</p>
              </div>
            </div>

            <!-- Pipeline sub-views -->
            <div class="mt-4 flex-1 px-5 pb-5">
              <!-- Mix view (segment breakdown) -->
              <template v-if="pipelineView === 'summary'">
                <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">Segment mix</p>
                <div class="mt-2.5 flex h-2.5 w-full overflow-hidden rounded-md" style="outline:1px solid var(--h2-border)">
                  <div
                    v-for="(row, i) in pipelineMixRows"
                    :key="row.label"
                    class="h-full cursor-pointer transition-all duration-500"
                    :style="segColorStyles[i] + ';width:' + row.pct + '%;opacity:' + (hoveredSegment === null || hoveredSegment === i ? '1' : '0.3')"
                    :title="`${row.label}: ${row.pct}%`"
                    @mouseenter="hoveredSegment = i"
                    @mouseleave="hoveredSegment = null"
                  />
                </div>
                <div class="mt-3.5 space-y-2">
                  <div
                    v-for="(row, i) in pipelineMixRows"
                    :key="row.label"
                    class="flex items-center gap-2 rounded-md px-1 py-0.5 -mx-1 cursor-pointer transition-all duration-150"
                    :style="hoveredSegment === i ? 'background: rgba(0,0,0,0.03)' : ''"
                    :class="hoveredSegment !== null && hoveredSegment !== i ? 'opacity-40' : ''"
                    @mouseenter="hoveredSegment = i"
                    @mouseleave="hoveredSegment = null"
                  >
                    <div class="h-2 w-2 shrink-0 rounded-sm" :style="segDotStyles[i]" />
                    <span class="flex-1 truncate text-[12px] font-medium text-[var(--h2-fg)]">{{ row.label }}</span>
                    <span class="font-mono text-[12px] tabular-nums text-[var(--h2-muted)]">{{ formatPremiumM(row.premiumM) }}</span>
                    <span class="w-8 text-right text-[11px] tabular-nums text-[var(--h2-muted)]">{{ row.pct }}%</span>
                  </div>
                </div>
              </template>

              <!-- Stage funnel view -->
              <template v-if="pipelineView === 'stage'">
                <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">Pipeline stages</p>
                <div class="mt-3 space-y-2.5">
                  <div v-for="stage in pipelineStages" :key="stage.label" class="h2-pipeline-stage">
                    <div class="flex items-center justify-between mb-1">
                      <span class="text-[12px] font-medium text-[var(--h2-fg)]">{{ stage.label }}</span>
                      <div class="flex items-center gap-2">
                        <span class="text-[11px] tabular-nums text-[var(--h2-muted)]">{{ stage.count }}</span>
                        <span class="font-mono text-[11px] font-semibold tabular-nums text-[var(--h2-fg)]">{{ stage.value }}</span>
                      </div>
                    </div>
                    <div class="h2-pipeline-bar">
                      <div class="h2-pipeline-bar-fill" :style="`width: ${stage.pct}%; background: ${stage.color}`" />
                    </div>
                  </div>
                </div>
              </template>

              <!-- LOB breakdown view -->
              <template v-if="pipelineView === 'lob'">
                <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">By line of business</p>
                <div class="mt-3 space-y-2.5">
                  <div v-for="lob in pipelineLobBreakdown" :key="lob.label" class="h2-pipeline-stage">
                    <div class="flex items-center justify-between mb-1">
                      <span class="text-[12px] font-medium text-[var(--h2-fg)]">{{ lob.label }}</span>
                      <div class="flex items-center gap-2">
                        <span class="text-[11px] tabular-nums text-[var(--h2-muted)]">{{ lob.count }} quotes</span>
                        <span class="font-mono text-[11px] font-semibold tabular-nums text-[var(--h2-fg)]">{{ lob.value }}</span>
                      </div>
                    </div>
                    <div class="h2-pipeline-bar">
                      <div class="h2-pipeline-bar-fill" :style="`width: ${lob.pct}%; background: var(--h2-accent)`" />
                    </div>
                  </div>
                </div>
              </template>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== BROKERAGE HEALTH ==================== -->
      <template #brokerage_health>
        <section v-if="welcome.ceoKpis?.length" class="space-y-4">
          <div class="h2-section-header">
            <h2 class="h2-section-title">Brokerage health</h2>
            <p class="h2-section-sub">YTD and trailing measures</p>
          </div>
          <div class="grid gap-3 sm:grid-cols-2 xl:grid-cols-4">
            <div v-for="k in welcome.ceoKpis" :key="k.id" class="h2-card p-5 transition-all duration-150 hover:shadow-md">
              <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">{{ k.label }}</p>
              <p class="mt-1.5 font-mono text-xl font-bold tabular-nums tracking-tight text-[var(--h2-fg)]">{{ k.value }}</p>
              <p v-if="k.change" class="mt-1 text-xs font-semibold" :class="changeToneClass(k.changeTone)">{{ k.change }}</p>
              <p v-if="k.hint" class="mt-1 text-[11px] text-[var(--h2-muted)]">{{ k.hint }}</p>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== SENT QUOTES ==================== -->
      <template #quotes_line>
        <section class="space-y-4" aria-labelledby="h2q">
          <div class="flex flex-wrap items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 id="h2q" class="h2-section-title">Sent Quotes</h2>
              <p class="h2-section-sub">{{ filteredSentQuotes.length }} quotes · ${{ (filteredSentQuotes.reduce((s, q) => s + q.premium, 0) / 1000).toFixed(0) }}K total premium</p>
            </div>
            <NuxtLink to="/quotes" class="text-[13px] font-medium h2-text-accent transition hover:h2-text-accent-hover">
              All quotes &rarr;
            </NuxtLink>
          </div>

          <!-- Filters row -->
          <div class="flex flex-wrap items-center gap-2">
            <select v-model="quoteSortKey" class="h2-qs-select">
              <option v-for="o in quoteSortOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
            </select>
            <select v-model="quoteFilterLob" class="h2-qs-select">
              <option v-for="o in quoteLobOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
            </select>
            <select v-model="quoteFilterParty" class="h2-qs-select">
              <option v-for="o in quotePartyOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
            </select>
            <select v-model="quoteFilterStatus" class="h2-qs-select">
              <option v-for="o in quoteStatusOptions" :key="o.value" :value="o.value">{{ o.label }}</option>
            </select>
          </div>

          <!-- Quotes list -->
          <div class="h2-card overflow-hidden">
            <div v-if="filteredSentQuotes.length === 0" class="px-6 py-10 text-center">
              <UIcon name="i-heroicons-document-text" style="width: 32px; height: 32px; color: #c0c0bc; margin: 0 auto 8px;" />
              <p class="text-[13px] text-[var(--h2-muted)]">No quotes match these filters</p>
            </div>
            <div
              v-for="(qt, qi) in filteredSentQuotes"
              :key="qt.id"
              class="h2-qs-row"
              :class="qi < filteredSentQuotes.length - 1 ? 'h2-qs-row-border' : ''"
            >
              <!-- LOB icon -->
              <div class="h2-qs-lob-icon">
                <UIcon :name="qt.lobIcon" style="width: 16px; height: 16px;" />
              </div>

              <!-- Main info -->
              <div class="min-w-0 flex-1">
                <div class="flex items-center gap-2 flex-wrap">
                  <p class="text-[13px] font-medium text-[var(--h2-fg)]">{{ qt.customer }}</p>
                  <span class="h2-qs-party">{{ qt.party === 'corporate' ? 'Corp' : 'Personal' }}</span>
                  <span :class="quoteStatusMeta[qt.status].cls">{{ quoteStatusMeta[qt.status].label }}</span>
                </div>
                <div class="mt-0.5 flex items-center gap-2 flex-wrap text-[11px] text-[var(--h2-muted)]">
                  <span class="font-mono">{{ qt.id }}</span>
                  <span class="h2-qs-sep" />
                  <span>{{ qt.lob }}</span>
                  <span class="h2-qs-sep" />
                  <span>{{ qt.carrier }}</span>
                  <span class="h2-qs-sep" />
                  <span>{{ qt.agent }}</span>
                </div>
              </div>

              <!-- Numbers -->
              <div class="text-right shrink-0">
                <p class="text-[14px] font-semibold tabular-nums text-[var(--h2-fg)]">${{ qt.premium.toLocaleString() }}</p>
                <p class="text-[11px] tabular-nums text-[var(--h2-muted)]">${{ qt.commission.toLocaleString() }} comm</p>
              </div>

              <!-- Date -->
              <div class="hidden sm:block shrink-0 text-right">
                <p class="text-[12px] tabular-nums text-[var(--h2-muted)]">{{ qt.sentDate }}</p>
              </div>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== CALENDAR SUMMARY ==================== -->
      <template #calendar>
        <section class="space-y-4">
          <div class="flex flex-wrap items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 class="h2-section-title">Today's agenda</h2>
              <p class="h2-section-sub">{{ filteredCalEvents.filter(e => e.urgent).length }} urgent · {{ filteredCalEvents.length }} total</p>
            </div>
            <NuxtLink to="/calendar" class="text-[13px] font-medium h2-text-accent transition hover:h2-text-accent-hover">
              Open calendar &rarr;
            </NuxtLink>
          </div>

          <div class="h2-card overflow-hidden">
            <!-- Compact upcoming list (max 4) -->
            <div class="cal-event-list">
              <div
                v-for="event in filteredCalEvents.slice(0, 4)"
                :key="event.id"
                class="cal-event-row"
                :class="event.urgent ? 'cal-event-urgent' : ''"
              >
                <div class="cal-event-time">{{ event.time }}</div>
                <div class="cal-event-dot" :style="calEventDotStyle(event.type)" />
                <div class="min-w-0 flex-1">
                  <div class="flex items-center gap-2">
                    <p class="text-[13px] font-medium text-[var(--h2-fg)]">{{ event.title }}</p>
                    <span v-if="event.urgent" class="cal-urgent-badge">Urgent</span>
                  </div>
                  <p v-if="event.customer" class="mt-0.5 text-[12px] text-[var(--h2-muted)]">{{ event.customer }}</p>
                </div>
                <UIcon :name="calEventTypeMeta[event.type].icon" class="cal-event-type-icon" :style="`color: ${calEventTypeMeta[event.type].color}`" />
              </div>
            </div>

            <!-- "More" footer -->
            <div v-if="filteredCalEvents.length > 4" class="cal-summary-footer">
              <NuxtLink to="/calendar" class="text-[12px] font-medium h2-text-accent">
                +{{ filteredCalEvents.length - 4 }} more today
              </NuxtLink>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== NOTES ==================== -->
      <template #notes>
        <section class="space-y-3">
          <div class="flex items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 class="h2-section-title">Notes</h2>
              <p class="h2-section-sub">{{ stickyNotes.length }} notes · auto-saved</p>
            </div>
            <button type="button" class="h2-sticky-add" @click="addStickyNote">
              <UIcon name="i-heroicons-plus" style="width: 12px; height: 12px;" />
              New note
            </button>
          </div>

          <div class="h2-notes-stack">
            <TransitionGroup
              enter-active-class="transition-all duration-200 ease-out"
              enter-from-class="opacity-0 -translate-y-1 scale-95"
              enter-to-class="opacity-100 translate-y-0 scale-100"
              leave-active-class="transition-all duration-150 ease-in"
              leave-from-class="opacity-100 max-h-60"
              leave-to-class="opacity-0 max-h-0"
            >
              <div
                v-for="note in sortedStickyNotes"
                :key="note.id"
                class="h2-note-card group/note"
                :class="note.expanded ? 'h2-note-expanded' : ''"
              >
                <!-- Color accent bar -->
                <div class="h2-note-accent" :style="`background: ${stickyColor(note.color).border}`" />

                <!-- Collapsed: single-line preview -->
                <div v-if="!note.expanded" class="h2-note-collapsed" @click="toggleStickyExpand(note.id)">
                  <UIcon v-if="note.pinned" name="i-heroicons-bookmark-solid" style="width: 11px; height: 11px; color: var(--h2-accent); flex-shrink: 0;" />
                  <span class="h2-note-preview">{{ stickyPreview(note.content) }}</span>
                  <div class="h2-note-actions">
                    <button type="button" class="h2-note-action" title="Pin" @click.stop="toggleStickyPin(note.id)">
                      <UIcon :name="note.pinned ? 'i-heroicons-bookmark-solid' : 'i-heroicons-bookmark'" style="width: 11px; height: 11px;" />
                    </button>
                    <button type="button" class="h2-note-action h2-note-action-color" title="Color" @click.stop="cycleStickyColor(note.id)">
                      <span class="h2-note-colordot" :style="`background: ${stickyColor(note.color).border}`" />
                    </button>
                    <button type="button" class="h2-note-action h2-note-action-danger" title="Delete" @click.stop="removeStickyNote(note.id)">
                      <UIcon name="i-heroicons-trash" style="width: 11px; height: 11px;" />
                    </button>
                  </div>
                </div>

                <!-- Expanded: editable textarea -->
                <div v-else class="h2-note-body">
                  <div class="h2-note-body-head">
                    <div class="flex items-center gap-1.5">
                      <button type="button" class="h2-note-action" title="Pin" @click="toggleStickyPin(note.id)">
                        <UIcon :name="note.pinned ? 'i-heroicons-bookmark-solid' : 'i-heroicons-bookmark'" style="width: 12px; height: 12px;" />
                      </button>
                      <div class="flex gap-0.5">
                        <button
                          v-for="c in STICKY_COLORS"
                          :key="c.id"
                          type="button"
                          class="h2-note-colordot-pick"
                          :class="note.color === c.id ? 'h2-note-colordot-active' : ''"
                          :style="`background: ${c.border}`"
                          :title="c.label"
                          @click="note.color = c.id"
                        />
                      </div>
                    </div>
                    <div class="flex items-center gap-1">
                      <button type="button" class="h2-note-action h2-note-action-danger" title="Delete" @click="removeStickyNote(note.id)">
                        <UIcon name="i-heroicons-trash" style="width: 12px; height: 12px;" />
                      </button>
                      <button type="button" class="h2-note-action" title="Collapse" @click="toggleStickyExpand(note.id)">
                        <UIcon name="i-heroicons-chevron-up" style="width: 12px; height: 12px;" />
                      </button>
                    </div>
                  </div>
                  <textarea
                    v-model="note.content"
                    class="h2-note-textarea"
                    placeholder="Write something..."
                    rows="4"
                  />
                </div>
              </div>
            </TransitionGroup>
          </div>
        </section>
      </template>

      <!-- ==================== QUICK LEADS: Last 10 days ==================== -->
      <template #quick_leads>
        <section class="space-y-3">
          <div class="flex items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 class="h2-section-title">Quick Leads</h2>
              <p class="h2-section-sub">Last 10 days · {{ dashQuickLeads.length }} leads</p>
            </div>
            <NuxtLink to="/sales/quick-lead" class="h2-ql-view-all">
              View all
              <UIcon name="i-heroicons-arrow-right" style="width: 10px; height: 10px;" />
            </NuxtLink>
          </div>

          <div v-if="dashQuickLeads.length === 0" key="ql-empty" class="h2-card h2-ql-empty">
            <UIcon name="i-heroicons-bolt" style="width: 24px; height: 24px; color: #c0c0bc;" />
            <p class="text-[12px] text-[var(--h2-muted)] mt-1.5">No quick leads in the last 10 days.</p>
            <NuxtLink to="/sales/quick-lead" class="h2-ql-add-link">
              <UIcon name="i-heroicons-plus" style="width: 10px; height: 10px;" />
              Capture a lead
            </NuxtLink>
          </div>

          <div v-else key="ql-list" class="h2-card h2-card-flush overflow-hidden">
            <div v-for="(lead, i) in dashQuickLeads" :key="lead.id" class="h2-ql-row" :class="i < dashQuickLeads.length - 1 ? 'h2-ql-row-border' : ''">
              <div class="h2-ql-avatar">{{ lead.name.split(' ').map(w => w[0]).join('').slice(0, 2) }}</div>
              <div class="min-w-0 flex-1">
                <div class="flex items-center gap-2">
                  <p class="text-[12px] font-medium text-[var(--h2-fg)] truncate">{{ lead.name }}</p>
                  <span :class="qlPriorityMeta(lead.priority).cls">{{ qlPriorityMeta(lead.priority).label }}</span>
                </div>
                <div class="flex items-center gap-1.5 mt-0.5 flex-wrap">
                  <span class="text-[11px] text-[var(--h2-muted)]">{{ lead.product }}</span>
                  <span class="h2-ql-sep" />
                  <span class="text-[11px] text-[var(--h2-muted)]">{{ qlTimeAgo(lead.createdAt) }}</span>
                  <template v-if="lead.source">
                    <span class="h2-ql-sep" />
                    <span class="h2-ql-source">
                      <UIcon name="i-heroicons-map-pin" style="width: 10px; height: 10px;" />
                      {{ lead.source }}
                    </span>
                  </template>
                </div>
                <p v-if="lead.note" class="mt-0.5 text-[11px] text-[var(--h2-muted)] truncate opacity-70">{{ lead.note }}</p>
              </div>
              <div class="hidden sm:flex flex-col items-end gap-0.5 shrink-0 text-right">
                <span v-if="lead.phone" class="text-[11px] text-[var(--h2-muted)] tabular-nums">{{ lead.phone }}</span>
                <span v-if="lead.email" class="text-[11px] text-[var(--h2-muted)] truncate max-w-[160px]">{{ lead.email }}</span>
              </div>
              <NuxtLink to="/quotes/new" class="h2-ql-quote-btn" title="Start quote">
                <UIcon name="i-heroicons-calculator" style="width: 12px; height: 12px;" />
              </NuxtLink>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== SALES LEADS ==================== -->
      <template #sales_leads>
        <section class="space-y-3">
          <div class="flex items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 class="h2-section-title">Sales Leads</h2>
              <p class="h2-section-sub">{{ filteredSalesLeads.length }} leads{{ slSourceFilter !== 'all' ? ' · ' + slSourceLabel(slSourceFilter) : '' }}</p>
            </div>
            <div class="flex items-center gap-2">
              <span class="text-[10px] font-medium text-[var(--h2-muted)] uppercase tracking-wide hidden sm:inline">API integrations coming soon</span>
              <NuxtLink to="/sales" class="h2-ql-view-all">
                Manage leads
                <UIcon name="i-heroicons-arrow-right" style="width: 10px; height: 10px;" />
              </NuxtLink>
            </div>
          </div>

          <!-- Source filter pills -->
          <div class="flex items-center gap-1.5 overflow-x-auto pb-1 -mb-1">
            <button
              v-for="src in SALES_LEAD_SOURCES"
              :key="src.value"
              type="button"
              class="h2-sl-filter-pill"
              :class="slSourceFilter === src.value ? 'h2-sl-filter-pill--active' : ''"
              @click="slSourceFilter = src.value as any"
            >
              <UIcon :name="src.icon" style="width: 11px; height: 11px;" />
              {{ src.label }}
              <span v-if="slSourceCounts[src.value]" class="h2-sl-filter-count">{{ slSourceCounts[src.value] }}</span>
            </button>
          </div>

          <!-- Leads table -->
          <div class="h2-card h2-card-flush overflow-hidden">
            <!-- Header row -->
            <div class="h2-sl-header">
              <span class="flex-1">Lead</span>
              <span class="w-[80px] text-center hidden sm:block">Source</span>
              <span class="w-[70px] text-center hidden sm:block">Status</span>
              <span class="w-[70px] text-right hidden md:block">Value</span>
              <span class="w-[80px] text-right hidden lg:block">Assigned</span>
              <span class="w-[24px]"></span>
            </div>

            <div v-if="filteredSalesLeads.length === 0" class="h2-sl-empty">
              <UIcon name="i-heroicons-funnel" style="width: 20px; height: 20px; color: #c0c0bc;" />
              <p>No leads match this filter.</p>
            </div>

            <div v-for="(lead, i) in filteredSalesLeads" :key="lead.id" class="h2-sl-row" :class="i < filteredSalesLeads.length - 1 ? 'h2-ql-row-border' : ''">
              <!-- Avatar + name block -->
              <div class="flex items-center gap-2.5 min-w-0 flex-1">
                <div class="h2-ql-avatar" :style="'background: rgba(1,105,111,0.07); color: #01696f'">{{ lead.name.split(' ').map((w: string) => w[0]).join('').slice(0, 2) }}</div>
                <div class="min-w-0 flex-1">
                  <div class="flex items-center gap-2">
                    <p class="text-[12px] font-medium text-[var(--h2-fg)] truncate">{{ lead.name }}</p>
                    <span class="text-[10px] text-[var(--h2-muted)]">{{ lead.lob }}</span>
                  </div>
                  <div class="flex items-center gap-1 mt-0.5">
                    <span v-if="lead.phone" class="text-[10px] text-[var(--h2-muted)] tabular-nums">{{ lead.phone }}</span>
                    <span v-if="lead.phone && lead.email" class="h2-ql-sep" />
                    <span v-if="lead.email" class="text-[10px] text-[var(--h2-muted)] truncate">{{ lead.email }}</span>
                  </div>
                  <p v-if="lead.note || lead.campaignName" class="text-[10px] text-[var(--h2-muted)] truncate opacity-60 mt-0.5">
                    {{ lead.campaignName ? '📣 ' + lead.campaignName : lead.note }}
                  </p>
                </div>
              </div>
              <!-- Source -->
              <div class="w-[80px] hidden sm:flex items-center justify-center">
                <span class="h2-sl-source-chip">
                  <UIcon :name="slSourceIcon(lead.source)" style="width: 10px; height: 10px;" />
                  {{ slSourceLabel(lead.source) }}
                </span>
              </div>
              <!-- Status -->
              <div class="w-[70px] hidden sm:flex items-center justify-center">
                <span :class="slStatusBadge(lead.status).cls">{{ slStatusBadge(lead.status).label }}</span>
              </div>
              <!-- Value -->
              <div class="w-[70px] hidden md:flex items-center justify-end">
                <span v-if="lead.value" class="text-[11px] font-medium text-[var(--h2-fg)] tabular-nums">{{ slFmtValue(lead.value) }}</span>
              </div>
              <!-- Assigned -->
              <div class="w-[80px] hidden lg:flex items-center justify-end">
                <span class="text-[10px] text-[var(--h2-muted)]">{{ lead.assignedTo }}</span>
              </div>
              <!-- Action -->
              <NuxtLink to="/quotes/new" class="h2-ql-quote-btn" title="Start quote">
                <UIcon name="i-heroicons-arrow-right" style="width: 10px; height: 10px;" />
              </NuxtLink>
            </div>
          </div>
        </section>
      </template>

      <!-- ==================== CLIENT FAVORITES ==================== -->
      <template #client_favorites>
        <section class="space-y-3">
          <div class="flex items-end justify-between gap-3">
            <div class="h2-section-header">
              <h2 class="h2-section-title">Favorite Clients</h2>
              <p class="h2-section-sub">{{ favCustomers.length }} starred</p>
            </div>
            <NuxtLink to="/customers" class="h2-ql-view-all">
              All customers
              <UIcon name="i-heroicons-arrow-right" style="width: 10px; height: 10px;" />
            </NuxtLink>
          </div>

          <div v-if="favCustomers.length === 0" class="h2-ql-empty">
            <UIcon name="i-heroicons-star" style="width: 24px; height: 24px; color: #c0c0bc;" />
            <p class="text-[12px] text-[var(--h2-muted)] mt-1.5">No favorite clients yet. Star clients from the customer list.</p>
          </div>

          <div v-else class="h2-fav-grid">
            <NuxtLink
              v-for="c in favCustomers" :key="c.id"
              :to="`/customers/${c.id}`"
              class="h2-fav-card"
            >
              <div class="h2-fav-top">
                <div class="h2-fav-avatar">{{ c.initials }}</div>
                <button
                  type="button"
                  class="h2-fav-star"
                  title="Remove from favorites"
                  @click.prevent.stop="unfavClient(c.id)"
                >
                  <UIcon name="i-heroicons-star-solid" style="width: 12px; height: 12px;" />
                </button>
              </div>
              <p class="h2-fav-name">{{ c.name }}</p>
              <span :class="favTierClass(c)" class="h2-fav-tier">{{ favTierLabel(c) }}</span>
              <div class="h2-fav-stats">
                <span>{{ c.policies.length }} {{ c.policies.length === 1 ? 'policy' : 'policies' }}</span>
                <span v-if="favTotalPremium(c) > 0">{{ favFmtMoney(favTotalPremium(c)) }}/yr</span>
              </div>
              <div class="h2-fav-meta">
                <span>{{ c.agent }}</span>
                <span>{{ c.type }}</span>
              </div>
            </NuxtLink>
          </div>
        </section>
      </template>

      <!-- ==================== DRAFTS ==================== -->
      <template #drafts>
        <section class="space-y-3">
          <div class="h2-section-header">
            <h2 class="h2-section-title">Drafts</h2>
            <p class="h2-section-sub">Resume in-progress work</p>
          </div>

          <div v-if="activeDrafts.length > 0" class="grid gap-2">
            <div
              v-for="draft in activeDrafts"
              :key="draft.key"
              class="h2-card flex items-center gap-3 px-4 py-3"
            >
              <div class="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg" style="background: rgba(1,105,111,0.08); color: #01696f;">
                <UIcon :name="draft.icon" style="width: 16px; height: 16px;" />
              </div>
              <div class="min-w-0 flex-1">
                <p class="text-[13px] font-semibold text-[var(--h2-fg)]">{{ draft.label }}</p>
                <p class="text-[11px] text-[var(--h2-muted)]">Saved {{ draft.age }}</p>
              </div>
              <NuxtLink :to="draft.route" class="text-[12px] font-semibold" style="color: #01696f;">Resume</NuxtLink>
              <button type="button" class="text-[var(--h2-muted)] opacity-50 hover:opacity-100 transition" title="Discard draft" @click="removeDraft(draft.key)">
                <UIcon name="i-heroicons-x-mark" style="width: 14px; height: 14px;" />
              </button>
            </div>
          </div>

          <div v-else class="h2-card px-4 py-6 text-center">
            <p class="text-[12px] text-[var(--h2-muted)]">No drafts in progress</p>
          </div>
        </section>
      </template>
    </HomeDashboardWidgetBlocks>

    <!-- Layout controls -->
    <div class="mx-auto mt-12 flex max-w-6xl flex-col gap-3 border-t border-[var(--h2-border)] pt-6 sm:flex-row sm:items-center sm:justify-between">
      <div class="flex flex-wrap items-center gap-2">
        <UButton
          :icon="layoutUnlocked ? 'i-heroicons-arrows-up-down' : 'i-heroicons-lock-closed'"
          :color="layoutUnlocked ? 'primary' : 'neutral'" variant="soft" class="h2-btn-outline"
          @click="toggleLayoutUnlock"
        >{{ layoutUnlocked ? 'Reorder on' : 'Reorder off' }}</UButton>
        <p class="max-w-md text-xs text-[var(--h2-muted)]">Drag blocks by the grip when reorder is on.</p>
      </div>
      <UButton icon="i-heroicons-squares-2x2" color="primary" class="h2-btn-primary shrink-0" @click="dashConfigOpen = true">
        Layout &amp; widgets
      </UButton>
    </div>

    <!-- Slideover -->
    <USlideover v-model:open="dashConfigOpen" side="right">
      <template #content>
        <div class="flex h-full max-w-md flex-col bg-[var(--h2-surface)] sm:max-w-lg">
          <div class="flex shrink-0 items-start justify-between gap-3 border-b border-[var(--h2-border)] p-6">
            <div class="min-w-0">
              <h2 class="text-lg font-semibold text-[var(--h2-fg)]">Dashboard layout</h2>
              <p class="mt-1 text-sm text-[var(--h2-fg-secondary)]">Choose a role preset or toggle sections.</p>
            </div>
            <UButton icon="i-heroicons-x-mark" color="neutral" variant="ghost" class="shrink-0" aria-label="Close" @click="dashConfigOpen = false" />
          </div>
          <div class="min-h-0 flex-1 overflow-y-auto p-6">
            <div class="space-y-6">
              <div>
                <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">Role preset</p>
                <USelect :model-value="activePreset" :items="presetSelectItems" value-key="value" label-key="label" class="mt-2 w-full"
                  @update:model-value="applyPreset($event as DashboardRolePresetId)" />
                <p class="mt-2 text-xs text-[var(--h2-muted)]">{{ activePresetHint }}</p>
                <UButton v-if="isPresetDirty" size="xs" color="neutral" variant="soft" class="mt-2" @click="reapplySelectedPreset">Reset to preset</UButton>
              </div>
              <div class="border-t border-[var(--h2-border)] pt-4">
                <p class="text-[11px] font-semibold uppercase tracking-wider text-[var(--h2-muted)]">Sections</p>
                <ul class="mt-3 space-y-4">
                  <li v-for="w in DASHBOARD_WIDGETS" :key="w.id" class="flex items-start justify-between gap-3">
                    <div class="min-w-0">
                      <p class="text-sm font-medium text-[var(--h2-fg)]">{{ w.label }}</p>
                      <p class="text-xs text-[var(--h2-muted)]">{{ w.description }}</p>
                    </div>
                    <USwitch :model-value="widgets[w.id]" @update:model-value="setWidget(w.id, $event)" />
                  </li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </template>
    </USlideover>
  </div>
</template>

<style scoped>
/* =====================================================================
   HOME 2 — PETROLEUM / STONE DESIGN SYSTEM (scoped)
   ===================================================================== */

/* ---- Palette ---- */
.h2 {
  /* Petroleum accent scale */
  /* Map scoped vars to global theme tokens for theme-awareness */
  --h2-accent:        var(--brand, #0d5c63);
  --h2-accent-hover:  var(--brand-hover, #094a50);
  --h2-accent-muted:  color-mix(in srgb, var(--brand) 60%, var(--text-muted));
  --h2-accent-soft:   var(--brand) / 0.08;

  /* Surfaces — inherit from global theme */
  --h2-page-bg:       var(--page-bg, #f4f2ef);
  --h2-surface:       var(--surface, #faf9f7);
  --h2-surface-raised:var(--surface-elevated, #ffffff);
  --h2-surface-inset: var(--page-bg, #f0eeeb);

  /* Foregrounds — inherit from global theme */
  --h2-fg:            var(--text-primary, #1a1a1a);
  --h2-fg-secondary:  var(--text-muted, #5c5650);
  --h2-muted:         var(--text-muted, #8c857d);

  /* Borders — inherit from global theme */
  --h2-border:        var(--card-border, #e5e0da);
  --h2-border-strong: var(--sidebar-border, #d5cfc8);

  /* Semantic — inherit from global theme */
  --h2-success:       var(--success, #0f7b5f);
  --h2-warning:       var(--warning, #c27b1a);
  --h2-error:         var(--error, #c13838);
  --h2-info:          var(--brand, #0d5c63);

  background: var(--h2-page-bg);
}

/* ---- Card system ---- */
.h2-card {
  background: #ffffff;
  border-radius: 12px;
  border: 1px solid rgba(0, 0, 0, 0.06);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
}
.h2-card-flush {
  border-radius: 12px;
  overflow: hidden;
}

/* ---- Ops cells (modernised command bar — unified card) ---- */
.h2-ops-cell {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 16px 20px;
  transition: background 150ms ease;
}
.h2-ops-cell:hover {
  background: rgba(0, 0, 0, 0.015);
}
/* Lead cell gets a very subtle warm accent wash */
.h2-ops-cell--lead {
  background: rgba(1, 105, 111, 0.025);
}
.h2-ops-cell--lead:hover {
  background: rgba(1, 105, 111, 0.04);
}
.h2-ops-cell--lead .h2-ops-cell-value {
  color: #01696f;
}
.h2-ops-cell-icon {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px;
  border-radius: 8px;
  flex-shrink: 0;
  margin-top: 1px;
}
.h2-ops-cell-body {
  min-width: 0; flex: 1;
}
.h2-ops-cell-label {
  font-size: 11px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.06em; color: #8a8a86;
}
.h2-ops-cell-value {
  font-size: 22px; font-weight: 600; letter-spacing: -0.01em;
  color: #1a1a18; line-height: 1.2;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}
.h2-ops-cell-target {
  display: flex; align-items: center; gap: 5px;
  margin-top: 3px;
  font-size: 11px; color: var(--h2-muted);
}

/* ---- Milestone card (modernised) ---- */
.h2-milestone-card {
  background: rgba(1, 105, 111, 0.035);
  border: 1px solid rgba(1, 105, 111, 0.08);
  border-radius: 12px;
  padding: 12px 18px;
}
.h2-milestone-top {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.h2-milestone-badge {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px;
  background: color-mix(in srgb, var(--ms-color) 10%, transparent);
  color: var(--ms-color);
}
.h2-milestone-status {
  font-size: 13px; font-weight: 600; display: block; line-height: 1.2;
}
.h2-milestone-tag {
  font-size: 10px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.06em; color: #8a8a86; display: block;
}
.h2-milestone-bar-wrap {
  display: flex; align-items: center; gap: 8px;
  min-width: 120px; flex: 0 1 200px;
}
.h2-milestone-bar-track {
  flex: 1; height: 5px; border-radius: 3px;
  background: rgba(0, 0, 0, 0.06);
  overflow: hidden;
}
.h2-milestone-bar-fill {
  height: 100%; border-radius: 3px;
  transition: width 600ms cubic-bezier(0.22, 1, 0.36, 1);
}
.h2-milestone-bar-pct {
  font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums;
  min-width: 32px; text-align: right;
}

/* Metric pills row */
.h2-milestone-metrics {
  display: flex; flex-wrap: wrap; gap: 6px;
  flex: 1; min-width: 0;
}
.h2-milestone-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 8px;
  border-radius: 6px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid rgba(0, 0, 0, 0.05);
  font-size: 12px;
}
.h2-milestone-pill-label {
  font-weight: 500; color: #8a8a86;
}
.h2-milestone-pill-val {
  font-weight: 700; color: #1a1a18;
  font-variant-numeric: tabular-nums;
}
.h2-milestone-pill-sep { color: #ccc; font-weight: 300; }
.h2-milestone-pill-target {
  color: #8a8a86; font-variant-numeric: tabular-nums;
}
.h2-milestone-pill-pct {
  font-size: 10px; font-weight: 600; padding: 1px 5px;
  border-radius: 9999px; margin-left: 1px;
}
.h2-milestone-pill-pct--good {
  background: rgba(15, 123, 95, 0.08); color: #0f7b5f;
}
.h2-milestone-pill-pct--close {
  background: rgba(194, 123, 26, 0.08); color: #c27b1a;
}
.h2-milestone-pill-pct--behind {
  background: rgba(193, 56, 56, 0.08); color: #c13838;
}

/* ---- Ops status dot ---- */
.h2-ops-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* ---- Ops rail config button ---- */
.h2-ops-rail-wrap { position: relative; }
.h2-ops-config-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: 8px;
  border: 1px solid rgba(0,0,0,0.08);
  background: #ffffff;
  color: #8a8a86;
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
  transition: all 150ms ease;
}
.h2-ops-config-btn:hover {
  color: #01696f;
  border-color: rgba(1,105,111,0.2);
  box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}

/* ---- Ops config popover ---- */
.h2-ops-config-popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 300px;
  max-height: 480px;
  overflow-y: auto;
  border-radius: 12px;
  border: 1px solid rgba(0,0,0,0.08);
  background: #ffffff;
  box-shadow: 0 8px 30px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.06);
  z-index: 30;
}
.h2-ops-config-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--text-primary);
}
.h2-ops-config-reset {
  font-size: 11px;
  font-weight: 500;
  color: #01696f;
  background: none; border: none; cursor: pointer;
  padding: 2px 6px; border-radius: 4px;
  transition: background 150ms ease;
}
.h2-ops-config-reset:hover { background: rgba(1,105,111,0.06); }
.h2-ops-config-hint {
  padding: 4px 16px 8px;
  font-size: 11px;
  color: #8a8a86;
}
.h2-ops-config-list {
  max-height: 320px;
  overflow-y: auto;
  list-style: none;
  margin: 0; padding: 0;
}
.h2-ops-config-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  cursor: pointer;
  transition: background 100ms ease;
  border-bottom: 1px solid rgba(0,0,0,0.03);
}
.h2-ops-config-item:last-child { border-bottom: none; }
.h2-ops-config-item:hover { background: rgba(0,0,0,0.02); }
.h2-ops-config-item-on { background: rgba(1,105,111,0.03); }
.h2-ops-config-item-on:hover { background: rgba(1,105,111,0.05); }
.h2-ops-config-check {
  width: 18px; height: 18px;
  border-radius: 5px;
  border: 1.5px solid rgba(0,0,0,0.15);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: all 150ms ease;
}
.h2-ops-config-item-on .h2-ops-config-check {
  background: #01696f;
  border-color: #01696f;
  color: #ffffff;
}
.h2-ops-config-dot {
  width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0;
}
.h2-ops-config-footer {
  padding: 8px 16px;
  border-top: 1px solid rgba(0,0,0,0.06);
  font-size: 10px;
  color: #8a8a86;
  text-align: center;
}

/* ---- Sent Quotes widget ---- */
.h2-qs-select {
  appearance: none;
  padding: 4px 24px 4px 8px;
  border-radius: 6px;
  border: 1px solid rgba(0,0,0,0.08);
  background: #ffffff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238a8a86' stroke-width='1.2' stroke-linecap='round'/%3E%3C/svg%3E") no-repeat right 8px center;
  font-size: 11px;
  font-weight: 500;
  color: var(--text-primary);
  cursor: pointer;
  transition: border-color 150ms ease;
  min-width: 0;
}
.h2-qs-select:hover { border-color: rgba(0,0,0,0.15); }
.h2-qs-select:focus { outline: none; border-color: #01696f; }

.h2-qs-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  transition: background 100ms ease;
}
.h2-qs-row:hover { background: rgba(0,0,0,0.015); }
.h2-qs-row-border { border-bottom: 1px solid rgba(0,0,0,0.04); }

.h2-qs-lob-icon {
  width: 32px; height: 32px;
  border-radius: 8px;
  background: rgba(1,105,111,0.06);
  color: #01696f;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.h2-qs-party {
  font-size: 9px; font-weight: 600; text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 5px; border-radius: 4px;
  background: rgba(0,0,0,0.04); color: #8a8a86;
}
.h2-qs-sep {
  width: 3px; height: 3px; border-radius: 50%;
  background: rgba(0,0,0,0.15); flex-shrink: 0;
}
/* Status badges */
.h2-qs-sent {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(0,0,0,0.04); color: #8a8a86;
}
.h2-qs-viewed {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(1,105,111,0.08); color: #01696f;
}
.h2-qs-accepted {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(15,123,95,0.08); color: #0f7b5f;
}
.h2-qs-expired {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(194,123,26,0.08); color: #964219;
}
.h2-qs-declined {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(193,56,56,0.08); color: #c13838;
}

/* ---- Section headers ---- */
.h2-section-header { }
.h2-section-title {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: #1a1a18;
}
.h2-section-sub {
  margin-top: 2px;
  font-size: 13px;
  color: #8a8a86;
}

/* ---- Card headers (icon + title) ---- */
.h2-card-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 20px 20px 16px;
  border-bottom: 1px solid var(--h2-border);
}
.h2-icon-box {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 8px;
  background: rgba(1, 105, 111, 0.06);
  color: var(--h2-accent);
}
.h2-icon-box-error {
  background: color-mix(in srgb, var(--h2-error) 10%, transparent);
  color: var(--h2-error);
}

/* ---- Lists ---- */
.h2-list {
  padding: 0;
  list-style: none;
  margin: 0;
}

/* ---- Alert rows ---- */
.h2-alert-row {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.75rem 0.75rem 0.75rem 1rem;
  border-radius: 8px;
  transition: background 150ms ease;
}

/* ---- Ops cell borders (internal vertical dividers) ---- */
.h2-cell-border {
  border-right: 1px solid rgba(0, 0, 0, 0.06);
}
@media (max-width: 639px) {
  .h2-cell-border:nth-child(2n) { border-right: none; }
  .h2-cell-border { border-bottom: 1px solid rgba(0, 0, 0, 0.06); }
}

/* ---- Tone classes ---- */
.h2-tone-pos     { color: var(--h2-success); }
.h2-tone-neg     { color: var(--h2-error); }
.h2-tone-neutral { color: var(--h2-muted); }

/* ---- Badges ---- */
.h2-badge-success {
  display: inline-flex;
  align-items: center;
  border-radius: 9999px;
  padding: 0.125rem 0.625rem;
  font-size: 0.6875rem;
  font-weight: 600;
  background: color-mix(in srgb, var(--h2-success) 10%, transparent);
  color: var(--h2-success);
  border: 1px solid color-mix(in srgb, var(--h2-success) 20%, transparent);
}

/* ---- Buttons ---- */
.h2-btn-primary {
  background: var(--brand) !important;
  color: #fff !important;
  border-radius: 10px !important;
  border: none !important;
  box-shadow: 0 1px 3px color-mix(in srgb, var(--brand) 30%, transparent), 0 1px 2px color-mix(in srgb, var(--brand) 18%, transparent) !important;
  transition: background 150ms ease, box-shadow 150ms ease !important;
}
.h2-btn-primary:hover {
  background: var(--brand-hover) !important;
  box-shadow: 0 2px 6px color-mix(in srgb, var(--brand) 35%, transparent), 0 1px 3px color-mix(in srgb, var(--brand) 22%, transparent) !important;
}
.h2-btn-outline {
  border-radius: 10px !important;
  border-color: var(--h2-border-strong) !important;
  color: var(--h2-fg) !important;
}

/* ---- Accent icon tile ---- */
.h2-accent-icon {
  background: rgba(1, 105, 111, 0.06);
  color: var(--brand);
  border: 1px solid rgba(1, 105, 111, 0.1);
}

/* ---- Accent text ---- */
.h2-text-accent { color: var(--brand); }
.h2-text-accent-hover { color: var(--brand-hover); }

/* ---- Calendar summary widget ---- */
.cal-event-list { display: flex; flex-direction: column; }
.cal-event-row {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px 20px; border-bottom: 1px solid rgba(0,0,0,0.04);
  transition: background 100ms ease;
}
.cal-event-row:last-child { border-bottom: none; }
.cal-event-row:hover { background: rgba(0,0,0,0.015); }
.cal-event-urgent { background: rgba(193,56,56,0.02); }
.cal-event-time {
  width: 60px; flex-shrink: 0; font-size: 12px; font-weight: 500;
  color: var(--text-muted); font-variant-numeric: tabular-nums; padding-top: 1px;
}
.cal-event-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; margin-top: 5px; }
.cal-event-type-icon { width: 16px; height: 16px; flex-shrink: 0; margin-top: 2px; opacity: 0.4; }
.cal-urgent-badge {
  font-size: 10px; font-weight: 600; padding: 1px 6px; border-radius: 9999px;
  background: rgba(193,56,56,0.08); color: #c13838; flex-shrink: 0;
}
.cal-summary-footer {
  padding: 10px 20px; border-top: 1px solid rgba(0,0,0,0.04); text-align: center;
}

/* ---- Notes widget (compact expandable cards) ---- */
.h2-sticky-add {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border-radius: 6px;
  border: 1px solid rgba(0,0,0,0.08);
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
  transition: all 150ms ease;
}
.h2-sticky-add:hover { border-color: rgba(1,105,111,0.2); color: #01696f; }

.h2-notes-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
  position: relative;
}

.h2-note-card {
  display: flex;
  border-radius: 8px;
  border: 1px solid rgba(0,0,0,0.06);
  background: #fff;
  overflow: hidden;
  transition: all 200ms ease;
}
.h2-note-card:hover { border-color: rgba(0,0,0,0.1); }
.h2-note-expanded {
  box-shadow: 0 2px 8px rgba(0,0,0,0.06);
  border-color: rgba(0,0,0,0.1);
  flex-direction: column;
}

.h2-note-accent {
  width: 3px;
  flex-shrink: 0;
  border-radius: 3px 0 0 3px;
}
.h2-note-expanded .h2-note-accent {
  width: 100%;
  height: 3px;
  border-radius: 3px 3px 0 0;
}

/* Collapsed row */
.h2-note-collapsed {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 10px 8px 10px;
  flex: 1;
  min-width: 0;
  cursor: pointer;
  transition: background 100ms ease;
}
.h2-note-collapsed:hover { background: rgba(0,0,0,0.01); }

.h2-note-preview {
  flex: 1;
  font-size: 12px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.h2-note-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  opacity: 0;
  transition: opacity 150ms ease;
}
.h2-note-collapsed:hover .h2-note-actions,
.group\/note:hover .h2-note-actions { opacity: 1; }

.h2-note-action {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: 5px;
  border: none;
  background: transparent;
  color: #8a8a86;
  cursor: pointer;
  transition: all 120ms ease;
}
.h2-note-action:hover { background: rgba(0,0,0,0.05); color: var(--text-primary); }
.h2-note-action-danger:hover { color: #c13838; background: rgba(193,56,56,0.06); }
.h2-note-action-color { padding: 0; }

.h2-note-colordot {
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 1px solid rgba(0,0,0,0.08);
}

/* Expanded body */
.h2-note-body {
  display: flex;
  flex-direction: column;
}
.h2-note-body-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 8px;
  border-bottom: 1px solid rgba(0,0,0,0.04);
}

.h2-note-colordot-pick {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 2px solid transparent;
  cursor: pointer;
  transition: all 150ms ease;
}
.h2-note-colordot-pick:hover { transform: scale(1.15); }
.h2-note-colordot-active { border-color: rgba(0,0,0,0.3); box-shadow: 0 0 0 1px #fff inset; }

.h2-note-textarea {
  display: block;
  width: 100%;
  min-height: 80px;
  padding: 10px 12px;
  border: none;
  outline: none;
  resize: vertical;
  font-size: 12px;
  line-height: 1.65;
  color: var(--text-primary);
  background: transparent;
  font-family: inherit;
}
.h2-note-textarea::placeholder { color: var(--text-muted); opacity: 0.5; }

/* ---- Chart toolbar ---- */
.h2-chart-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 12px 16px;
  border-bottom: 1px solid rgba(0,0,0,0.04);
  flex-wrap: wrap;
}
.h2-chart-metric-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px;
  border-radius: 6px;
  border: none;
  background: transparent;
  cursor: pointer;
  transition: background 150ms ease;
}
.h2-chart-metric-btn:hover { background: rgba(0,0,0,0.03); }

.h2-chart-metric-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 30;
  width: 220px;
  border-radius: 10px;
  border: 1px solid rgba(0,0,0,0.08);
  background: #fff;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  overflow: hidden;
  padding: 4px 0;
}
.h2-chart-metric-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 8px 12px;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  transition: background 100ms ease;
}
.h2-chart-metric-item:hover { background: rgba(0,0,0,0.03); }
.h2-chart-metric-item-on { background: rgba(1,105,111,0.04); font-weight: 600; }

.h2-chart-sep {
  width: 1px;
  height: 16px;
  background: rgba(0,0,0,0.08);
}
.h2-chart-type-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 4px 6px;
  border-radius: 4px;
  border: none;
  font-size: 10px;
  color: var(--text-muted);
  background: transparent;
  cursor: pointer;
  transition: all 150ms ease;
}

/* ---- Pipeline stage bars ---- */
.h2-pipeline-stage {}
.h2-pipeline-bar {
  height: 6px;
  border-radius: 3px;
  background: rgba(0,0,0,0.04);
  overflow: hidden;
}
.h2-pipeline-bar-fill {
  height: 100%;
  border-radius: 3px;
  transition: width 500ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* ---- Tasks widget interactivity ---- */
.h2-task-add-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px; height: 28px;
  border-radius: 6px;
  border: 1px solid rgba(0,0,0,0.08);
  background: transparent;
  color: #8a8a86;
  cursor: pointer;
  transition: all 150ms ease;
}
.h2-task-add-btn:hover { background: rgba(1,105,111,0.06); color: #01696f; border-color: rgba(1,105,111,0.2); }

.h2-task-progress {
  height: 2px;
  background: rgba(0,0,0,0.04);
  overflow: hidden;
}
.h2-task-progress-fill {
  height: 100%;
  background: #01696f;
  border-radius: 1px;
  transition: width 400ms cubic-bezier(0.4, 0, 0.2, 1);
}

.h2-task-add-row {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid rgba(0,0,0,0.04);
  overflow: hidden;
}
.h2-task-add-input {
  flex: 1;
  padding: 5px 8px;
  border-radius: 6px;
  border: 1px solid rgba(0,0,0,0.1);
  font-size: 12px;
  color: var(--text-primary);
  outline: none;
  transition: border-color 150ms ease;
}
.h2-task-add-input:focus { border-color: #01696f; }
.h2-task-add-submit {
  padding: 5px 10px;
  border-radius: 6px;
  border: none;
  background: #01696f;
  color: #fff;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background 150ms ease;
}
.h2-task-add-submit:hover { background: #015a5f; }
.h2-task-add-submit:disabled { opacity: 0.4; cursor: not-allowed; }

.h2-task-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(0,0,0,0.03);
  transition: background 100ms ease;
}
.h2-task-item:hover { background: rgba(0,0,0,0.01); }
.h2-task-item:last-child { border-bottom: none; }

.h2-task-checkbox {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: 5px;
  border: 1.5px solid rgba(0,0,0,0.18);
  background: transparent;
  color: transparent;
  cursor: pointer;
  flex-shrink: 0;
  margin-top: 1px;
  transition: all 200ms ease;
}
.h2-task-checkbox:hover { border-color: #01696f; }
.h2-task-checkbox-done {
  background: #01696f;
  border-color: #01696f;
  color: #fff;
}

.h2-task-text {
  font-size: 13px;
  line-height: 1.4;
  transition: all 200ms ease;
}
.h2-task-done {
  text-decoration: line-through;
  color: #b0b0ac !important;
  font-weight: 400 !important;
}

.h2-task-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 20px; height: 20px;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: #c13838;
  cursor: pointer;
  opacity: 0;
  flex-shrink: 0;
  transition: all 150ms ease;
}
.group\/task:hover .h2-task-remove { opacity: 0.5; }
.h2-task-remove:hover { opacity: 1 !important; background: rgba(193,56,56,0.06); }

/* ---- Alert dismiss ---- */
.h2-alert-dismiss {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: 5px;
  border: none;
  background: transparent;
  color: #8a8a86;
  cursor: pointer;
  opacity: 0;
  flex-shrink: 0;
  transition: all 150ms ease;
}
.group\/alert:hover .h2-alert-dismiss { opacity: 0.6; }
.h2-alert-dismiss:hover { opacity: 1 !important; background: rgba(0,0,0,0.05); }

.h2-alert-restore {
  font-size: 11px;
  font-weight: 500;
  color: #01696f;
  background: none;
  border: none;
  cursor: pointer;
  padding: 2px 8px;
  border-radius: 4px;
  transition: background 150ms ease;
}
.h2-alert-restore:hover { background: rgba(1,105,111,0.06); }

/* ---- Milestone config ---- */
.h2-milestone-bottom {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
}
.h2-mile-config-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  border-radius: 6px;
  border: none;
  background: rgba(0,0,0,0.03);
  color: #8a8a86;
  cursor: pointer;
  transition: all 150ms ease;
  flex-shrink: 0;
}
.h2-mile-config-btn:hover { background: rgba(0,0,0,0.06); color: #01696f; }

.h2-mile-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 30;
  width: 260px;
  border-radius: 10px;
  border: 1px solid rgba(0,0,0,0.08);
  background: #fff;
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  overflow: hidden;
}
.h2-mile-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  cursor: pointer;
  transition: background 100ms ease;
}
.h2-mile-item:hover { background: rgba(0,0,0,0.02); }
.h2-mile-item-on { background: rgba(1,105,111,0.03); }
.h2-mile-check {
  width: 16px; height: 16px;
  border-radius: 4px;
  border: 1.5px solid rgba(0,0,0,0.15);
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  transition: all 150ms ease;
}
.h2-mile-check-on {
  background: #01696f;
  border-color: #01696f;
  color: #fff;
}

/* ---- Chart range toggle ---- */
.h2-chart-range {
  display: inline-flex;
  gap: 1px;
  padding: 2px;
  border-radius: 6px;
  background: rgba(0,0,0,0.04);
}
.h2-chart-range-btn {
  padding: 3px 8px;
  border-radius: 4px;
  border: none;
  font-size: 10px;
  font-weight: 600;
  cursor: pointer;
  transition: all 150ms ease;
  color: var(--text-muted);
  background: transparent;
}
.h2-chart-range-on {
  background: #fff;
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(0,0,0,0.06);
}

/* ---- Card hover state ---- */
.h2-card-hover:hover {
  box-shadow: var(--card-shadow-hover);
  border-color: color-mix(in srgb, var(--brand) 25%, transparent);
}

/* ======== Quick Leads widget ======== */
.h2-ql-view-all {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 600;
  color: #01696f;
  text-decoration: none;
  transition: opacity 150ms ease;
}
.h2-ql-view-all:hover { opacity: 0.7; }

.h2-ql-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 24px 12px;
  text-align: center;
}
.h2-ql-add-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 500;
  color: #01696f;
  text-decoration: none;
}
.h2-ql-add-link:hover { text-decoration: underline; }

.h2-ql-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 14px;
  width: 100%;
  align-self: stretch;
  transition: background 100ms ease;
}
.h2-ql-row:hover { background: rgba(0,0,0,0.015); }
.h2-ql-row-border {
  border-bottom: 1px solid rgba(0,0,0,0.05);
}
.h2-ql-sep {
  width: 3px; height: 3px; border-radius: 50%;
  background: rgba(0,0,0,0.15); flex-shrink: 0;
}
.h2-ql-source {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 11px; font-weight: 500;
  color: #01696f;
}

.h2-ql-avatar {
  width: 26px; height: 26px;
  border-radius: 7px;
  background: rgba(194,123,26,0.08);
  color: #c27b1a;
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.h2-ql-quote-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px; height: 24px;
  border-radius: 6px;
  background: rgba(1,105,111,0.06);
  color: #01696f;
  flex-shrink: 0;
  transition: all 150ms ease;
  margin-left: auto;
}
.h2-ql-quote-btn:hover { background: rgba(1,105,111,0.14); }

.h2-ql-pri-urgent {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(193,56,56,0.08); color: #c13838; white-space: nowrap;
}
.h2-ql-pri-high {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(194,123,26,0.08); color: #c27b1a; white-space: nowrap;
}
.h2-ql-pri-normal {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(0,0,0,0.04); color: #8a8a86; white-space: nowrap;
}

/* ======== Sales Leads widget ======== */
.h2-sl-filter-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 10px; border-radius: 9999px;
  font-size: 11px; font-weight: 500; white-space: nowrap;
  border: 1px solid rgba(0,0,0,0.08); background: transparent;
  color: var(--h2-muted); cursor: pointer;
  transition: all 120ms ease;
}
.h2-sl-filter-pill:hover { background: rgba(0,0,0,0.03); }
.h2-sl-filter-pill--active {
  background: rgba(1,105,111,0.08); color: #01696f;
  border-color: rgba(1,105,111,0.2); font-weight: 600;
}
.h2-sl-filter-count {
  font-size: 9px; font-weight: 700; min-width: 14px; text-align: center;
  padding: 0 3px; border-radius: 9999px;
  background: rgba(0,0,0,0.05); color: var(--h2-muted);
}
.h2-sl-filter-pill--active .h2-sl-filter-count {
  background: rgba(1,105,111,0.12); color: #01696f;
}

.h2-sl-header {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 14px; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--h2-muted); border-bottom: 1px solid rgba(0,0,0,0.06);
  background: rgba(0,0,0,0.015);
}

.h2-sl-row {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 14px; transition: background 100ms ease;
}
.h2-sl-row:hover { background: rgba(0,0,0,0.015); }

.h2-sl-empty {
  display: flex; flex-direction: column; align-items: center;
  gap: 4px; padding: 20px 12px; text-align: center;
  font-size: 12px; color: var(--h2-muted);
}

.h2-sl-source-chip {
  display: inline-flex; align-items: center; gap: 3px;
  font-size: 10px; font-weight: 500; color: #01696f;
  padding: 1px 6px; border-radius: 9999px;
  background: rgba(1,105,111,0.06);
}

.h2-sl-status-new {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(59,130,246,0.08); color: #3b82f6; white-space: nowrap;
}
.h2-sl-status-contacted {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(1,105,111,0.08); color: #01696f; white-space: nowrap;
}
.h2-sl-status-qualified {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(194,123,26,0.08); color: #c27b1a; white-space: nowrap;
}
.h2-sl-status-proposal {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(139,92,246,0.08); color: #8b5cf6; white-space: nowrap;
}
.h2-sl-status-won {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(15,123,95,0.08); color: #0f7b5f; white-space: nowrap;
}
.h2-sl-status-lost {
  font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 9999px;
  background: rgba(0,0,0,0.04); color: #8a8a86; white-space: nowrap;
}

/* ── Client favorites widget ── */
.h2-fav-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
}
.h2-fav-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 14px;
  border-radius: 10px;
  border: 1px solid var(--h2-border);
  background: var(--h2-surface);
  text-decoration: none;
  color: inherit;
  transition: all 150ms ease;
  cursor: pointer;
}
.h2-fav-card:hover {
  border-color: rgba(1,105,111,0.2);
  box-shadow: 0 2px 8px rgba(0,0,0,0.04);
  transform: translateY(-1px);
}
.h2-fav-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.h2-fav-avatar {
  width: 32px; height: 32px;
  border-radius: 8px;
  background: rgba(1,105,111,0.08);
  color: #01696f;
  font-size: 11px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.h2-fav-star {
  width: 22px; height: 22px;
  border-radius: 4px;
  border: none;
  background: transparent;
  color: #f59e0b;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  opacity: 0;
  transition: all 150ms ease;
}
.h2-fav-card:hover .h2-fav-star { opacity: 1; }
.h2-fav-star:hover { background: rgba(245,158,11,0.1); }
.h2-fav-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--h2-fg);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.h2-fav-tier {
  display: inline-block;
  font-size: 9px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 9999px;
  white-space: nowrap;
  width: fit-content;
}
.h2-fav-tier-customer { background: rgba(1,105,111,0.08); color: #01696f; }
.h2-fav-tier-lead { background: rgba(147,51,234,0.08); color: #9333ea; }
.h2-fav-tier-ql { background: rgba(194,123,26,0.08); color: #c27b1a; }
.h2-fav-tier-cancelled { background: rgba(0,0,0,0.04); color: #8a8a86; }
.h2-fav-stats {
  display: flex;
  gap: 6px;
  font-size: 11px;
  color: var(--h2-fg);
  font-weight: 500;
}
.h2-fav-meta {
  display: flex;
  gap: 6px;
  font-size: 10px;
  color: var(--h2-muted);
}
</style>
