import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import {
  FaArrowLeft,
  FaChartBar,
  FaSpinner,
  FaSync,
  FaExclamationTriangle,
  FaFolderOpen,
  FaSearch,
  FaChevronDown,
  FaList,
  FaBolt,
  FaPrint,
  FaFileExport,
} from 'react-icons/fa';
import {
  ResponsiveContainer,
  BarChart,
  Bar,
  XAxis,
  YAxis,
  CartesianGrid,
  Tooltip,
  Legend,
  LabelList,
  PieChart,
  Pie,
  Cell,
} from 'recharts';
import projectService, {
  type Project,
  type Task,
  type ProgressUpdateChildRow,
} from '../services/projectService';
import salesInvoiceService, { type SalesInvoice } from '../services/salesInvoiceService';
import salesOrderService from '../services/salesOrderService';
import { formatProjectNotesForDisplay } from '../utils/projectNotesDisplay';

// Dashboard-aligned palette (matches ModernDashboard vibe; avoids harsh red)
const PIE_COLORS = [
  '#2563eb',
  '#4f46e5',
  '#7c3aed',
  '#0ea5e9',
  '#14b8a6',
  '#10b981',
  '#f59e0b',
  '#64748b',
  '#ec4899',
  '#06b6d4',
  '#84cc16',
  '#f97316',
  '#a855f7',
  '#2dd4bf',
  '#eab308',
];
const TASK_CUSTOM_ACTION_OPTIONS = [
  'Open',
  'Raised',
  'Warning',
  'Penalty',
  'Change Requests',
  'Problems',
  'Letter',
] as const;
const REPORT_WINDOW_DAYS = 30;

/** Slightly wider than print min — PNG export only — gives snapshot columns room so text/charts do not collide. */
const REPORT_EXPORT_CAPTURE_WIDTH_PX = 1380;

/**
 * Project snapshot only — 4-column chart row + updates/notes + bottom tasks; print/export sizing helpers.
 * Injected on screen + included in print/export via REPORT_PRINT_LAYOUT_CSS.
 */
const REPORT_SNAPSHOT_LAYOUT_CSS = `
/* Snapshot charts: 4 columns (xl+) / print; 2 columns sm–xl; 1 column on narrow screens. */
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-charts-section {
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-charts-four {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) !important;
  gap: 10px !important;
  align-items: stretch !important;
  padding-top: 10px !important;
  padding-bottom: 12px !important;
  padding-left: 6px !important;
  padding-right: 6px !important;
  box-sizing: border-box !important;
}
@media (min-width: 640px) and (max-width: 1279px) {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-charts-four {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}
@media (min-width: 1280px), print {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-charts-four {
    grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  }
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-chart-col {
  min-width: 0 !important;
  min-height: 0 !important;
  max-width: 100% !important;
}
/* Clip horizontal bleed only; vertical overflow visible so bar heights are not clipped. */
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart-card {
  overflow-x: hidden !important;
  overflow-y: visible !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart-card .recharts-wrapper,
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart-card .recharts-surface {
  max-width: 100% !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart.reports-chart-plot-shell,
#asm-project-reports-root .reports-project-snapshot .reports-chart-plot-shell.reports-snapshot-weekly-chart {
  width: 100% !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
  min-height: 10rem !important;
  height: 100% !important;
  flex: 1 1 auto !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart .recharts-responsive-container {
  width: 100% !important;
  max-width: 100% !important;
}
/* Updates | Notes: force two columns from md+ and in print (Tailwind lg: alone stacks in print preview). */
#asm-project-reports-root .reports-snapshot-updates-notes-row {
  display: grid !important;
  grid-template-columns: minmax(0, 1fr) !important;
  gap: 8px !important;
  align-items: stretch !important;
}
@media (min-width: 768px), print {
  #asm-project-reports-root .reports-snapshot-updates-notes-row {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  }
}
/* Bottom task tables: three columns in print like lg: */
@media print {
  #asm-project-reports-root .reports-snapshot-bottom-tasks {
    display: grid !important;
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
    gap: 6px !important;
    align-items: start !important;
  }
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart {
  min-height: 156px !important;
  height: 156px !important;
  flex-shrink: 0 !important;
  overflow: hidden !important;
  max-width: 100% !important;
  padding: 0 !important;
}
@media print {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart {
    min-height: 124px !important;
    height: 124px !important;
  }
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-progress-row {
  display: flex !important;
  flex-direction: column !important;
  align-items: stretch !important;
  gap: 8px !important;
}
@media (min-width: 768px), print {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-progress-row {
    flex-direction: row !important;
    flex-wrap: nowrap !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-progress-row > * {
    flex: 1 1 0 !important;
    min-width: 0 !important;
  }
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-table-scroll {
  max-height: 11rem !important;
  overflow-y: auto !important;
  overflow-x: auto !important;
}
@media print {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-table-scroll {
    max-height: 10rem !important;
    overflow-y: auto !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart.reports-chart-plot-shell {
    display: block !important;
    min-height: 11rem !important;
    height: 100% !important;
    max-height: none !important;
    flex: 1 1 auto !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart .recharts-responsive-container {
    width: 100% !important;
    height: 100% !important;
    min-height: 120px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart .recharts-responsive-container {
    width: 100% !important;
    height: 100% !important;
    min-height: 104px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-inner {
    min-height: 132px !important;
    height: 132px !important;
    min-width: 132px !important;
    width: 132px !important;
    margin-left: auto !important;
    margin-right: auto !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-inner .recharts-responsive-container {
    width: 100% !important;
    height: 100% !important;
    min-height: 132px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart-card {
    overflow-x: hidden !important;
    overflow-y: visible !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart {
    min-height: 128px !important;
    height: 128px !important;
    overflow: visible !important;
  }
  /* Donut card uses overflow-x-hidden on screen — some print engines clip the SVG. */
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-overall-card {
    overflow: visible !important;
  }
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-notes-body {
  max-height: 11rem !important;
  overflow-y: auto !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-legend {
  max-height: none !important;
  overflow: visible !important;
}
/* Project Overview row: keep start/end date pills aligned with top of meta column (print/PDF flex/grid stretch) */
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-overview-inner {
  align-items: start !important;
}
#asm-project-reports-root .reports-project-snapshot .reports-snapshot-overview-dates {
  align-self: start !important;
  justify-content: flex-start !important;
  align-content: flex-start !important;
}
/* md+ and print: right-align date rows in the third column (main axis is column → do not use justify-content:end) */
@media (min-width: 768px), print {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-overview-dates {
    align-items: flex-end !important;
  }
}
@media screen and (max-width: 767px) {
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-overview-dates {
    align-items: flex-start !important;
  }
}
/*
 * Print: viewport is often < 768px so Tailwind md:grid-cols-12 does not apply — dates fall below.
 * Force the same 5 + 4 + 3 column row as the dashboard.
 */
@media print {
  #asm-project-reports-root .reports-snapshot-overview-inner {
    display: grid !important;
    grid-template-columns: repeat(12, minmax(0, 1fr)) !important;
    gap: 6px !important;
    align-items: start !important;
  }
  #asm-project-reports-root .reports-snapshot-overview-meta {
    grid-column: 1 / span 5 !important;
    max-width: none !important;
  }
  #asm-project-reports-root .reports-snapshot-overview-timeline {
    grid-column: 6 / span 4 !important;
    max-width: none !important;
    border-left: 1px solid #e5e7eb !important;
    padding-left: 8px !important;
  }
  #asm-project-reports-root.dark .reports-snapshot-overview-timeline {
    border-left-color: #4b5563 !important;
  }
  #asm-project-reports-root .reports-snapshot-overview-dates {
    grid-column: 10 / span 3 !important;
    max-width: none !important;
  }
}
`;

/**
 * Shared by print @media and html2canvas clone — matches on-screen dashboard density to printed output.
 */
const REPORT_PRINT_LAYOUT_CSS = `
${REPORT_SNAPSHOT_LAYOUT_CSS}
#asm-project-reports-root .reports-project-snapshot {
  overflow: visible !important;
}
@media print {
  /* Let the snapshot span pages so flex/grid does not squash chart cells to 0 height. */
  #asm-project-reports-root .reports-project-snapshot {
    page-break-inside: auto !important;
    break-inside: auto !important;
  }
}
@media screen {
  #asm-project-reports-root .reports-project-snapshot {
    page-break-inside: avoid;
    break-inside: avoid;
  }
}
#asm-project-reports-root .dashboard-report-project-scope { display: none !important; }
#asm-project-reports-root .reports-selected-project-banner {
  display: block !important;
  page-break-after: avoid !important;
  break-after: avoid !important;
  page-break-inside: avoid !important;
  break-inside: avoid !important;
  margin-bottom: 12px !important;
  -webkit-print-color-adjust: exact !important;
  print-color-adjust: exact !important;
  background: #ecfdf5 !important;
  border: 1px solid #2dd4bf !important;
  box-shadow: none !important;
}
#asm-project-reports-root .reports-selected-project-banner p:nth-child(1) {
  color: #0f766e !important;
}
#asm-project-reports-root .reports-selected-project-banner p:nth-child(2) {
  color: #0f172a !important;
}
#asm-project-reports-root .reports-selected-project-banner p:nth-child(3) {
  color: #475569 !important;
}
#asm-project-reports-root .reports-chart-grid {
  display: grid !important;
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  gap: 10px !important;
  align-items: start !important;
}
#asm-project-reports-root .reports-chart-grid > * {
  min-width: 0 !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-snapshot-split {
  display: grid !important;
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  gap: 8px !important;
  align-items: start !important;
}
#asm-project-reports-root .reports-snapshot-split > * {
  min-width: 0 !important;
  max-width: 100% !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-chart-grid > * {
  page-break-inside: avoid !important;
  break-inside: avoid !important;
  overflow: hidden !important;
}
#asm-project-reports-root .reports-table-block {
  page-break-inside: avoid !important;
  break-inside: avoid !important;
  overflow: visible !important;
}
#asm-project-reports-root {
  padding: 0 !important;
  margin: 0 !important;
  min-height: 0 !important;
  background: #fff !important;
  color: #111827 !important;
  -webkit-print-color-adjust: exact !important;
  print-color-adjust: exact !important;
}
#asm-project-reports-root * { box-shadow: none !important; }
#asm-project-reports-root .min-h-screen { min-height: 0 !important; }
#asm-project-reports-root .mb-6 { margin-bottom: 10px !important; }
#asm-project-reports-root .mb-4 { margin-bottom: 8px !important; }
#asm-project-reports-root .mb-3 { margin-bottom: 6px !important; }
`;

/**
 * Print only — keep overflow:hidden on cards (from REPORT_PRINT_LAYOUT_CSS); shrink/wrap legends & ticks so they stay inside.
 * Do not set overflow:visible here (causes bleed into the next column).
 */
const REPORT_PRINT_CHART_COMPACT_CSS = `
@media print {
  #asm-project-reports-root .reports-project-snapshot .recharts-wrapper {
    overflow: visible !important;
  }
}
#asm-project-reports-root .reports-chart-grid .recharts-legend-wrapper,
#asm-project-reports-root .reports-project-snapshot .recharts-legend-wrapper {
  max-width: calc(100% - 8px) !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-chart-grid .recharts-legend-item-text,
#asm-project-reports-root .reports-project-snapshot .recharts-legend-item-text {
  fill: #111827 !important;
  color: #111827 !important;
  font-size: 8px !important;
}
#asm-project-reports-root .reports-chart-grid .recharts-default-legend,
#asm-project-reports-root .reports-project-snapshot .recharts-default-legend {
  display: flex !important;
  flex-wrap: wrap !important;
  justify-content: center !important;
  row-gap: 2px !important;
  column-gap: 8px !important;
  width: 100% !important;
  padding: 0 4px !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-chart-grid .recharts-default-legend li,
#asm-project-reports-root .reports-project-snapshot .recharts-default-legend li {
  margin-right: 0 !important;
  margin-left: 0 !important;
}
#asm-project-reports-root .reports-chart-grid .recharts-cartesian-axis-tick text,
#asm-project-reports-root .reports-project-snapshot .recharts-cartesian-axis-tick text {
  font-size: 8px !important;
}
`;

/**
 * When the user prints in landscape (dialog or system default), tighten spacing and scale the report root.
 * Page `size` is not set here so the browser print dialog can offer Portrait/Landscape freely.
 */
const REPORT_PRINT_LANDSCAPE_CSS = `
@media print and (orientation: landscape) {
  /*
   * Do not set @page { size: ... } here — it locks orientation in some browsers and hides the
   * print dialog’s layout control. Margins only; user picks Portrait/Landscape in the dialog.
   */
  @page {
    margin: 2mm;
  }
  /*
   * One landscape sheet: root scale ~80% + tighter vertical rhythm.
   * (Avoid forcing height on .recharts-surface — that breaks layout; scale on the root is OK here.)
   */
  #asm-project-reports-root.reports-print-root {
    transform: scale(0.8) !important;
    transform-origin: top left !important;
    width: calc(100% / 0.8) !important;
    max-width: none !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-body {
    padding: 4px 6px !important;
    gap: 4px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-overview-inner {
    padding: 4px 6px !important;
    gap: 4px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart.reports-chart-plot-shell {
    min-height: 8.5rem !important;
    height: 100% !important;
    flex: 1 1 auto !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart .recharts-responsive-container {
    min-height: 112px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart {
    min-height: 80px !important;
    height: 80px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart .recharts-responsive-container {
    min-height: 72px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-inner {
    min-height: 88px !important;
    height: 88px !important;
    min-width: 88px !important;
    width: 88px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-inner .recharts-responsive-container {
    min-height: 88px !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-donut-legend {
    max-height: 1.9rem !important;
    overflow: hidden !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-table-scroll {
    max-height: 3.75rem !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-notes-body,
  #asm-project-reports-root .reports-snapshot-updates-notes-row .reports-snapshot-notes-body {
    max-height: 3.5rem !important;
    padding: 3px 5px !important;
  }
  #asm-project-reports-root .reports-snapshot-updates-notes-row {
    gap: 2px !important;
  }
  #asm-project-reports-root .reports-snapshot-bottom-tasks {
    gap: 3px !important;
  }
  #asm-project-reports-root .reports-snapshot-bottom-tasks h3 {
    margin-bottom: 1px !important;
    line-height: 1 !important;
  }
  /* Keep each bottom table block together so headers are not orphaned on the previous page. */
  #asm-project-reports-root .reports-snapshot-bottom-tasks > div {
    break-inside: avoid !important;
    page-break-inside: avoid !important;
  }
}
`;

const PROJECT_REPORTS_PRINT_STYLE = `
@media print {
  @page {
    margin: 3mm;
  }
  html, body {
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    overflow: visible !important;
    background: #fff !important;
  }
  /*
   * App shell (see asm-app-sidebar / asm-app-main on Sidebar + main column): hide nav chrome
   * so only the report (#asm-project-reports-root) prints across the full page width.
   */
  .asm-app-sidebar-backdrop {
    display: none !important;
  }
  .asm-app-sidebar {
    display: none !important;
  }
  .asm-app-main {
    flex: none !important;
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
    overflow: visible !important;
  }
  .asm-app-main > header {
    display: none !important;
  }
  .asm-app-main > div.flex-1.overflow-y-auto {
    overflow: visible !important;
    width: 100% !important;
    max-width: 100% !important;
  }
  .flex.h-screen.overflow-hidden {
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }
  /* Print should be the report content only (no filter row). KPI strip removed from UI. */
  #asm-project-reports-root .dashboard-report-project-scope {
    display: none !important;
  }
  /* Do not use body * visibility:hidden — blank pages in Desk / some browsers. */
  /* Do not use CSS zoom on the root — breaks SVG/Recharts in print; 1:1 like dashboard (may span pages). */
  #asm-project-reports-root.reports-print-root {
    position: relative !important;
    left: auto !important;
    top: auto !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    box-sizing: border-box !important;
    background: #fff !important;
    color: #111827 !important;
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }
  #asm-project-reports-root .reports-print-chrome-only {
    display: none !important;
  }
  ${REPORT_PRINT_LAYOUT_CSS}
  ${REPORT_PRINT_CHART_COMPACT_CSS}
  /*
   * REPORT_PRINT_LAYOUT_CSS sets overflow:hidden on chart cards — fine on screen;
   * in print it can clip Recharts surfaces (blank plot area with legend still visible).
   */
  #asm-project-reports-root .reports-chart-grid > * {
    overflow: visible !important;
  }
  /* Compact “Report project” banner — saves vertical space for single-page print. */
  #asm-project-reports-root .reports-selected-project-banner {
    padding: 3px 8px !important;
    margin-bottom: 6px !important;
    border-radius: 6px !important;
  }
  #asm-project-reports-root .reports-selected-project-banner p:nth-child(1) {
    font-size: 7px !important;
    letter-spacing: 0.06em !important;
    margin: 0 !important;
    line-height: 1.05 !important;
  }
  #asm-project-reports-root .reports-selected-project-banner p:nth-child(2) {
    font-size: 13px !important;
    margin: 2px 0 0 !important;
    line-height: 1.15 !important;
  }
  #asm-project-reports-root .reports-selected-project-banner p:nth-child(3) {
    font-size: 8px !important;
    margin: 1px 0 0 !important;
    line-height: 1.1 !important;
  }
  /* Avoid a tall empty “loading” box in print when updates are still fetching. */
  #asm-project-reports-root .reports-snapshot-updates-loading {
    height: auto !important;
    min-height: 0 !important;
    padding: 4px 0 !important;
  }
}
${REPORT_PRINT_LANDSCAPE_CSS}
`;

/** html2canvas clone: print layout + optional wider root; PNG-only breathing room uses .reports-png-export-active on #asm-project-reports-root. */
function buildReportExportCloneCss(rootWidthPx: number): string {
  const w = rootWidthPx;
  return `
html { width: ${w}px !important; min-width: ${w}px !important; background: #fff !important; }
body { width: ${w}px !important; min-width: ${w}px !important; overflow: visible !important; margin: 0 !important; background: #fff !important; }
#asm-project-reports-root {
  min-width: ${w}px !important;
  width: ${w}px !important;
  max-width: ${w}px !important;
  overflow: visible !important;
  box-sizing: border-box !important;
}
#asm-project-reports-root .reports-export-strip-hidden { display: none !important; }
${REPORT_PRINT_LAYOUT_CSS}
${REPORT_PRINT_CHART_COMPACT_CSS}
#asm-project-reports-root.reports-png-export-active .reports-snapshot-split {
  grid-template-columns: minmax(0, 1.34fr) minmax(0, 0.66fr) !important;
  gap: 14px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-body {
  padding: 12px 14px !important;
  gap: 12px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-overview-inner {
  padding: 12px 14px !important;
  gap: 12px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-overview-meta .flex.flex-wrap {
  gap: 6px 14px !important;
  row-gap: 6px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-charts-four {
  display: grid !important;
  grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
  gap: 12px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-budget-metrics {
  padding: 4px 0 !important;
  line-height: 1.45 !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-invoice-chart {
  min-height: 140px !important;
  height: 140px !important;
  overflow: hidden !important;
  max-width: 100% !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-overall-card {
  padding: 11px 12px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-donut-inner {
  width: 132px !important;
  height: 132px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-donut-legend {
  margin-top: 8px !important;
  padding: 4px 4px 10px !important;
  gap: 6px 12px !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-donut-legend li {
  line-height: 1.45 !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-legend-label {
  max-width: 10rem !important;
  white-space: normal !important;
  overflow: visible !important;
  text-overflow: clip !important;
  line-height: 1.35 !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot .reports-snapshot-table-scroll {
  max-height: 10.5rem !important;
}
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot table th,
#asm-project-reports-root.reports-png-export-active .reports-project-snapshot table td {
  padding-left: 7px !important;
  padding-right: 7px !important;
}
`;
}

/** Banner visible only in print / export — must be @media screen so print layout can force display:block. */
const REPORT_SCREEN_ONLY_CSS = `
@media screen {
  #asm-project-reports-root .reports-selected-project-banner {
    display: none !important;
  }
}
`;

/** Snapshot tables / scroll regions: cap height + inner scroll — keeps print layout stable */
const REPORT_DASHBOARD_SCROLL_CSS = `
@media screen {
  #asm-project-reports-root .reports-dashboard-scroll-body {
    max-height: min(11rem, 32vh);
    overflow: auto;
    overscroll-behavior: contain;
  }
}
@media print {
  #asm-project-reports-root .reports-dashboard-scroll-body {
    max-height: 11rem !important;
    overflow: auto !important;
  }
}
`;

/** Screen only — print applies REPORT_PRINT_CHART_COMPACT_CSS (wrapped margins + smaller legend text) inside @media print. */
const REPORT_CHARTS_OVERFLOW_CONTAIN_CSS = `
@media screen {
  /* Clip card chrome only — do not set overflow on .recharts-* or charts can measure 0×0 and render blank */
  #asm-project-reports-root .reports-chart-grid > * {
    overflow: hidden !important;
  }
}
`;

/**
 * Wide landscape (screen): full-width chart grid like portrait; minmax(0) so Recharts measures correctly.
 * Snapshot invoice chart: block sizing helpers only — no narrowing of the snapshot row.
 * (Center gutter / edge alignment for chart rows is handled in landscape *print* only — REPORT_PRINT_LANDSCAPE_CSS.)
 */
const REPORT_SCREEN_LANDSCAPE_CHART_FILL_CSS = `
@media screen and (orientation: landscape) and (min-width: 1024px) {
  #asm-project-reports-root .reports-chart-grid {
    width: 100% !important;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important;
    justify-items: stretch !important;
  }
  #asm-project-reports-root .reports-chart-grid > * {
    min-width: 0 !important;
  }
  #asm-project-reports-root .reports-chart-plot-shell {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-invoice-chart {
    display: block !important;
    width: 100% !important;
    max-width: 100% !important;
    min-width: 0 !important;
    box-sizing: border-box !important;
  }
  #asm-project-reports-root .reports-chart-plot-shell > .recharts-responsive-container,
  #asm-project-reports-root .reports-snapshot-invoice-chart > .recharts-responsive-container {
    width: 100% !important;
    min-width: 0 !important;
  }
  #asm-project-reports-root .reports-project-snapshot .reports-snapshot-weekly-chart {
    min-height: 12rem !important;
    height: 100% !important;
    flex: 1 1 auto !important;
  }
}
`;

/**
 * Extra sharpness for PNG export (print uses CSS zoom 0.72 on-screen; bitmap uses native DPR up to cap).
 * If canvas exceeds browser limits, scale is reduced in handleExportDashboardPng.
 */
const REPORT_EXPORT_CANVAS_SCALE_FACTOR = 1;
const TASK_PAGE_SIZE = 500;
const PAGED_FETCH_MAX_PAGES = 200;

async function fetchAllTasksPaged(): Promise<Task[]> {
  const all: Task[] = [];
  let start = 0;
  for (let p = 0; p < PAGED_FETCH_MAX_PAGES; p++) {
    const res = await projectService.getTasks({
      limit_start: start,
      limit_page_length: TASK_PAGE_SIZE,
      order_by: 'modified desc',
    });
    const batch = res.data || [];
    all.push(...batch);
    if (batch.length < TASK_PAGE_SIZE) break;
    start += TASK_PAGE_SIZE;
  }
  return all;
}

function ymd(d: Date): string {
  return d.toISOString().slice(0, 10);
}

/** Calendar YYYY-MM-DD in the user's local timezone (use for comparing Frappe date fields). */
function ymdLocal(d: Date): string {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

function startOfDay(d: Date): Date {
  const x = new Date(d);
  x.setHours(0, 0, 0, 0);
  return x;
}

function countByName(items: { name: string; value: number }[]): number {
  return items.reduce((s, x) => s + x.value, 0);
}

function taskIsOpenForDashboard(t: Task): boolean {
  return t.status !== 'Completed' && t.status !== 'Cancelled';
}

function normCustomRiskIsYes(v: unknown): boolean {
  const s = String(v ?? '').trim().toLowerCase();
  return s === 'yes' || s === 'y' || s === '1' || s === 'true';
}

function canonicalCustomAction(raw: string): string {
  const t = raw.trim();
  if (!t) return '';
  const lower = t.toLowerCase();
  for (const opt of TASK_CUSTOM_ACTION_OPTIONS) {
    if (opt.toLowerCase() === lower) return opt;
  }
  return t;
}

/** Stacked risk chart: High / Medium / Low from task priority (Urgent counts as High). */
function priorityToRiskStackBucket(priority?: string | null): 'High' | 'Medium' | 'Low' {
  const p = (priority || '').trim().toLowerCase();
  if (!p) return 'Low';
  if (p === 'urgent' || p === 'high') return 'High';
  if (/\burgent\b/.test(p) || /\bhigh\b/.test(p)) return 'High';
  if (p === 'medium' || /\bmedium\b/.test(p)) return 'Medium';
  if (p === 'low' || /\blow\b/.test(p)) return 'Low';
  return 'Low';
}

function countBy<T>(items: T[], keyFn: (t: T) => string): { name: string; value: number }[] {
  const m = new Map<string, number>();
  for (const it of items) {
    const k = keyFn(it) || 'Unknown';
    m.set(k, (m.get(k) ?? 0) + 1);
  }
  return [...m.entries()].map(([name, value]) => ({ name, value })).sort((a, b) => b.value - a.value);
}

function daysBetween(start: Date, end: Date): number {
  const ms = startOfDay(end).getTime() - startOfDay(start).getTime();
  return Math.floor(ms / (24 * 60 * 60 * 1000));
}

function parseISODate(s?: string | null): Date | null {
  if (!s) return null;
  const d = new Date(s);
  if (Number.isNaN(d.getTime())) return null;
  return startOfDay(d);
}

/** Elapsed calendar days from start through today (capped at planned end); total planned span in days. */
function projectTimelineMetrics(startStr?: string | null, endStr?: string | null) {
  const start = parseISODate(startStr || undefined);
  const plannedEnd = parseISODate(endStr || undefined);
  if (!start || !plannedEnd) {
    return { durationDays: 0, elapsedDays: 0, pctElapsed: 0 };
  }
  const durationDays = Math.max(1, daysBetween(start, plannedEnd));
  const today = startOfDay(new Date());
  const effectiveEnd = plannedEnd < today ? plannedEnd : today;
  const elapsedDays = effectiveEnd < start ? 0 : daysBetween(start, effectiveEnd);
  const pctElapsed = Math.min(100, Math.round((elapsedDays / durationDays) * 100));
  return { durationDays, elapsedDays, pctElapsed };
}

function formatOverviewPillDate(iso?: string | null): string {
  const d = parseISODate(iso || undefined);
  if (!d) return '—';
  const dd = String(d.getDate()).padStart(2, '0');
  const mm = String(d.getMonth() + 1).padStart(2, '0');
  const yyyy = d.getFullYear();
  return `${dd}/${mm}/${yyyy}`;
}

function formatReportHeaderDate(d: Date): string {
  const parts = d
    .toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: '2-digit' })
    .replace(/,/g, '')
    .trim()
    .split(/\s+/);
  return parts.length >= 3 ? `${parts[0]}-${parts[1]}-${parts[2]}` : d.toLocaleDateString('en-GB');
}

const REPORT_LANDSCAPE_WIDE_MQ = '(orientation: landscape) and (min-width: 1024px)';

/** Wide landscape only — used to remeasure Recharts and slightly tighten margins (portrait/compact layouts unchanged). */
function useReportLandscapeWideScreen(): boolean {
  return useSyncExternalStore(
    onStoreChange => {
      if (typeof window === 'undefined') return () => {};
      const mq = window.matchMedia(REPORT_LANDSCAPE_WIDE_MQ);
      const run = () => onStoreChange();
      mq.addEventListener('change', run);
      window.addEventListener('resize', run);
      return () => {
        mq.removeEventListener('change', run);
        window.removeEventListener('resize', run);
      };
    },
    () => (typeof window !== 'undefined' ? window.matchMedia(REPORT_LANDSCAPE_WIDE_MQ).matches : false),
    () => false
  );
}

/**
 * True during print / print preview (any orientation). Drives Recharts debounce=0 and tighter snapshot margins
 * so charts measure a real box before the engine paints (fixes blank charts when preview was not “landscape”).
 */
function usePrintLandscapeChartMode(): boolean {
  const [active, setActive] = useState(false);
  useEffect(() => {
    const setFromMedia = () => {
      let printLike = false;
      try {
        printLike = window.matchMedia('print').matches;
      } catch {
        printLike = false;
      }
      setActive(printLike);
    };
    const onBeforePrint = () => {
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          setActive(true);
        });
      });
    };
    const onAfterPrint = () => setActive(false);
    window.addEventListener('beforeprint', onBeforePrint);
    window.addEventListener('afterprint', onAfterPrint);
    let mqPrint: MediaQueryList | null = null;
    try {
      mqPrint = window.matchMedia('print');
      mqPrint.addEventListener('change', setFromMedia);
    } catch {
      /* ignore */
    }
    return () => {
      window.removeEventListener('beforeprint', onBeforePrint);
      window.removeEventListener('afterprint', onAfterPrint);
      mqPrint?.removeEventListener('change', setFromMedia);
    };
  }, []);
  return active;
}

/**
 * Recharts 3 ResponsiveContainer with width+height both as "%" uses a 0×0 inner wrapper and relies on
 * overflow:visible. Snapshot cards use overflow-x:hidden to stop column bleed, which clips that overflow
 * and leaves “empty” charts with tiny slivers. Measuring the shell and passing numeric width+height avoids
 * the zero-size inner box (see recharts responsiveContainerUtils getInnerDivStyle).
 */
/** Numeric width+height for Recharts v3 (avoids %/% clipping); ref box should be flex-1 h-full to fill card body. */
function useObservedChartBoxPx(
  containerRef: React.RefObject<HTMLDivElement | null>,
  fallbackWidth: number,
  fallbackHeight: number
): { width: number; height: number } {
  const [box, setBox] = useState({ width: fallbackWidth, height: fallbackHeight });
  useLayoutEffect(() => {
    const el = containerRef.current;
    if (!el || typeof ResizeObserver === 'undefined') return;
    const apply = () => {
      const r = el.getBoundingClientRect();
      const w = Math.floor(r.width);
      const h = Math.floor(r.height);
      if (w <= 0) return;
      const nh = Math.max(96, h);
      setBox(prev => (prev.width === w && prev.height === nh ? prev : { width: w, height: nh }));
    };
    apply();
    const ro = new ResizeObserver(apply);
    ro.observe(el);
    return () => ro.disconnect();
  }, [containerRef]);
  return box;
}

const CustomTooltip = ({ active, payload, label }: any) => {
  if (!active || !payload?.length) return null;
  const row = payload[0]?.payload;
  const doc = row?.docname;
  const rowLabel = row?.label != null ? String(row.label) : '';
  return (
    <div className="rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 px-3 py-2 shadow-lg text-xs">
      <div className="font-semibold text-gray-800 dark:text-gray-100 mb-0.5">{doc || rowLabel || label}</div>
      {payload.map((p: any, i: number) => (
        <div key={i} className="text-gray-600 dark:text-gray-300">
          {p.name}: <span className="font-mono font-semibold">{p.value}</span>
        </div>
      ))}
    </div>
  );
};

/** Short Y-axis labels so large currency values do not stack vertically in narrow snapshot charts. */
function formatCompactAxisNumber(n: unknown): string {
  const v = typeof n === 'number' ? n : Number(n);
  if (!Number.isFinite(v)) return '';
  const x = Math.abs(v);
  if (x >= 1_000_000) return `${(v / 1_000_000).toFixed(x >= 10_000_000 ? 0 : 1)}M`;
  if (x >= 1000) return `${Math.round(v / 1000)}k`;
  return `${Math.round(v)}`;
}

const ProjectReportsDashboard: React.FC = () => {
  const { t } = useTranslation();
  const reportLandscapeWide = useReportLandscapeWideScreen();
  const printLandscapeCharts = usePrintLandscapeChartMode();
  const riskBarShellRef = useRef<HTMLDivElement>(null);
  const actionsBarShellRef = useRef<HTMLDivElement>(null);
  const snapshotBarPlotFallbackHeightPx = printLandscapeCharts ? 216 : reportLandscapeWide ? 296 : 272;
  const riskChartBox = useObservedChartBoxPx(riskBarShellRef, 220, snapshotBarPlotFallbackHeightPx);
  const actionsChartBox = useObservedChartBoxPx(actionsBarShellRef, 220, snapshotBarPlotFallbackHeightPx);
  const navigate = useNavigate();
  const [searchParams, setSearchParams] = useSearchParams();
  const urlProjectParam = searchParams.get('project') ?? '';

  const range = useMemo(() => {
    const end = new Date();
    const start = new Date();
    start.setDate(start.getDate() - REPORT_WINDOW_DAYS);
    return { from: ymd(start), to: ymd(end) };
  }, []);

  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState<string>('');
  const [portfolioTasks, setPortfolioTasks] = useState<Task[]>([]);
  const [projectRows, setProjectRows] = useState<Project[]>([]);

  const [focusProject, setFocusProject] = useState<string>('');
  const [focusTasks, setFocusTasks] = useState<Task[]>([]);
  const [focusLoading, setFocusLoading] = useState(false);
  const [focusInvoices, setFocusInvoices] = useState<SalesInvoice[]>([]);
  const [focusInvoiceLoading, setFocusInvoiceLoading] = useState(false);
  const [refreshNonce, setRefreshNonce] = useState(0);
  const [projectPickerOpen, setProjectPickerOpen] = useState(false);
  const [projectPickerSearch, setProjectPickerSearch] = useState('');
  const projectPickerRef = useRef<HTMLDivElement>(null);
  const dashboardExportRef = useRef<HTMLDivElement>(null);
  const [exportingSnapshot, setExportingSnapshot] = useState(false);
  const [focusProjectDetail, setFocusProjectDetail] = useState<Project | null>(null);
  const [dashboardProgressUpdates, setDashboardProgressUpdates] = useState<ProgressUpdateChildRow[]>([]);
  const [dashboardUpdatesLoading, setDashboardUpdatesLoading] = useState(false);

  const loadPortfolio = useCallback(async () => {
    setLoading(true);
    setLoadError('');
    try {
      const settled = await Promise.allSettled([
        projectService.getProjectCount({ status: 'Open' }),
        projectService.getTaskCount({ status: ['!=', 'Completed'] }),
        projectService.getProjects({
          filters: { status: 'Open' },
          limit_page_length: 2000,
          order_by: 'modified desc',
          fields: [
            'name',
            'project_name',
            'status',
            'total_billed_amount',
            'total_sales_amount',
            'estimated_costing',
            'percent_complete',
            'expected_start_date',
            'expected_end_date',
            'department',
            'customer',
            'company',
            'notes',
          ],
        }),
      ]);

      const val = <T,>(i: number, fallback: T): T => (settled[i].status === 'fulfilled' ? (settled[i] as any).value : fallback);

      const projRes = val<{ data: Project[] }>(2, { data: [] as Project[] });

      const taskRows = await fetchAllTasksPaged();
      setPortfolioTasks(taskRows || []);

      setProjectRows(projRes.data);
      setFocusProject(prev => {
        const params = new URLSearchParams(window.location.search);
        const fromUrl = params.get('project');
        if (fromUrl && projRes.data.some(p => p.name === fromUrl)) return fromUrl;
        if (prev && projRes.data.some(p => p.name === prev)) return prev;
        return projRes.data[0]?.name || '';
      });
      const rejected = settled
        .map((r, idx) => (r.status === 'rejected' ? idx : -1))
        .filter(idx => idx !== -1);
      if (rejected.length) setLoadError(`Some data failed to load (sources: ${rejected.join(', ')}).`);
    } catch (e: any) {
      const msg = typeof e?.message === 'string' ? e.message : 'Failed to load dashboard data.';
      setLoadError(msg);
      setProjectRows([]);
      setPortfolioTasks([]);
    } finally {
      setLoading(false);
    }
  }, [range.from, range.to]);

  useEffect(() => {
    loadPortfolio();
  }, [loadPortfolio]);

  useEffect(() => {
    if (!focusProject) {
      setDashboardProgressUpdates([]);
      return;
    }
    let cancelled = false;
    setDashboardUpdatesLoading(true);
    projectService
      .getMergedProjectProgressUpdates(focusProject)
      .then(rows => {
        if (!cancelled) setDashboardProgressUpdates(rows);
      })
      .catch(() => {
        if (!cancelled) setDashboardProgressUpdates([]);
      })
      .finally(() => {
        if (!cancelled) setDashboardUpdatesLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, [focusProject, refreshNonce]);

  const handleRefreshAll = useCallback(() => {
    setRefreshNonce(n => n + 1);
    loadPortfolio();
  }, [loadPortfolio]);

  const handlePrintDashboard = useCallback(() => {
    window.dispatchEvent(new Event('resize'));
    requestAnimationFrame(() => {
      window.print();
    });
  }, []);

  const handleExportDashboardPng = useCallback(async () => {
    const el = dashboardExportRef.current;
    if (!el) return;
    setExportingSnapshot(true);
    const prevScrollY = window.scrollY;
    const prevRootWidth = el.style.width;
    const prevRootMaxW = el.style.maxWidth;
    const prevRootBox = el.style.boxSizing;
    window.scrollTo(0, 0);
    await new Promise<void>(resolve => {
      requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
    });
    await new Promise<void>(resolve => setTimeout(resolve, 450));
    try {
      el.classList.add('reports-png-export-active');
      /** Wider than print min-width — snapshot gets more horizontal room in PNG only. */
      el.style.setProperty('width', `${REPORT_EXPORT_CAPTURE_WIDTH_PX}px`, 'important');
      el.style.setProperty('max-width', `${REPORT_EXPORT_CAPTURE_WIDTH_PX}px`, 'important');
      el.style.setProperty('box-sizing', 'border-box', 'important');
      window.dispatchEvent(new Event('resize'));
      await new Promise<void>(resolve => {
        requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
      });
      await new Promise<void>(resolve => setTimeout(resolve, 200));

      const html2canvas = (await import('html2canvas')).default;
      const onClone = (clonedDoc: Document) => {
        const st = clonedDoc.createElement('style');
        st.textContent = buildReportExportCloneCss(REPORT_EXPORT_CAPTURE_WIDTH_PX);
        clonedDoc.head.appendChild(st);
        const root = clonedDoc.getElementById('asm-project-reports-root');
        if (root) {
          const rh = root as HTMLElement;
          rh.style.setProperty('overflow', 'visible', 'important');
          rh.style.setProperty('max-height', 'none', 'important');
        }
        clonedDoc.documentElement.style.width = `${REPORT_EXPORT_CAPTURE_WIDTH_PX}px`;
        clonedDoc.body.style.width = `${REPORT_EXPORT_CAPTURE_WIDTH_PX}px`;
        clonedDoc.querySelectorAll('.print-hidden').forEach((node) => {
          node.parentNode?.removeChild(node);
        });
        clonedDoc.querySelectorAll('.reports-selected-project-banner').forEach(node => {
          (node as HTMLElement).style.setProperty('display', 'block', 'important');
        });
        clonedDoc.querySelectorAll('#asm-project-reports-root *').forEach((n) => {
          const h = n as HTMLElement;
          if (h.closest('.reports-project-snapshot')) return;
          const win = clonedDoc.defaultView;
          if (!win) return;
          try {
            const oy = win.getComputedStyle(h).overflowY;
            if (oy === 'auto' || oy === 'scroll') {
              h.style.setProperty('overflow', 'visible', 'important');
              h.style.setProperty('max-height', 'none', 'important');
            }
          } catch {
            /* ignore */
          }
        });
      };
      const w = REPORT_EXPORT_CAPTURE_WIDTH_PX;
      const h = el.scrollHeight;
      /** Browser canvas limits (~16k); scale down so the full dashboard fits one image. */
      const MAX_EDGE = 15800;
      let scale =
        Math.min(2.25, Math.max(1, window.devicePixelRatio || 1)) * REPORT_EXPORT_CANVAS_SCALE_FACTOR;
      while (h * scale > MAX_EDGE || w * scale > MAX_EDGE) scale *= 0.88;
      scale = Math.max(0.22, scale);
      const canvas = await html2canvas(el, {
        scale,
        width: w,
        height: h,
        windowWidth: REPORT_EXPORT_CAPTURE_WIDTH_PX,
        windowHeight: h,
        scrollX: 0,
        scrollY: 0,
        x: 0,
        y: 0,
        useCORS: true,
        allowTaint: true,
        foreignObjectRendering: false,
        logging: false,
        backgroundColor: '#ffffff',
        imageTimeout: 20000,
        onclone: onClone,
      });
      const blob: Blob | null = await new Promise(resolve => {
        canvas.toBlob(b => resolve(b), 'image/png', 0.92);
      });
      if (!blob) throw new Error('no blob');
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      const slug = focusProject ? focusProject.replace(/[^a-zA-Z0-9-_]+/g, '_').slice(0, 80) : 'all-projects';
      a.href = url;
      a.download = `project-reports-${slug}-${new Date().toISOString().slice(0, 10)}.png`;
      a.rel = 'noopener';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
    } catch {
      window.alert('Could not export an image of this page. Use Print and choose “Save as PDF” instead.');
    } finally {
      el.classList.remove('reports-png-export-active');
      el.style.width = prevRootWidth;
      el.style.maxWidth = prevRootMaxW;
      el.style.boxSizing = prevRootBox;
      window.dispatchEvent(new Event('resize'));
      window.scrollTo(0, prevScrollY);
      setExportingSnapshot(false);
    }
  }, [focusProject]);

  const applyFocusProject = useCallback(
    (name: string) => {
      setFocusProject(name);
      setSearchParams(
        prev => {
          const n = new URLSearchParams(prev);
          if (name) n.set('project', name);
          else n.delete('project');
          return n;
        },
        { replace: true },
      );
    },
    [setSearchParams],
  );

  useEffect(() => {
    if (!projectRows.length) return;
    if (urlProjectParam && projectRows.some(p => p.name === urlProjectParam)) {
      setFocusProject(curr => (curr === urlProjectParam ? curr : urlProjectParam));
    }
  }, [urlProjectParam, projectRows]);

  useEffect(() => {
    if (!projectPickerOpen) return;
    const onDocDown = (e: MouseEvent) => {
      const el = projectPickerRef.current;
      if (el && !el.contains(e.target as Node)) {
        setProjectPickerOpen(false);
        setProjectPickerSearch('');
      }
    };
    document.addEventListener('mousedown', onDocDown);
    return () => document.removeEventListener('mousedown', onDocDown);
  }, [projectPickerOpen]);

  useEffect(() => {
    if (!focusProject) {
      setFocusProjectDetail(null);
      return;
    }
    let cancel = false;
    projectService
      .getProject(focusProject)
      .then(p => {
        if (!cancel) setFocusProjectDetail(p);
      })
      .catch(() => {
        if (!cancel) setFocusProjectDetail(null);
      });
    return () => {
      cancel = true;
    };
  }, [focusProject, refreshNonce]);

  const filteredPickerProjects = useMemo(() => {
    const q = projectPickerSearch.trim().toLowerCase();
    if (!q) return projectRows;
    return projectRows.filter(p => {
      const title = (p.project_name || '').toLowerCase();
      const id = (p.name || '').toLowerCase();
      return title.includes(q) || id.includes(q);
    });
  }, [projectRows, projectPickerSearch]);

  const tasksForReports = useMemo(() => {
    if (!focusProject) return portfolioTasks;
    return portfolioTasks.filter(t => String(t.project || '') === focusProject);
  }, [portfolioTasks, focusProject]);

  useEffect(() => {
    if (!focusProject) {
      setFocusTasks([]);
      return;
    }
    let cancel = false;
    setFocusLoading(true);
    projectService
      .getTasksForProject(focusProject, { limit_page_length: 500 })
      .then(res => {
        if (!cancel) setFocusTasks(res.data);
      })
      .catch(() => {
        if (!cancel) setFocusTasks([]);
      })
      .finally(() => {
        if (!cancel) setFocusLoading(false);
      });
    return () => {
      cancel = true;
    };
  }, [focusProject, refreshNonce]);

  // Load sales invoices for focused project (for budget graph)
  useEffect(() => {
    if (!focusProject) {
      setFocusInvoices([]);
      return;
    }
    let cancel = false;
    setFocusInvoiceLoading(true);
    const invFields = ['name', 'posting_date', 'grand_total', 'currency', 'docstatus', 'status'] as const;
    Promise.all([
      salesOrderService.getSalesOrderNamesForProject(focusProject, 1000).then((salesOrders) =>
        salesOrders.length
          ? salesInvoiceService.getSalesInvoicesBySalesOrders({
              salesOrders,
              limit: 1000,
              orderBy: 'posting_date asc',
              fields: [...invFields],
            })
          : [],
      ),
      salesInvoiceService.getSalesInvoicesLinkedToProject({
        project: focusProject,
        limit: 1000,
        orderBy: 'posting_date asc',
        fields: [...invFields],
      }),
    ])
      .then(([fromSalesOrders, fromProject]) => {
        if (cancel) return;
        // Merge SO-linked + project-linked (many sites set Project on SI / items but not SO on every line)
        const byName = new Map<string, SalesInvoice>();
        for (const inv of fromSalesOrders) if (inv?.name) byName.set(inv.name, inv);
        for (const inv of fromProject) if (inv?.name) byName.set(inv.name, inv);
        const unique = [...byName.values()].sort((a, b) =>
          String(a.posting_date || '').localeCompare(String(b.posting_date || '')),
        );
        setFocusInvoices(unique);
      })
      .catch(() => {
        if (!cancel) setFocusInvoices([]);
      })
      .finally(() => {
        if (!cancel) setFocusInvoiceLoading(false);
      });
    return () => { cancel = true; };
  }, [focusProject, refreshNonce]);

  const focusProjectDoc = useMemo(
    () => projectRows.find(p => p.name === focusProject),
    [projectRows, focusProject],
  );

  const overviewProject = useMemo((): Project | null => {
    if (!focusProject) return null;
    const base = focusProjectDoc;
    const detail = focusProjectDetail;
    if (!base && !detail) return null;
    return { ...(base || {}), ...(detail || {}) } as Project;
  }, [focusProject, focusProjectDoc, focusProjectDetail]);

  const overviewTimeline = useMemo(
    () =>
      projectTimelineMetrics(
        overviewProject?.expected_start_date,
        overviewProject?.expected_end_date,
      ),
    [overviewProject?.expected_start_date, overviewProject?.expected_end_date],
  );

  const reportAsOfLabel = useMemo(() => formatReportHeaderDate(new Date()), [refreshNonce]);

  const budgetTotal = useMemo(() => {
    const p = focusProjectDoc;
    if (!p) return 0;
    // Per request: Budget uses Estimated Cost (fallback total sales)
    const raw = Number(p.estimated_costing ?? p.total_sales_amount ?? 0);
    return Number.isFinite(raw) ? raw : 0;
  }, [focusProjectDoc]);

  const budgetSpent = useMemo(() => {
    const p = focusProjectDoc;
    if (!p) return 0;
    // Per request: Spent uses Total Billed Amount (Sales Invoice)
    const raw = Number(p.total_billed_amount ?? 0);
    return Number.isFinite(raw) ? raw : 0;
  }, [focusProjectDoc]);

  const budgetRemaining = Math.max(0, budgetTotal - budgetSpent);
  const budgetPct = budgetTotal > 0 ? Math.min(100, Math.round((budgetSpent / budgetTotal) * 100)) : 0;

  const invoiceBudgetRows = useMemo(() => {
    if (!budgetTotal) return [];
    const valid = (focusInvoices || []).filter((inv) => (inv.docstatus ?? 0) !== 2);
    // Show up to 12 for readability; still calculates all totals above.
    return valid.slice(0, 12).map((inv, idx) => {
      const spent = Math.max(0, Number(inv.grand_total ?? 0) || 0);
      const remaining = Math.max(0, budgetTotal - spent);
      return {
        name: `INVOICE ${idx + 1}`,
        key: inv.name,
        docname: inv.name,
        spent,
        remaining,
      };
    });
  }, [focusInvoices, budgetTotal]);

  const todayStart = startOfDay(new Date());
  const weekStart = new Date(todayStart);
  weekStart.setDate(weekStart.getDate() - 7);
  const todayStr = ymdLocal(todayStart);
  const weekAgoStr = ymdLocal(weekStart);

  const focusDonut = useMemo(() => countBy(focusTasks, t => t.status || 'Unknown'), [focusTasks]);
  const focusDonutTotal = useMemo(() => countByName(focusDonut), [focusDonut]);

  /** Due date fell in the last 7 local calendar days and is before today (not still “upcoming” today). */
  const lastWeekRows = useMemo(() => {
    return focusTasks.filter(t => {
      if (t.status === 'Completed' || t.status === 'Cancelled') return false;
      const end = t.exp_end_date ? String(t.exp_end_date).slice(0, 10) : '';
      if (!end) return false;
      return end >= weekAgoStr && end < todayStr;
    }).slice(0, 12);
  }, [focusTasks, weekAgoStr, todayStr]);

  const upcomingRows = useMemo(() => {
    return focusTasks
      .filter(t => {
        if (t.status === 'Completed' || t.status === 'Cancelled') return false;
        const end = t.exp_end_date ? String(t.exp_end_date).slice(0, 10) : '';
        return end && end >= todayStr;
      })
      .sort((a, b) => (a.exp_end_date || '').localeCompare(b.exp_end_date || ''))
      .slice(0, 12);
  }, [focusTasks, todayStr]);

  /** One row per priority bucket — thin adjacent bars (not stacked) to match snapshot reference. */
  const riskIssuesBarData = useMemo(() => {
    const pool = tasksForReports
      .filter(taskIsOpenForDashboard)
      .filter(t => normCustomRiskIsYes(t.custom_risk));
    let high = 0;
    let medium = 0;
    let low = 0;
    for (const t of pool) {
      const b = priorityToRiskStackBucket(t.priority);
      if (b === 'High') high += 1;
      else if (b === 'Medium') medium += 1;
      else low += 1;
    }
    return [
      { name: 'High', count: high, fill: '#ef4444' },
      { name: 'Medium', count: medium, fill: '#14b8a6' },
      { name: 'Low', count: low, fill: '#22c55e' },
    ];
  }, [tasksForReports]);

  const actionsBarData = useMemo(() => {
    const pool = tasksForReports.filter(taskIsOpenForDashboard);
    const counts = new Map<string, number>();
    for (const o of TASK_CUSTOM_ACTION_OPTIONS) counts.set(o, 0);
    for (const t of pool) {
      const c = canonicalCustomAction(String(t.custom_action || ''));
      if (!c || !counts.has(c)) continue;
      counts.set(c, (counts.get(c) ?? 0) + 1);
    }
    return TASK_CUSTOM_ACTION_OPTIONS.map(label => ({
      name: label.length > 11 ? `${label.slice(0, 8)}…` : label,
      fullLabel: label,
      KPI: counts.get(label) ?? 0,
    }));
  }, [tasksForReports]);

  const riskPriorityMaxCount = useMemo(
    () => riskIssuesBarData.reduce((m, r) => Math.max(m, Number(r.count) || 0), 0),
    [riskIssuesBarData]
  );

  /** At least 0–5 on the axis for headroom (per product request); grow if any bucket exceeds 5. */
  const riskYAxisMax = useMemo(() => Math.max(5, riskPriorityMaxCount), [riskPriorityMaxCount]);

  const actionsKpiMax = useMemo(
    () => actionsBarData.reduce((m, row) => Math.max(m, Number(row.KPI) || 0), 0),
    [actionsBarData]
  );
  const actionsYAxisMax = useMemo(() => Math.max(5, actionsKpiMax), [actionsKpiMax]);

  const obstacleTableRows = useMemo(() => {
    return focusTasks
      .filter(t => String(t.custom_task_obstacle || '').trim())
      .sort((a, b) => String(a.exp_end_date || '').localeCompare(String(b.exp_end_date || '')))
      .slice(0, 40);
  }, [focusTasks]);

  const assignLabel = (t: Task) => {
    try {
      if (t._assign) {
        const a = JSON.parse(t._assign as string);
        if (Array.isArray(a) && a[0]) return String(a[0]);
      }
    } catch { /* ignore */ }
    return t.owner || '—';
  };

  /** Inner plot only: grows with card body (numeric size from ResizeObserver). */
  const snapshotRiskActionsPlotShellClass =
    'reports-chart-plot-shell reports-snapshot-weekly-chart w-full min-w-0 flex-1 h-full min-h-[10rem]';
  /** Print snapshot: animated bars/pies often paint empty in print engines. */
  const snapshotChartIsAnimationActive = printLandscapeCharts ? false : ('auto' as const);

  return (
    <>
      <style dangerouslySetInnerHTML={{ __html: PROJECT_REPORTS_PRINT_STYLE }} />
      <style dangerouslySetInnerHTML={{ __html: REPORT_SNAPSHOT_LAYOUT_CSS }} />
      <style dangerouslySetInnerHTML={{ __html: REPORT_CHARTS_OVERFLOW_CONTAIN_CSS }} />
      <style dangerouslySetInnerHTML={{ __html: REPORT_SCREEN_LANDSCAPE_CHART_FILL_CSS }} />
      <style dangerouslySetInnerHTML={{ __html: REPORT_SCREEN_ONLY_CSS }} />
      <style dangerouslySetInnerHTML={{ __html: REPORT_DASHBOARD_SCROLL_CSS }} />
      <div
        ref={dashboardExportRef}
        id="asm-project-reports-root"
        className="reports-print-root p-6 min-h-screen bg-gray-50 dark:bg-gray-900"
      >
      <div className="reports-print-chrome-only flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6">
        <div className="flex items-start gap-3">
          <button
            type="button"
            onClick={() => navigate('/projects')}
            className="mt-1 p-2 rounded-lg text-gray-500 hover:text-gray-800 dark:hover:text-gray-200 border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800"
          >
            <FaArrowLeft size={14} />
          </button>
          <div className="flex items-center gap-3">
            <div className="w-11 h-11 rounded-xl bg-gradient-to-br from-teal-500 to-cyan-600 flex items-center justify-center shadow-md">
              <FaChartBar className="text-white text-lg" />
            </div>
            <div>
              <h1 className="text-xl font-bold text-gray-900 dark:text-white">
                {t('projects.reportsDashboardTitle', 'Project reports')}
              </h1>
            </div>
          </div>
        </div>
        <div className="reports-export-strip-hidden print:hidden flex flex-wrap items-center justify-end gap-2">
          <button
            type="button"
            onClick={handlePrintDashboard}
            className="inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold text-white bg-violet-600 hover:bg-violet-700 shadow-sm border border-violet-700/30"
          >
            <FaPrint size={13} />
            Print
          </button>
          <button
            type="button"
            onClick={handleExportDashboardPng}
            disabled={exportingSnapshot || loading}
            className="!hidden inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-semibold text-white bg-emerald-600 hover:bg-emerald-700 shadow-sm border border-emerald-700/30 disabled:opacity-50 disabled:pointer-events-none"
          >
            {exportingSnapshot ? <FaSpinner className="animate-spin" size={13} /> : <FaFileExport size={13} />}
            Export
          </button>
          <button
            type="button"
            onClick={handleRefreshAll}
            disabled={loading}
            title={t('common.refresh', 'Refresh')}
            aria-label={t('common.refresh', 'Refresh')}
            className="inline-flex items-center justify-center p-2.5 rounded-lg bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 text-gray-700 dark:text-gray-200 hover:border-teal-400 hover:text-teal-700 shadow-sm disabled:opacity-50"
          >
            <FaSync size={14} className={loading ? 'animate-spin' : ''} />
          </button>
        </div>
      </div>

      {loadError && (
        <div className="mb-6 rounded-xl border border-rose-200 dark:border-rose-800 bg-rose-50/80 dark:bg-rose-950/30 px-4 py-3 text-sm text-rose-900 dark:text-rose-100">
          <div className="flex items-center justify-between gap-3">
            <div className="min-w-0">
              <p className="font-semibold">Reports couldn’t load completely</p>
              <p className="text-xs opacity-80 break-words">{loadError}</p>
            </div>
            <button
              type="button"
              onClick={handleRefreshAll}
              className="shrink-0 inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-white/90 dark:bg-gray-800/70 border border-rose-200 dark:border-rose-700 text-xs font-semibold hover:shadow"
            >
              <FaSync size={11} className={loading ? 'animate-spin' : ''} />
              Retry
            </button>
          </div>
        </div>
      )}

      {/* Global project scope — drives focused widgets, URL ?project=, and filtered sections */}
      <div className="dashboard-report-project-scope mb-6 rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 shadow-md px-4 py-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
        <div className="min-w-0">
          <p className="text-xs font-bold uppercase tracking-wide text-gray-500 dark:text-gray-400">
            {t('projects.reportProjectFilter', 'Report project')}
          </p>
          <p className="text-[11px] text-gray-500 dark:text-gray-400 mt-0.5">
            {t(
              'projects.reportProjectFilterHint',
              'Selection applies to charts, KPIs, and tasks. Project snapshot and project updates appear only when a project is selected.',
            )}
          </p>
        </div>
        <div ref={projectPickerRef} className="relative w-full sm:w-[min(28rem,100%)] shrink-0">
          <label htmlFor="report-project-search-global" className="text-[11px] font-medium text-gray-600 dark:text-gray-300 sr-only">
            {t('projects.reportProjectFilter', 'Report project')}
          </label>
          <button
            type="button"
            onClick={() => {
              setProjectPickerOpen(o => !o);
              if (!projectPickerOpen) setProjectPickerSearch('');
            }}
            className="w-full flex items-center justify-between gap-2 text-left text-sm rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-900/50 text-gray-900 dark:text-white px-3 py-2.5 hover:border-teal-400 focus:outline-none focus:ring-2 focus:ring-teal-400/40"
          >
            <span className="truncate">
              {focusProject
                ? (projectRows.find(p => p.name === focusProject)?.project_name || focusProject)
                : t('projects.selectProjectPlaceholder', 'Select project…')}
            </span>
            <FaChevronDown size={12} className={`shrink-0 opacity-70 transition-transform ${projectPickerOpen ? 'rotate-180' : ''}`} />
          </button>
          {projectPickerOpen && (
            <div className="absolute right-0 z-[70] mt-1 w-full min-w-[16rem] max-h-[min(24rem,70vh)] flex flex-col rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 shadow-xl overflow-hidden">
              <div className="flex items-center gap-2 px-2 py-2 border-b border-gray-100 dark:border-gray-700 bg-gray-50 dark:bg-gray-900/40">
                <FaSearch className="text-gray-400 shrink-0 text-xs" />
                <input
                  id="report-project-search-global"
                  type="search"
                  value={projectPickerSearch}
                  onChange={e => setProjectPickerSearch(e.target.value)}
                  placeholder={t('projects.searchProjectPlaceholder', 'Search by name or ID…')}
                  autoComplete="off"
                  className="flex-1 min-w-0 bg-transparent text-sm text-gray-900 dark:text-white placeholder:text-gray-400 focus:outline-none"
                />
              </div>
              <ul className="overflow-y-auto max-h-[min(20rem,60vh)] py-1 text-sm">
                <li>
                  <button
                    type="button"
                    className="w-full text-left px-3 py-2 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700"
                    onClick={() => {
                      applyFocusProject('');
                      setProjectPickerOpen(false);
                      setProjectPickerSearch('');
                    }}
                  >
                    {t('projects.clearProjectSelection', 'Clear selection')}
                  </button>
                </li>
                {filteredPickerProjects.length === 0 ? (
                  <li className="px-3 py-4 text-center text-gray-400 text-xs">
                    {t('projects.noMatchingOpenProjects', 'No matching open projects.')}
                  </li>
                ) : (
                  filteredPickerProjects.map(p => (
                    <li key={p.name}>
                      <button
                        type="button"
                        className={`w-full text-left px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700 ${
                          p.name === focusProject ? 'bg-teal-50 dark:bg-teal-900/30 text-teal-800 dark:text-teal-200 font-medium' : 'text-gray-800 dark:text-gray-200'
                        }`}
                        onClick={() => {
                          applyFocusProject(p.name);
                          setProjectPickerOpen(false);
                          setProjectPickerSearch('');
                        }}
                      >
                        <span className="block truncate">{p.project_name || p.name}</span>
                        <span className="block text-[10px] text-gray-400 font-mono truncate">{p.name}</span>
                      </button>
                    </li>
                  ))
                )}
              </ul>
              {projectRows.length >= 2000 && (
                <p className="px-3 py-1.5 text-[10px] text-gray-500 border-t border-gray-100 dark:border-gray-700 bg-gray-50/80 dark:bg-gray-900/30">
                  {t(
                    'projects.projectPickerLimitNote',
                    'Showing up to 2,000 most recently modified open projects. Refine search to find one.',
                  )}
                </p>
              )}
            </div>
          )}
        </div>
      </div>

      {focusProject && (
        <div className="reports-selected-project-banner mb-5 rounded-xl border border-teal-200 bg-teal-50/95 px-4 py-3 text-center shadow-sm dark:border-teal-800 dark:bg-teal-950/50">
          <p className="text-[10px] font-bold uppercase tracking-widest text-teal-700 dark:text-teal-300">
            {t('projects.reportProjectFilter', 'Report project')}
          </p>
          <p className="mt-1 text-lg sm:text-xl font-semibold text-gray-900 dark:text-white">
            {projectRows.find(p => p.name === focusProject)?.project_name || focusProject}
          </p>
          <p className="mt-0.5 text-[11px] text-gray-500 dark:text-gray-400 font-mono tabular-nums">{focusProject}</p>
        </div>
      )}

      {focusProject ? (
      <>
      {/* Project snapshot (only when a report project is selected) */}
      <div className="reports-project-snapshot bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 shadow-sm overflow-x-auto overflow-y-visible mb-3">
        <div className="flex flex-col gap-0.5 px-2 py-1 bg-gradient-to-r from-teal-600 to-emerald-700 text-white">
          <div className="flex items-center gap-1.5 min-w-0">
            <FaFolderOpen size={12} className="shrink-0 opacity-95" />
            <div className="min-w-0">
              <h2 className="text-[10px] font-bold leading-tight">Project snapshot</h2>
              <p className="text-[9px] text-white/85 leading-snug line-clamp-2">
                <span className="text-white/90">Using </span>
                <span className="font-semibold text-white">
                  {projectRows.find(p => p.name === focusProject)?.project_name || focusProject}
                </span>
                <span className="text-white/90"> from the report filter.</span>
              </p>
            </div>
          </div>
        </div>

        <div className="reports-snapshot-body p-1.5 flex flex-col gap-2.5">
          {focusProject && overviewProject && (
            <div className="rounded-md border border-gray-200 dark:border-gray-600 overflow-hidden shadow-sm bg-white dark:bg-gray-800">
              <div className="bg-gradient-to-r from-slate-800 to-blue-900 text-white text-center py-0.5 px-2">
                <h2 className="text-[9px] font-bold tracking-wide leading-none">
                  {t('projects.projectOverviewBanner', 'Project Overview')}
                </h2>
              </div>
              <div className="reports-snapshot-overview-inner p-1.5 grid grid-cols-1 md:grid-cols-12 gap-1.5 items-start">
                <div className="reports-snapshot-overview-meta md:col-span-5 min-w-0 text-[10px] text-gray-800 dark:text-gray-200 leading-snug space-y-0.5 self-start">
                  <p className="font-semibold line-clamp-2">
                    <span className="font-bold">{t('projects.overviewProjectNameLabel', 'Project Name :')}</span>{' '}
                    {overviewProject.project_name || overviewProject.name}
                  </p>
                  <div className="flex flex-wrap gap-x-3 gap-y-0 text-[10px]">
                    <span>
                      <span className="font-bold">{t('projects.overviewDepartmentLabel', 'Department:')}</span>{' '}
                      {overviewProject.department ? String(overviewProject.department) : '—'}
                    </span>
                    <span>
                      <span className="font-bold">{t('projects.overviewDateLabel', 'DATE:')}</span> {reportAsOfLabel}
                    </span>
                    <span>
                      <span className="font-bold">{t('projects.overviewProgressLabel', 'Progress:')}</span>{' '}
                      {focusDonutTotal > 0 ? overviewProject.percent_complete ?? 0 : 0}%
                    </span>
                    <span>
                      <span className="font-bold">{t('projects.overviewCustomerLabel', 'Customer:')}</span>{' '}
                      {overviewProject.customer || '—'}
                    </span>
                  </div>
                </div>

                <div className="reports-snapshot-overview-timeline md:col-span-4 min-w-0 md:border-l md:border-gray-200 dark:md:border-gray-600 md:pl-2 space-y-1 self-start">
                  <div className="flex items-center gap-1.5 min-w-0">
                    <span className="text-[9px] font-semibold underline decoration-gray-400 shrink-0 w-[5.75rem]">
                      {t('projects.overviewDatesUpToday', 'Dates up today')}
                    </span>
                    <div className="flex-1 flex items-center gap-1.5 min-w-0">
                      <div className="h-2 flex-1 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden min-w-0">
                        <div
                          className="h-full bg-blue-600 dark:bg-blue-500 rounded-full transition-all"
                          style={{ width: `${overviewTimeline.durationDays ? overviewTimeline.pctElapsed : 0}%` }}
                        />
                      </div>
                      <span className="text-[9px] font-mono tabular-nums font-semibold text-gray-800 dark:text-gray-100 shrink-0">
                        {overviewTimeline.durationDays ? overviewTimeline.elapsedDays : '—'}
                      </span>
                    </div>
                  </div>
                  <div className="flex items-center gap-1.5 min-w-0">
                    <span className="text-[9px] font-semibold underline decoration-gray-400 shrink-0 w-[5.75rem]">
                      {t('projects.overviewProjectDuration', 'Project Duration')}
                    </span>
                    <div className="flex-1 flex items-center gap-1.5 min-w-0">
                      <div className="h-2 flex-1 bg-gray-100 dark:bg-gray-700 rounded-full overflow-hidden min-w-0">
                        <div className="h-full bg-blue-600 dark:bg-blue-500 rounded-full w-full" />
                      </div>
                      <span className="text-[9px] font-mono tabular-nums font-semibold text-gray-800 dark:text-gray-100 shrink-0">
                        {overviewTimeline.durationDays || '—'}
                      </span>
                    </div>
                  </div>
                </div>

                <div className="reports-snapshot-overview-dates md:col-span-3 flex flex-col gap-1.5 self-start items-start md:items-end">
                  <div className="flex items-center gap-1">
                    <span className="text-[9px] font-semibold text-gray-600 dark:text-gray-300 whitespace-nowrap">
                      {t('projects.overviewStartDateLabel', 'Start:')}
                    </span>
                    <span className="inline-flex rounded bg-teal-700 dark:bg-teal-800 text-white text-[10px] font-medium px-1.5 py-0.5 tabular-nums">
                      {formatOverviewPillDate(overviewProject.expected_start_date)}
                    </span>
                  </div>
                  <div className="flex items-center gap-1">
                    <span className="text-[9px] font-semibold text-gray-600 dark:text-gray-300 whitespace-nowrap">
                      {t('projects.overviewEndDateLabel', 'End:')}
                    </span>
                    <span className="inline-flex rounded bg-teal-700 dark:bg-teal-800 text-white text-[10px] font-medium px-1.5 py-0.5 tabular-nums">
                      {formatOverviewPillDate(overviewProject.expected_end_date)}
                    </span>
                  </div>
                </div>
              </div>
            </div>
          )}

          <div className="reports-snapshot-charts-section rounded-md border border-gray-200 dark:border-gray-600 overflow-x-auto shadow-sm bg-white dark:bg-gray-800">
            <div className="bg-gradient-to-r from-slate-800 via-slate-800 to-blue-900 text-white text-center py-1.5 px-2">
              <h2 className="text-[9px] font-bold tracking-wide leading-none">
                {t('projects.weeklyProjectStatus', 'Weekly Project status')}
              </h2>
            </div>
            <div className="reports-snapshot-charts-four grid min-w-0">
              {/* Column 1 — Project budget (billing + invoice chart) */}
              <div className="reports-snapshot-chart-col flex flex-col min-w-0 min-h-0 max-w-full rounded-md border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800/90 shadow-sm overflow-hidden">
                <div className="flex items-center gap-1 px-1.5 py-0.5 bg-gradient-to-r from-teal-600 to-cyan-600 text-white shrink-0">
                  <FaChartBar className="shrink-0 opacity-95" size={11} aria-hidden />
                  <span className="text-[9px] font-bold uppercase tracking-wide leading-none">
                    {t('projects.snapshotProjectBudget', 'Project budget')}
                  </span>
                </div>
                <div className="flex flex-1 min-h-0 flex-col gap-1.5 p-1.5 min-w-0">
                  {focusProjectDoc ? (
                    <>
                      <div className="rounded-md border border-gray-200 dark:border-gray-600 p-1.5 bg-white dark:bg-gray-800/90 shadow-sm shrink-0 min-w-0 w-full">
                        <div className="flex items-start justify-between gap-1.5">
                          <div className="min-w-0">
                            <p className="text-[9px] font-bold uppercase tracking-wide text-gray-600 dark:text-gray-300">
                              Budget & Billing
                            </p>
                            <p className="text-[9px] text-gray-500 dark:text-gray-400 leading-tight mt-0.5 line-clamp-2">
                              Budget = estimated / sales · Used = billed
                            </p>
                          </div>
                          <span className="text-[9px] font-mono bg-gray-100 dark:bg-gray-700/80 text-gray-800 dark:text-gray-100 px-1 py-0.5 rounded shrink-0">
                            {budgetPct}%
                          </span>
                        </div>
                        <p className="reports-snapshot-budget-metrics mt-1.5 text-[9px] leading-snug text-gray-700 dark:text-gray-200 tabular-nums">
                          <span className="text-gray-500">Total</span>{' '}
                          <span className="font-semibold text-gray-900 dark:text-white">{budgetTotal.toLocaleString()}</span>
                          <span className="mx-1 text-gray-300 dark:text-gray-600">·</span>
                          <span className="text-gray-500">Used</span>{' '}
                          <span className="font-semibold text-gray-900 dark:text-white">{budgetSpent.toLocaleString()}</span>
                          <span className="mx-1 text-gray-300 dark:text-gray-600">·</span>
                          <span className="text-gray-500">Balance</span>{' '}
                          <span className="font-semibold text-gray-900 dark:text-white">{budgetRemaining.toLocaleString()}</span>
                        </p>
                        <div className="mt-1">
                          <div className="h-1 w-full bg-gray-100 dark:bg-gray-700/50 rounded-full overflow-hidden">
                            <div
                              className="h-full bg-gradient-to-r from-teal-500 to-emerald-600 rounded-full transition-all"
                              style={{ width: `${budgetPct}%` }}
                            />
                          </div>
                          <div className="mt-0.5 flex items-center justify-between text-[8px] text-gray-500 leading-none">
                            <span>0</span>
                            <span>{budgetTotal.toLocaleString()}</span>
                          </div>
                        </div>
                      </div>
                      <div className="rounded-md border border-gray-200 dark:border-gray-600 p-1.5 flex flex-col min-w-0 min-h-0 flex-1 bg-white dark:bg-gray-800/90 shadow-sm">
                        <p className="text-[9px] font-bold uppercase tracking-wide text-gray-600 dark:text-gray-300 leading-none shrink-0">
                          Budget (by invoice)
                        </p>
                        <p className="text-[8px] text-gray-500 dark:text-gray-400 mb-0.5 mt-0.5 leading-tight line-clamp-1 shrink-0">
                          Linked via SO lines or Project · stacked per invoice
                        </p>
                        <div
                          className={`reports-snapshot-invoice-chart mx-auto w-[calc(100%-8px)] max-w-full rounded border border-gray-200 dark:border-gray-600 bg-white/70 dark:bg-gray-800/60 ${
                            reportLandscapeWide ? 'min-h-[9.75rem] h-[9.75rem]' : 'min-h-[9.25rem] h-[9.25rem]'
                          }`}
                        >
                          {focusInvoiceLoading ? (
                            <div className="h-full flex items-center justify-center text-gray-400">
                              <FaSpinner className="animate-spin text-sm" />
                            </div>
                          ) : invoiceBudgetRows.length === 0 ? (
                            <div className="h-full flex items-center justify-center text-[10px] text-gray-400 text-center px-1">
                              No linked Sales Invoices.
                            </div>
                          ) : (
                            <ResponsiveContainer
                              key={`inv-${reportLandscapeWide}-${printLandscapeCharts}`}
                              width="100%"
                              height="100%"
                              minHeight={112}
                              debounce={printLandscapeCharts ? 0 : 32}
                            >
                              <BarChart
                                data={invoiceBudgetRows}
                                margin={
                                  printLandscapeCharts
                                    ? { top: 4, right: 6, left: 2, bottom: 18 }
                                    : { top: 6, right: 8, left: 4, bottom: 20 }
                                }
                                barCategoryGap="16%"
                              >
                                <CartesianGrid strokeDasharray="3 3" className="stroke-gray-200 dark:stroke-gray-600" />
                                <XAxis
                                  dataKey="name"
                                  tick={{ fontSize: 7 }}
                                  interval={0}
                                  angle={0}
                                  textAnchor="middle"
                                  height={18}
                                  tickMargin={0}
                                  padding={{ left: 10, right: 10 }}
                                />
                                <YAxis
                                  tick={{ fontSize: 7 }}
                                  width={printLandscapeCharts ? 26 : reportLandscapeWide ? 28 : 30}
                                  tickMargin={2}
                                  tickFormatter={formatCompactAxisNumber}
                                />
                                <Tooltip content={<CustomTooltip />} />
                                <Bar
                                  dataKey="spent"
                                  name="Spent"
                                  stackId="b"
                                  fill="#2563eb"
                                  radius={[1, 1, 0, 0]}
                                  maxBarSize={printLandscapeCharts ? 14 : reportLandscapeWide ? 13 : 12}
                                  isAnimationActive={snapshotChartIsAnimationActive}
                                />
                                <Bar
                                  dataKey="remaining"
                                  name="Rem."
                                  stackId="b"
                                  fill="#14b8a6"
                                  radius={[1, 1, 0, 0]}
                                  maxBarSize={printLandscapeCharts ? 14 : reportLandscapeWide ? 13 : 12}
                                  isAnimationActive={snapshotChartIsAnimationActive}
                                />
                              </BarChart>
                            </ResponsiveContainer>
                          )}
                        </div>
                        {invoiceBudgetRows.length > 0 && (
                          <p className="mt-0.5 text-[7px] text-gray-500 text-center leading-tight">
                            <span className="inline-block w-1.5 h-1.5 rounded-sm bg-[#2563eb] align-middle mr-0.5" aria-hidden />
                            Spent
                            <span className="mx-1.5 text-gray-400">·</span>
                            <span className="inline-block w-1.5 h-1.5 rounded-sm bg-[#14b8a6] align-middle mr-0.5" aria-hidden />
                            Remaining (vs budget)
                          </p>
                        )}
                        {focusInvoices.length > 12 && (
                          <p className="mt-0.5 text-[8px] text-gray-500">First 12 of {focusInvoices.length} invoices.</p>
                        )}
                      </div>
                    </>
                  ) : (
                    <div className="flex flex-1 items-center justify-center text-[9px] text-gray-400 border border-dashed rounded-md px-2 text-center py-4">
                      Select a project above.
                    </div>
                  )}
                </div>
              </div>

              {/* Column 2 — Tasks status (donut + legend, progress in center) */}
              <div className="reports-snapshot-chart-col flex flex-col min-w-0 min-h-0 max-w-full rounded-md border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800/90 shadow-sm overflow-hidden">
                <div className="flex items-center gap-1 px-1.5 py-0.5 bg-gradient-to-r from-teal-600 to-emerald-700 text-white shrink-0">
                  <FaList className="shrink-0" size={11} aria-hidden />
                  <span className="text-[9px] font-bold uppercase tracking-wide leading-none">Tasks status</span>
                </div>
                <div className="flex flex-1 min-h-0 flex-col p-1.5 min-w-0">
                  <div className="reports-snapshot-overall-card rounded-md border border-gray-200 dark:border-gray-600 bg-white/50 dark:bg-gray-800/40 p-1.5 flex flex-col overflow-hidden min-w-0 min-h-0 flex-1">
                    <div className="flex flex-1 flex-col min-h-0 gap-2 min-w-0 w-full">
                      {focusLoading ? (
                        <div className="w-full h-16 flex items-center justify-center text-gray-400">
                          <FaSpinner className="animate-spin text-sm" />
                        </div>
                      ) : !focusProject ? (
                        <div className="w-full min-h-16 flex items-center justify-center text-[9px] text-gray-400 border border-dashed rounded-md px-2 text-center">
                          Select a project above.
                        </div>
                      ) : focusDonut.length === 0 ? (
                        <div className="w-full min-h-16 flex items-center justify-center text-[9px] text-gray-400 border border-dashed rounded-md">
                          No tasks.
                        </div>
                      ) : (
                        <div className="flex flex-1 flex-col min-h-0 w-full min-w-0">
                          <div className="flex flex-1 min-h-[9rem] w-full items-center justify-center">
                            <div
                              className={`reports-snapshot-donut-inner relative shrink-0 mx-auto ${
                                reportLandscapeWide ? 'h-[140px] w-[140px]' : 'h-[128px] w-[128px]'
                              }`}
                            >
                            <div className="absolute inset-0">
                              <ResponsiveContainer
                                key={`donut-${reportLandscapeWide}-${printLandscapeCharts}`}
                                width="100%"
                                height="100%"
                                debounce={printLandscapeCharts ? 0 : 32}
                              >
                                <PieChart margin={{ top: 0, right: 0, bottom: 0, left: 0 }}>
                                  <Pie
                                    data={focusDonut}
                                    dataKey="value"
                                    nameKey="name"
                                    cx="50%"
                                    cy="50%"
                                    innerRadius={reportLandscapeWide ? 36 : 30}
                                    outerRadius={reportLandscapeWide ? 58 : 48}
                                    paddingAngle={0.5}
                                    label={false}
                                    isAnimationActive={snapshotChartIsAnimationActive}
                                  >
                                    {focusDonut.map((_, i) => (
                                      <Cell key={i} fill={PIE_COLORS[i % PIE_COLORS.length]} stroke="#fff" strokeWidth={1} />
                                    ))}
                                  </Pie>
                                  <Tooltip content={<CustomTooltip />} />
                                </PieChart>
                              </ResponsiveContainer>
                            </div>
                            <div className="absolute inset-0 z-10 flex flex-col items-center justify-center pointer-events-none px-1 text-center">
                              <span className="text-[6px] font-bold uppercase tracking-wide text-gray-500 dark:text-gray-400 leading-none">
                                {t('projects.snapshotOverallShort', 'Overall')}
                              </span>
                              <span className="text-sm font-bold text-gray-900 dark:text-white tabular-nums leading-tight">
                                {focusDonutTotal > 0
                                  ? overviewProject?.percent_complete ?? focusProjectDoc?.percent_complete ?? 0
                                  : 0}
                                %
                              </span>
                            </div>
                          </div>
                          </div>
                          <ul className="reports-snapshot-donut-legend list-none m-0 w-full shrink-0 border-t border-gray-200/90 dark:border-gray-600/80 pt-1.5 pb-0.5 flex flex-wrap justify-center gap-x-1.5 gap-y-1 content-start max-h-[3.5rem] overflow-y-auto">
                            {focusDonut.map((row, i) => {
                              const pct = focusDonutTotal ? Math.round((row.value / focusDonutTotal) * 100) : 0;
                              return (
                                <li
                                  key={row.name}
                                  className="inline-flex items-center gap-0.5 text-[7px] text-gray-700 dark:text-gray-200 leading-none whitespace-nowrap"
                                >
                                  <span
                                    className="w-1.5 h-1.5 rounded-full shrink-0 border border-white/80 dark:border-gray-600"
                                    style={{ backgroundColor: PIE_COLORS[i % PIE_COLORS.length] }}
                                    aria-hidden
                                  />
                                  <span
                                    className="reports-snapshot-legend-label max-w-[5.5rem] truncate"
                                    title={`${row.name} (${pct}%)`}
                                  >
                                    {row.name} ({pct}%)
                                  </span>
                                </li>
                              );
                            })}
                          </ul>
                        </div>
                      )}
                    </div>
                  </div>
                </div>
              </div>

              {/* Column 3 — Risks / Issues */}
              <div className="reports-snapshot-chart-col flex flex-col min-w-0 min-h-0 max-w-full">
                <div className="reports-snapshot-weekly-chart-card rounded-md border border-gray-200 dark:border-gray-600 overflow-x-hidden overflow-y-visible flex flex-col flex-1 min-h-0 w-full min-w-0 max-w-full bg-white dark:bg-gray-800/90 shadow-sm h-full">
                  <div className="flex items-center gap-1.5 px-1.5 py-1 bg-gradient-to-r from-amber-500 to-orange-600 text-white shrink-0">
                    <FaExclamationTriangle size={11} className="shrink-0" aria-hidden />
                    <div className="min-w-0">
                      <h3 className="text-[9px] font-bold leading-none">Risks / Issues</h3>
                      <p className="text-[8px] text-white/90 line-clamp-2 leading-tight">
                        Open tasks with Risk = Yes, by priority (High includes Urgent).
                      </p>
                    </div>
                  </div>
                  <div className="p-1 pb-2 flex flex-1 flex-col min-h-0 items-stretch w-full min-w-0 max-w-full">
                    {loading ? (
                      <div className="flex min-h-[7rem] flex-1 items-center justify-center text-gray-400">
                        <FaSpinner className="animate-spin text-lg" />
                      </div>
                    ) : (
                      <div ref={riskBarShellRef} className={snapshotRiskActionsPlotShellClass}>
                        <ResponsiveContainer
                          key={`risk-${reportLandscapeWide}-${printLandscapeCharts}-${riskChartBox.width}-${riskChartBox.height}`}
                          width={riskChartBox.width}
                          height={riskChartBox.height}
                          debounce={printLandscapeCharts ? 0 : 32}
                        >
                          <BarChart
                            data={riskIssuesBarData}
                            margin={
                              printLandscapeCharts
                                ? { top: 14, right: 0, left: 0, bottom: 6 }
                                : { top: 18, right: 0, left: 0, bottom: 8 }
                            }
                            barCategoryGap="6%"
                          >
                            <CartesianGrid
                              vertical={false}
                              strokeDasharray="3 3"
                              className="stroke-gray-200/90 dark:stroke-gray-600/80"
                            />
                            <XAxis
                              dataKey="name"
                              tick={{ fontSize: 8 }}
                              interval={0}
                              height={22}
                              tickMargin={4}
                              tickLine={false}
                              axisLine={{ stroke: '#d1d5db' }}
                              padding={{ left: 0, right: 0 }}
                            />
                            <YAxis
                              tick={{ fontSize: 8 }}
                              width={18}
                              allowDecimals={false}
                              domain={[0, riskYAxisMax]}
                              ticks={riskYAxisMax <= 5 ? [0, 1, 2, 3, 4, 5] : undefined}
                              tickLine={false}
                              axisLine={{ stroke: '#d1d5db' }}
                            />
                            <Tooltip content={<CustomTooltip />} />
                            <Legend
                              verticalAlign="bottom"
                              align="center"
                              wrapperStyle={{ paddingTop: 4, margin: 0, width: '100%' }}
                              content={() => (
                                <div className="flex flex-wrap justify-center gap-x-3 gap-y-1 text-[7px] leading-none text-gray-700 dark:text-gray-200">
                                  {riskIssuesBarData.map(row => (
                                    <span key={row.name} className="inline-flex items-center gap-0.5">
                                      <span
                                        className="h-1.5 w-1.5 shrink-0 rounded-sm"
                                        style={{ backgroundColor: row.fill }}
                                        aria-hidden
                                      />
                                      {row.name}
                                    </span>
                                  ))}
                                </div>
                              )}
                            />
                            <Bar
                              dataKey="count"
                              name="Count"
                              maxBarSize={printLandscapeCharts ? 22 : reportLandscapeWide ? 28 : 24}
                              radius={[1, 1, 0, 0]}
                              isAnimationActive={snapshotChartIsAnimationActive}
                            >
                              {riskIssuesBarData.map(row => (
                                <Cell key={row.name} fill={row.count > 0 ? row.fill : 'transparent'} stroke="none" />
                              ))}
                              <LabelList
                                dataKey="count"
                                content={(props: any) => {
                                  const x = Number(props.x) || 0;
                                  const y = Number(props.y) || 0;
                                  const w = Number(props.width) || 0;
                                  const h = Number(props.height) || 0;
                                  const value = Number(props.value) || 0;
                                  const cx = x + w / 2;
                                  if (value > 0) {
                                    return (
                                      <text
                                        x={cx}
                                        y={y - 4}
                                        textAnchor="middle"
                                        fontSize={9}
                                        fontWeight={600}
                                        className="fill-gray-900 dark:fill-gray-100"
                                      >
                                        {value}
                                      </text>
                                    );
                                  }
                                  return (
                                    <text
                                      x={cx}
                                      y={y + h + 11}
                                      textAnchor="middle"
                                      fontSize={8}
                                      className="fill-gray-400 dark:fill-gray-500"
                                    >
                                      0
                                    </text>
                                  );
                                }}
                              />
                            </Bar>
                          </BarChart>
                        </ResponsiveContainer>
                      </div>
                    )}
                  </div>
                </div>
              </div>

              {/* Column 4 — Actions */}
              <div className="reports-snapshot-chart-col flex flex-col min-w-0 min-h-0 max-w-full">
                <div className="reports-snapshot-weekly-chart-card rounded-md border border-gray-200 dark:border-gray-600 overflow-x-hidden overflow-y-visible flex flex-col flex-1 min-h-0 w-full min-w-0 max-w-full bg-white dark:bg-gray-800/90 shadow-sm h-full">
                  <div className="flex items-center gap-1.5 px-1.5 py-1 bg-gradient-to-r from-sky-600 to-indigo-700 text-white shrink-0">
                    <FaBolt size={11} className="shrink-0" aria-hidden />
                    <div className="min-w-0">
                      <h3 className="text-[9px] font-bold leading-none">Actions</h3>
                      <p className="text-[8px] text-white/90 line-clamp-2 leading-tight">Open tasks by Action (custom field).</p>
                    </div>
                  </div>
                  <div className="p-1 pb-2 flex flex-1 flex-col min-h-0 items-stretch w-full min-w-0 max-w-full">
                    {loading ? (
                      <div className="flex min-h-[7rem] flex-1 items-center justify-center text-gray-400">
                        <FaSpinner className="animate-spin text-lg" />
                      </div>
                    ) : (
                      <div ref={actionsBarShellRef} className={snapshotRiskActionsPlotShellClass}>
                        <ResponsiveContainer
                          key={`actions-${reportLandscapeWide}-${printLandscapeCharts}-${actionsChartBox.width}-${actionsChartBox.height}`}
                          width={actionsChartBox.width}
                          height={actionsChartBox.height}
                          debounce={printLandscapeCharts ? 0 : 32}
                        >
                          <BarChart
                            data={actionsBarData}
                            margin={
                              printLandscapeCharts
                                ? { top: 14, right: 0, left: 0, bottom: 28 }
                                : { top: 18, right: 0, left: 0, bottom: 32 }
                            }
                            barCategoryGap="4%"
                          >
                            <CartesianGrid
                              vertical={false}
                              strokeDasharray="3 3"
                              className="stroke-gray-200/90 dark:stroke-gray-600/80"
                            />
                            <XAxis
                              dataKey="name"
                              tick={{ fontSize: 6 }}
                              interval={0}
                              angle={printLandscapeCharts ? -10 : -12}
                              textAnchor="end"
                              height={printLandscapeCharts ? 30 : reportLandscapeWide ? 32 : 34}
                              tickMargin={2}
                              tickLine={false}
                              axisLine={{ stroke: '#d1d5db' }}
                              padding={{ left: 0, right: 0 }}
                            />
                            <YAxis
                              tick={{ fontSize: 8 }}
                              width={18}
                              allowDecimals={false}
                              domain={[0, actionsYAxisMax]}
                              ticks={actionsYAxisMax <= 5 ? [0, 1, 2, 3, 4, 5] : undefined}
                              tickLine={false}
                              axisLine={{ stroke: '#d1d5db' }}
                            />
                            <Tooltip
                              content={({ payload }) => {
                                if (!payload?.length) return null;
                                const row = payload[0]?.payload as { fullLabel?: string; KPI?: number };
                                return (
                                  <div className="rounded border border-gray-200 bg-white px-2 py-1 text-[10px] shadow dark:bg-gray-800 dark:border-gray-600">
                                    <div className="font-semibold text-gray-800 dark:text-gray-100">{row.fullLabel}</div>
                                    <div className="text-gray-600 dark:text-gray-300">KPI: {row.KPI ?? 0}</div>
                                  </div>
                                );
                              }}
                            />
                            <Legend
                              verticalAlign="bottom"
                              align="center"
                              wrapperStyle={{ paddingTop: 4, margin: 0, width: '100%' }}
                              content={() => (
                                <div className="flex flex-wrap justify-center gap-x-3 gap-y-1 text-[7px] leading-none text-gray-700 dark:text-gray-200">
                                  <span className="inline-flex items-center gap-0.5">
                                    <span
                                      className="h-1.5 w-1.5 shrink-0 rounded-sm bg-[#2563eb]"
                                      aria-hidden
                                    />
                                    KPI (open tasks)
                                  </span>
                                </div>
                              )}
                            />
                            <Bar
                              dataKey="KPI"
                              name="KPI"
                              fill="#2563eb"
                              maxBarSize={printLandscapeCharts ? 18 : reportLandscapeWide ? 24 : 20}
                              radius={[1, 1, 0, 0]}
                              isAnimationActive={snapshotChartIsAnimationActive}
                            >
                              <LabelList
                                dataKey="KPI"
                                content={(props: any) => {
                                  const x = Number(props.x) || 0;
                                  const y = Number(props.y) || 0;
                                  const w = Number(props.width) || 0;
                                  const h = Number(props.height) || 0;
                                  const value = Number(props.value) || 0;
                                  const cx = x + w / 2;
                                  if (value > 0) {
                                    return (
                                      <text
                                        x={cx}
                                        y={y - 4}
                                        textAnchor="middle"
                                        fontSize={9}
                                        fontWeight={600}
                                        className="fill-gray-900 dark:fill-gray-100"
                                      >
                                        {value}
                                      </text>
                                    );
                                  }
                                  return (
                                    <text
                                      x={cx}
                                      y={y + h + 11}
                                      textAnchor="middle"
                                      fontSize={8}
                                      className="fill-gray-400 dark:fill-gray-500"
                                    >
                                      0
                                    </text>
                                  );
                                }}
                              />
                            </Bar>
                          </BarChart>
                        </ResponsiveContainer>
                      </div>
                    )}
                  </div>
                </div>
              </div>
            </div>
          </div>
          {(overviewProject || focusProjectDoc) && (
            <div className="reports-snapshot-updates-notes-row grid gap-1.5 min-w-0">
              <div className="rounded-md border border-violet-500/30 dark:border-violet-600/40 overflow-hidden shadow-sm bg-white dark:bg-gray-800 min-w-0 flex flex-col">
                <div className="bg-gradient-to-r from-violet-600 to-fuchsia-700 text-white px-2 py-0.5 shrink-0">
                  <h3 className="text-[9px] font-bold uppercase tracking-wide leading-none">
                    {t('projects.snapshotUpdatesColumn', 'Updates')}
                  </h3>
                </div>
                <div className="reports-snapshot-notes-body p-1.5 flex-1 min-h-0 max-h-[min(12rem,36vh)] overflow-y-auto">
                  {dashboardUpdatesLoading ? (
                    <div className="reports-snapshot-updates-loading h-16 flex items-center justify-center text-gray-400">
                      <FaSpinner className="animate-spin text-sm" />
                    </div>
                  ) : dashboardProgressUpdates.length === 0 ? (
                    <p className="text-[9px] text-gray-400">No updates for this project.</p>
                  ) : (
                    <table className="w-full text-[8px] text-left">
                      <thead>
                        <tr className="border-b border-gray-200 dark:border-gray-600 text-gray-500 dark:text-gray-400">
                          <th className="py-0.5 pr-1 font-semibold">Update</th>
                          <th className="py-0.5 px-0.5 font-semibold w-[4.5rem]">Date</th>
                          <th className="py-0.5 pl-0.5 font-semibold">Task</th>
                        </tr>
                      </thead>
                      <tbody>
                        {dashboardProgressUpdates.slice(0, 25).map((row, i) => (
                          <tr key={`${row.name || 'n'}-${row.task || ''}-${i}`} className="border-t border-gray-100 dark:border-gray-700 align-top">
                            <td className="py-0.5 pr-1 text-gray-800 dark:text-gray-200 break-words">{row.update_ || '—'}</td>
                            <td className="py-0.5 px-0.5 font-mono text-gray-600 dark:text-gray-400 whitespace-nowrap">
                              {row.date ? formatOverviewPillDate(String(row.date).slice(0, 10)) : '—'}
                            </td>
                            <td className="py-0.5 pl-0.5">
                              {row.task ? (
                                <button
                                  type="button"
                                  onClick={() => navigate(`/projects/tasks/${encodeURIComponent(row.task!)}`)}
                                  className="text-violet-700 dark:text-violet-300 hover:underline font-mono text-[8px] text-left"
                                >
                                  {row.task}
                                </button>
                              ) : (
                                '—'
                              )}
                            </td>
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  )}
                </div>
              </div>
              <div className="rounded-md border border-teal-500/35 dark:border-teal-600/45 overflow-hidden shadow-sm bg-white dark:bg-gray-800 min-w-0 flex flex-col">
                <div className="bg-gradient-to-r from-teal-500 to-emerald-600 text-white px-2 py-0.5 shrink-0">
                  <h3 className="text-[9px] font-bold uppercase tracking-wide leading-none">
                    {t('projects.snapshotNotesColumn', 'Notes')}
                  </h3>
                </div>
                <div className="reports-snapshot-notes-body p-1.5 flex-1 min-h-0 max-h-[min(12rem,36vh)] overflow-y-auto">
                  <p className="text-[9px] text-gray-700 dark:text-gray-200 whitespace-pre-wrap leading-tight">
                    {formatProjectNotesForDisplay(overviewProject?.notes ?? focusProjectDoc?.notes) || 'No notes for this project.'}
                  </p>
                </div>
              </div>
            </div>
          )}

          <div className="reports-snapshot-bottom-tasks grid grid-cols-1 lg:grid-cols-3 gap-1.5 min-w-0">
            <div className="min-h-0 shrink-0 min-w-0">
              <h3 className="text-[9px] font-bold uppercase tracking-wide text-gray-600 dark:text-gray-300 mb-0.5 leading-none">
                Last week Tasks
                {lastWeekRows.length > 0 && <span className="ml-1 text-[9px] font-mono text-gray-500">({lastWeekRows.length})</span>}
              </h3>
              <div className="rounded-md border border-gray-200 dark:border-gray-600 overflow-hidden">
                <div className="reports-snapshot-table-scroll">
                  <table className="min-w-full text-[9px] table-fixed w-full">
                    <thead className="sticky top-0 z-10">
                      <tr className="bg-violet-700 text-white text-left">
                        <th className="px-1 py-0.5 w-7">N</th>
                        <th className="px-1 py-0.5">Task</th>
                        <th className="px-1 py-0.5">Assigned</th>
                        <th className="px-1 py-0.5 w-[4.5rem]">Action</th>
                      </tr>
                    </thead>
                    <tbody>
                      {lastWeekRows.length === 0 ? (
                        <tr>
                          <td colSpan={4} className="px-1.5 py-1 text-gray-400 text-center">
                            None
                          </td>
                        </tr>
                      ) : (
                        lastWeekRows.map((t, idx) => {
                          const taskPath = `/projects/tasks/${encodeURIComponent(t.name)}`;
                          return (
                            <tr
                              key={t.name}
                              tabIndex={0}
                              role="link"
                              className="border-t border-gray-100 dark:border-gray-700 text-rose-700 dark:text-rose-300 cursor-pointer hover:bg-rose-50/80 dark:hover:bg-rose-950/20"
                              onClick={() => navigate(taskPath)}
                              onKeyDown={e => {
                                if (e.key === 'Enter' || e.key === ' ') {
                                  e.preventDefault();
                                  navigate(taskPath);
                                }
                              }}
                            >
                              <td className="px-1 py-0.5 font-mono tabular-nums text-gray-600 dark:text-gray-400">{idx + 1}</td>
                              <td className="px-1.5 py-0.5 truncate" title={t.subject || t.name}>
                                {t.subject || t.name}
                              </td>
                              <td className="px-1.5 py-0.5 truncate">{assignLabel(t)}</td>
                              <td className="px-1.5 py-0.5 font-medium text-gray-800 dark:text-gray-200 truncate" title={t.status || ''}>
                                {t.status || '—'}
                              </td>
                            </tr>
                          );
                        })
                      )}
                    </tbody>
                  </table>
                </div>
              </div>
              {lastWeekRows.length > 20 && (
                <p className="mt-0.5 text-[9px] text-gray-500">Scroll to see all {lastWeekRows.length} items.</p>
              )}
            </div>
            <div className="min-h-0 shrink-0 min-w-0">
              <h3 className="text-[9px] font-bold uppercase tracking-wide text-gray-600 dark:text-gray-300 mb-0.5 leading-none">
                Upcoming Tasks
                {upcomingRows.length > 0 && <span className="ml-1 text-[9px] font-mono text-gray-500">({upcomingRows.length})</span>}
              </h3>
              <div className="rounded-md border border-gray-200 dark:border-gray-600 overflow-hidden">
                <div className="reports-snapshot-table-scroll">
                  <table className="min-w-full text-[9px] table-fixed w-full">
                    <thead className="sticky top-0 z-10">
                      <tr className="bg-violet-700 text-white text-left">
                        <th className="px-1 py-0.5 w-7">N</th>
                        <th className="px-1 py-0.5 w-[32%]">Task</th>
                        <th className="px-1 py-0.5 w-[22%]">Assigned to</th>
                        <th className="px-1 py-0.5 w-[17%]">Start Date</th>
                        <th className="px-1 py-0.5 w-[17%]">End Date</th>
                      </tr>
                    </thead>
                    <tbody>
                      {upcomingRows.length === 0 ? (
                        <tr>
                          <td colSpan={5} className="px-1.5 py-1 text-gray-400 text-center">
                            None
                          </td>
                        </tr>
                      ) : (
                        upcomingRows.map((t, idx) => {
                          const taskPath = `/projects/tasks/${encodeURIComponent(t.name)}`;
                          return (
                            <tr
                              key={t.name}
                              tabIndex={0}
                              role="link"
                              className="border-t border-gray-100 dark:border-gray-700 cursor-pointer hover:bg-slate-100/90 dark:hover:bg-slate-800/60"
                              onClick={() => navigate(taskPath)}
                              onKeyDown={e => {
                                if (e.key === 'Enter' || e.key === ' ') {
                                  e.preventDefault();
                                  navigate(taskPath);
                                }
                              }}
                            >
                              <td className="px-1 py-0.5 font-mono tabular-nums text-gray-600 dark:text-gray-400">{idx + 1}</td>
                              <td className="px-1.5 py-0.5 text-gray-800 dark:text-gray-200 truncate" title={t.subject || t.name}>
                                {t.subject || t.name}
                              </td>
                              <td className="px-1.5 py-0.5 truncate">{assignLabel(t)}</td>
                              <td className="px-1 py-0.5 font-mono tabular-nums text-gray-600 dark:text-gray-400">{t.exp_start_date?.slice(0, 10) || '—'}</td>
                              <td className="px-1 py-0.5 font-mono tabular-nums text-gray-600 dark:text-gray-400">{t.exp_end_date?.slice(0, 10) || '—'}</td>
                            </tr>
                          );
                        })
                      )}
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
            <div className="min-h-0 shrink-0 min-w-0">
              <h3 className="text-[9px] font-bold uppercase tracking-wide text-gray-600 dark:text-gray-300 mb-0.5 leading-none">
                Obstacles
                {obstacleTableRows.length > 0 && (
                  <span className="ml-1 text-[9px] font-mono text-gray-500">({obstacleTableRows.length})</span>
                )}
              </h3>
              <div className="rounded-md border border-gray-200 dark:border-gray-600 overflow-hidden">
                <div className="reports-snapshot-table-scroll">
                  <table className="min-w-full text-[9px] table-fixed w-full">
                    <thead className="sticky top-0 z-10">
                      <tr className="bg-violet-700 text-white text-left">
                        <th className="px-1 py-0.5 w-7">N</th>
                        <th className="px-1 py-0.5">Task</th>
                      </tr>
                    </thead>
                    <tbody>
                      {obstacleTableRows.length === 0 ? (
                        <tr>
                          <td colSpan={2} className="px-1.5 py-1 text-gray-400 text-center">
                            None
                          </td>
                        </tr>
                      ) : (
                        obstacleTableRows.map((row, idx) => {
                          const taskPath = `/projects/tasks/${encodeURIComponent(row.name)}`;
                          const obs = String(row.custom_task_obstacle || '').trim();
                          return (
                            <tr key={row.name} className="border-t border-gray-100 dark:border-gray-700">
                              <td className="px-1 py-0.5 font-mono tabular-nums text-gray-600 dark:text-gray-400 align-top">{idx + 1}</td>
                              <td className="px-1.5 py-0.5 align-top text-gray-800 dark:text-gray-200">
                                <button
                                  type="button"
                                  onClick={() => navigate(taskPath)}
                                  className="text-left font-medium text-blue-600 dark:text-blue-400 hover:underline block w-full truncate"
                                  title={row.subject || row.name}
                                >
                                  {row.subject || row.name}
                                </button>
                                {obs ? <p className="text-[8px] text-gray-500 dark:text-gray-400 mt-0.5 whitespace-pre-wrap break-words">{obs}</p> : null}
                              </td>
                            </tr>
                          );
                        })
                      )}
                    </tbody>
                  </table>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      </>
      ) : null}

    </div>
    </>
  );
};

export default ProjectReportsDashboard;
