/* screen_finance_printplan.jsx — PrintPlan Dashboard (React)
 * Dados mock: screen_finance_printplan_data.js (carregado primeiro em index.html)
 * Gerado a partir de dashboard_printplan_v3 (24).html do Márcio */

const _PP_CSS = `
/* ===== TOKENS — bridge to portal design system ===== */
.pp-root {
  --red-700:#B53B3B;--red-100:#FFE3E0;
  --green-700:#1F8A52;--green-100:#E1F7E6;
  --yellow-700:#C99A2E;--yellow-100:#FFF6D6;
  --pumpkin-700:#E96D17;--pumpkin-100:#FFCFCA;
  --purple-700:#5B43C5;--purple-100:#ECE7FF;
  /* bridge to portal tokens */
  --dd-primary-900:var(--brand-900,#112954);
  --dd-primary-600:var(--brand-600,#3859D0);
  --bg-surface:var(--surface,#fff);
  --bg-app:var(--bg,#f5f6f8);
  --border-1:var(--border,#E1E7F3);
  --fg-1:var(--text,#1D2E38);
  --fg-2:var(--text-muted,#64748b);
  --fg-3:var(--text-dim,#94a4c4);
  --font-sans:var(--font-display,'Montserrat',sans-serif);
  --font-text:var(--font-body,'Inter',sans-serif);
  --space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;
  --radius-xs:4px;--radius-sm:6px;--radius-md:8px;--radius-lg:10px;--radius-xl:10px;--radius-pill:999px;
  --shadow-card:none;
}
/* ===== LAYOUT ===== */
.pp-root{height:100%;display:flex;flex-direction:column;overflow:hidden}
.pp-root *{box-sizing:border-box}
.pp-root .pp-tab-content{flex:1;overflow-y:auto;min-height:0}
.page{max-width:1440px;margin:0 auto;padding:24px 28px 48px}
.tab-panel{display:none}.tab-panel.active{display:block}
/* ===== TAB NAV ===== */
.pp-root .pp-tabnav{display:flex;align-items:center;gap:0;background:var(--bg-surface);border-bottom:1px solid var(--border-1);padding:0 20px;flex-shrink:0}
.tab-btn{height:44px;padding:0 14px;background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;font-size:12.5px;font-weight:500;color:var(--fg-2);font-family:var(--font-sans);white-space:nowrap;display:flex;align-items:center;gap:6px;margin-bottom:-1px;transition:color .15s,border-color .15s}
.tab-btn.active{color:var(--fg-1);border-bottom-color:var(--dd-primary-900);font-weight:600}
.tab-btn:hover:not(.active){color:var(--fg-1)}
.tab-pill{display:inline-flex;align-items:center;justify-content:center;height:16px;min-width:16px;padding:0 4px;border-radius:8px;background:var(--red-700);color:#fff;font-size:9.5px;font-weight:700}
.topbar-date{font-size:11px;color:var(--fg-3);font-family:var(--font-mono,monospace)}
/* ===== KPI BANNER ===== */
.ov-banner{display:grid;grid-template-columns:repeat(6,1fr);background:var(--bg-surface);border:1px solid var(--border-1);border-radius:var(--radius-xl);overflow:hidden;margin-bottom:14px}
.ov-banner-item{padding:16px 20px;border-right:1px solid var(--border-1)}
.ov-banner-item:last-child{border-right:none}
.ov-banner-v{font-family:var(--font-sans);font-size:21px;font-weight:600;color:var(--fg-1);line-height:1.2}
.ov-banner-l{font-size:10px;font-weight:600;color:var(--fg-3);text-transform:uppercase;letter-spacing:.07em;margin-top:4px;font-family:var(--font-text)}
.ov-banner-val{font-family:var(--font-sans);font-size:21px;font-weight:600;color:var(--fg-1);line-height:1.2}
.ov-banner-val.accent{color:var(--pumpkin-700)}
.ov-banner-label{font-size:10px;font-weight:600;color:var(--fg-3);text-transform:uppercase;letter-spacing:.07em;margin-top:4px;font-family:var(--font-text)}
.ov-banner-sub{font-size:10px;color:var(--fg-2);margin-top:2px}
/* ===== CARDS ===== */
.card{background:var(--bg-surface);border-radius:var(--radius-xl);border:1px solid var(--border-1);padding:20px}
.card-title{font-size:11px;font-weight:700;color:var(--fg-2);font-family:var(--font-sans);letter-spacing:.06em;text-transform:uppercase;margin-bottom:16px;display:flex;align-items:center;gap:8px}
.card-title .ct{font-weight:400;color:var(--fg-3);font-size:11px;text-transform:none;letter-spacing:0}
/* ===== GRIDS ===== */
.g2{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px}
.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin-bottom:14px}
.g4{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:14px}
.g4>.card{min-width:0}
.g4>.card table{table-layout:fixed;width:100%}
.g4>.card .ccode{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ===== KPI CARDS ===== */
.kpi-row{display:grid;gap:12px;margin-bottom:20px}
.kpi-row.cols-6{grid-template-columns:repeat(6,1fr)}
.kpi-row.cols-4{grid-template-columns:repeat(4,1fr)}
.kpi-card{background:var(--bg-surface);border:1px solid var(--border-1);border-radius:var(--radius-lg);padding:16px}
.kpi-card .k-label{font-size:10.5px;font-weight:600;color:var(--fg-3);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;font-family:var(--font-text)}
.kpi-card .k-val{font-family:var(--font-sans);font-size:22px;font-weight:700;color:var(--fg-1);line-height:1.15}
.kpi-card .k-sub{font-size:11px;color:var(--fg-2);margin-top:4px}
.kpi-card.primary .k-val{color:var(--dd-primary-900)}
.kpi-card.success .k-val{color:var(--green-700)}
.kpi-card.warning .k-val{color:var(--pumpkin-700)}
.kpi-card.danger .k-val{color:var(--red-700)}
/* ===== TABLES ===== */
.tbl-wrap{border:1px solid var(--border-1);border-radius:var(--radius-lg);overflow:auto}
.tbl-wrap table,.pp-root table{width:100%;border-collapse:collapse;font-size:12.5px;color:var(--fg-1)}
.pp-root thead,.pp-root .tbl-wrap thead{background:var(--bg-app)}
.pp-root thead th,.pp-root .tbl-wrap thead th{padding:9px 12px;text-align:left;font-size:10.5px;font-weight:700;color:var(--fg-3);text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--border-1);white-space:nowrap;font-family:var(--font-text)}
.pp-root tbody tr{border-bottom:1px solid var(--border-1);transition:background .1s}
.pp-root tbody tr:last-child{border-bottom:none}
.pp-root tbody td{padding:9px 12px;vertical-align:middle}
.pp-root tbody tr:hover{background:var(--bg-app)}
.num{text-align:right!important}
.cname{font-size:12.5px;font-weight:500;color:var(--fg-1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:160px}
.ccode{font-size:10.5px;color:var(--fg-3);font-family:var(--font-mono,monospace);margin-top:1px}
.th-tip{cursor:default;position:relative}
.th-tip::after{content:attr(title);display:none;position:absolute;top:100%;left:0;background:var(--fg-1);color:#fff;padding:4px 8px;border-radius:4px;font-size:10px;white-space:nowrap;z-index:100;pointer-events:none}
.th-tip:hover::after{display:block}
/* ===== BADGES ===== */
.badge{display:inline-flex;align-items:center;padding:2px 8px;border-radius:var(--radius-pill);font-size:10.5px;font-weight:600}
.b-acima{background:var(--green-100);color:var(--green-700)}
.b-abaixo{background:var(--red-100);color:var(--red-700)}
.b-warn{background:var(--yellow-100);color:var(--yellow-700)}
.b-exp{background:var(--red-100);color:var(--red-700)}
.b-care{background:color-mix(in oklch,var(--dd-primary-900) 10%,transparent);color:var(--dd-primary-900)}
.b-careplus{background:var(--purple-100);color:var(--purple-700)}
.b-nocare{background:var(--bg-app);color:var(--fg-3);border:1px solid var(--border-1)}
.b-neutral{background:var(--bg-app);color:var(--fg-2)}
.b-zero{background:var(--bg-app);color:var(--fg-3)}
.bar-pct{display:inline-block;width:48px;height:4px;border-radius:2px;background:var(--border-1);vertical-align:middle;position:relative;overflow:hidden}
.bf{position:absolute;left:0;top:0;height:100%;border-radius:2px}
.bf-g{background:var(--green-700)}
.bw{background:var(--pumpkin-700)!important}
.bb{background:var(--red-700)!important}
.pct-g{color:var(--green-700);font-weight:700}
.alert-pill,.pill-crit,.pill-exp,.pill-warn,.pill-info{display:inline-flex;align-items:center;padding:1px 7px;border-radius:var(--radius-pill);font-size:10px;font-weight:700}
.pill-crit{background:var(--red-100);color:var(--red-700)}
.pill-exp{background:var(--pumpkin-100);color:var(--pumpkin-700)}
.pill-warn{background:var(--yellow-100);color:var(--yellow-700)}
.pill-info{background:var(--bg-app);color:var(--fg-2);border:1px solid var(--border-1)}
/* ===== FILTER ROW ===== */
.frow{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px}
.frow input,.frow select,.filter-row input,.filter-row select{height:32px;padding:0 10px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12.5px;color:var(--fg-1);background:var(--bg-surface);outline:none;font-family:var(--font-text)}
.frow input:focus,.frow select:focus,.filter-row input:focus,.filter-row select:focus{border-color:var(--dd-primary-600);box-shadow:0 0 0 2px color-mix(in oklch,var(--dd-primary-600) 15%,transparent)}
.filter-row td{padding:8px 12px;border-bottom:1px solid var(--border-1)}
.rc{font-size:11px;color:var(--fg-3);font-family:var(--font-mono,monospace);padding:0 4px}
/* ===== BUTTONS ===== */
.fb{height:30px;padding:0 12px;background:var(--bg-surface);color:var(--fg-2);border:1px solid var(--border-1);border-radius:var(--radius-sm);cursor:pointer;font-size:12px;font-family:var(--font-text);transition:all .15s}
.fb:hover{background:var(--bg-app);color:var(--fg-1)}
.fb.on{background:var(--dd-primary-900);color:#fff;border-color:var(--dd-primary-900)}
.fb.on-r{background:var(--red-100);color:var(--red-700);border-color:color-mix(in oklch,var(--red-700) 30%,transparent)}
.fb.on-g{background:var(--green-100);color:var(--green-700);border-color:color-mix(in oklch,var(--green-700) 30%,transparent)}
.fb.on-o{background:var(--pumpkin-100);color:var(--pumpkin-700);border-color:color-mix(in oklch,var(--pumpkin-700) 30%,transparent)}
.btn-primary{background:var(--dd-primary-900);color:#fff;border:none;border-radius:var(--radius-sm);padding:6px 14px;cursor:pointer;font-size:12px;font-weight:600;display:inline-flex;align-items:center;gap:6px;font-family:var(--font-text)}
.btn-primary:hover{opacity:.9}
.btn-secondary{background:var(--bg-surface);color:var(--fg-1);border:1px solid var(--border-1);border-radius:var(--radius-sm);padding:6px 12px;cursor:pointer;font-size:12px;display:inline-flex;align-items:center;gap:6px;font-family:var(--font-text)}
.btn-secondary:hover{background:var(--bg-app)}
.btn-analisar{height:26px;padding:0 10px;background:var(--bg-app);color:var(--dd-primary-900);border:1px solid var(--border-1);border-radius:var(--radius-sm);cursor:pointer;font-size:11px;font-weight:600;white-space:nowrap}
.btn-analisar:hover{background:var(--dd-primary-900);color:#fff;border-color:var(--dd-primary-900)}
.btn-icon{width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;background:var(--bg-app);border:1px solid var(--border-1);border-radius:var(--radius-sm);cursor:pointer;color:var(--fg-2)}
.btn-icon:hover{background:var(--border-1)}
.pp-btn-mini{height:24px;padding:0 8px;font-size:10.5px;border:1px solid var(--border-1);border-radius:var(--radius-sm);cursor:pointer;background:var(--bg-surface);color:var(--fg-2)}
.pp-btn-mini:hover{background:var(--bg-app);color:var(--fg-1)}
.pp-btn-mini.danger:hover{background:var(--red-100);color:var(--red-700);border-color:color-mix(in oklch,var(--red-700) 30%,transparent)}
.time-label{font-size:11px;color:var(--fg-3)}
.tm-btn{height:30px;padding:0 12px;background:var(--bg-surface);color:var(--fg-2);border:1px solid var(--border-1);border-radius:var(--radius-sm);cursor:pointer;font-size:12px}
.tm-btn.active{background:var(--dd-primary-900);color:#fff;border-color:var(--dd-primary-900)}
/* ===== OVERVIEW — HEALTH + RAG SIDE-BY-SIDE ===== */
.ov-health-rag-layout{display:grid;grid-template-columns:240px 1fr;gap:0;align-items:start}
.ov-health-side{padding-right:24px;border-right:1px solid var(--border-1)}
.ov-rag-side{padding-left:24px}
.ov-health-body{display:flex;flex-direction:column;align-items:center;gap:4px}
.ov-gauge-wrap svg{display:block;width:180px;height:auto}
.ov-health-score{font-family:var(--font-sans);font-size:32px;font-weight:700;line-height:1;text-align:center}
.ov-health-label{font-size:13px;font-weight:600;margin-top:2px;text-align:center}
.ov-health-desc{font-size:12px;color:var(--fg-2);margin-top:4px}
.ov-health-text{display:flex;flex-direction:column;align-items:center}
/* ===== OVERVIEW — RAG BARS ===== */
.rag-row{display:flex;align-items:center;gap:12px;padding:9px 0;border-bottom:1px solid var(--border-1)}
.rag-row:last-child{border-bottom:none}
.rag-label{font-size:12.5px;color:var(--fg-1);width:210px;flex-shrink:0}
.rag-track{flex:1;height:18px;border-radius:4px;overflow:hidden;display:flex;background:var(--bg-app)}
.rag-seg{height:100%;transition:opacity .15s}
.rag-seg:hover,.rag-seg[onclick]:hover{opacity:.8}
.rag-total{font-size:11px;color:var(--fg-3);font-family:var(--font-mono,monospace)}
.rag-count{display:flex;gap:4px;font-size:12px;font-family:var(--font-mono,monospace);min-width:70px;justify-content:flex-end}
/* ===== OVERVIEW — RISK TABLE ===== */
.ov-risk-table{width:100%;border-collapse:collapse;font-size:12.5px}
.ov-risk-table th{padding:8px 12px;text-align:left;font-size:10.5px;font-weight:700;color:var(--fg-3);text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--border-1)}
.ov-risk-table td{padding:9px 12px;border-bottom:1px solid var(--border-1);color:var(--fg-1)}
.ov-risk-table tr:last-child td{border-bottom:none}
.ov-risk-table th.num,.ov-risk-table td.num{text-align:right}
.ov-risk-cell{font-weight:700;font-family:var(--font-mono,monospace);font-size:13px}
.ov-risk-cell:hover{text-decoration:underline}
/* ===== OVERVIEW — DRILLDOWN ===== */
.ov-drill-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.ov-drill-title{font-size:13px;font-weight:600;color:var(--fg-1)}
.ov-drill-title .ct{font-size:11px;font-weight:400;color:var(--fg-3)}
.ov-drill-close{width:28px;height:28px;display:inline-flex;align-items:center;justify-content:center;background:var(--bg-app);border:1px solid var(--border-1);border-radius:50%;cursor:pointer;color:var(--fg-2);font-size:13px}
.ov-drill-close:hover{background:var(--border-1)}
.ov-drill-empty{text-align:center;padding:32px;color:var(--fg-3);font-size:13px}
/* ===== VENDOR BARS ===== */
.vbar{display:flex;align-items:center;gap:12px;padding:8px 0;border-bottom:1px solid var(--border-1)}
.vbar:last-child{border-bottom:none}
.vname{font-size:12px;color:var(--fg-1);width:90px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.vtracks{flex:1;min-width:0}
.vtrack-label{font-size:10.5px;color:var(--fg-3);margin-bottom:4px}
.vtrack-bar{height:10px;border-radius:5px;background:var(--bg-app);overflow:hidden;position:relative}
.vtrack-fill{height:100%;border-radius:5px;transition:width .3s}
.vpct-badge{font-size:12px;font-weight:700;font-family:var(--font-mono,monospace);min-width:40px;text-align:right}
/* ===== OVERVIEW METRICS ===== */
.ov-metric{margin-bottom:12px}
.ov-metric label{display:flex;justify-content:space-between;font-size:12.5px;margin-bottom:5px;color:var(--fg-1)}
.ov-metric label strong{font-weight:600}
.gauge{height:6px;border-radius:3px;background:var(--bg-app);overflow:hidden}
.gauge-fill{height:100%;border-radius:3px;transition:width .3s}
/* ===== M2 TAB ===== */
.m2-group-row{background:color-mix(in oklch,var(--dd-primary-900) 5%,transparent);border-left:3px solid var(--dd-primary-600,#3859D0);transition:background .15s}
.m2-group-row td{padding:9px 12px}
.m2-group-row:hover{background:color-mix(in oklch,var(--dd-primary-900) 9%,transparent)}
.m2-group-row .cname{font-weight:700;color:var(--fg-1)}
.m2-detail-row td:first-child{padding-left:28px}
.m2-detail-row:hover{background:var(--bg-app)}
.m2-detail-row .cname{color:var(--fg-2)}
.m2-detail-row .ccode{color:var(--fg-3)}
.m2-group-count{font-size:11px;color:var(--fg-3);font-family:var(--font-mono,monospace)}
.m2-arrow-chip{font-size:10px;color:var(--dd-primary-600);margin-right:4px;transition:transform .15s;display:inline-block}
.m2-arrow-chip.is-open{transform:rotate(90deg)}
.ratio-stat{display:inline-flex;flex-direction:column;align-items:flex-end}
.ratio-nums{font-family:var(--font-mono,monospace);font-size:12px}
.ratio-sep{color:var(--fg-3);margin:0 2px}
.ratio-esp{color:var(--fg-3)}
.ratio-unit{font-size:10px;color:var(--fg-3);font-family:var(--font-mono,monospace)}
.interval-col{font-size:11px;color:var(--fg-3);font-family:var(--font-mono,monospace)}
.cell-stat{display:inline-block;position:relative}
.cell-stat::after{content:'';position:absolute;bottom:-2px;left:0;width:100%;height:2px;border-radius:1px;background:currentColor;opacity:.3}
/* ===== ALERT TAB ===== */
.alert-item{padding:14px 16px;border-bottom:1px solid var(--border-1);background:var(--bg-surface)}
.alert-item:last-child{border-bottom:none}
.alert-item.crit{border-left:3px solid var(--red-700)}
.alert-item.warn{border-left:3px solid var(--pumpkin-700)}
.alert-item.exp{border-left:3px solid var(--yellow-700)}
.alert-item.info{border-left:3px solid var(--dd-primary-600)}
.alert-item.pc{border-left:3px solid var(--purple-700)}
.alert-item.pc .alert-body span{color:var(--purple-700)}
.alert-body{font-size:12.5px;color:var(--fg-1);line-height:1.5}
.alert-body span{font-weight:600}
.alert-body strong{color:var(--fg-1);font-weight:700}
.pc-del{width:24px;height:24px;display:inline-flex;align-items:center;justify-content:center;background:none;border:1px solid var(--border-1);border-radius:50%;cursor:pointer;color:var(--fg-3);font-size:12px}
.pc-del:hover{background:var(--red-100);color:var(--red-700);border-color:color-mix(in oklch,var(--red-700) 30%,transparent)}
.pc-form{margin-top:12px;padding:12px;background:var(--bg-app);border-radius:var(--radius-md)}
.pc-form select,.pc-form input[type=text]{height:30px;padding:0 8px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12px;background:var(--bg-surface);color:var(--fg-1)}
.pc-form select:focus,.pc-form input[type=text]:focus,.pc-form textarea:focus{border-color:var(--dd-primary-600);outline:none}
.pc-form textarea{padding:6px 8px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12px;width:100%;resize:vertical;background:var(--bg-surface);color:var(--fg-1)}
/* ===== RENTING TAB ===== */
.pp-contract-card{background:var(--bg-surface);border:1px solid var(--border-1);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:12px}
.pp-contract-card:last-child{margin-bottom:0}
.pp-contract-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;border-bottom:1px solid var(--border-1);background:var(--bg-app)}
.pp-contract-code{font-family:var(--font-mono,monospace);font-size:11.5px;font-weight:700;color:var(--dd-primary-900)}
.pp-contract-sub{font-size:11px;color:var(--fg-3)}
.pp-contract-body{padding:14px 16px}
.pp-field label{font-size:11px;font-weight:600;color:var(--fg-2);display:block;margin-bottom:4px}
.pp-field select,.pp-field input[type="number"]{height:30px;padding:0 8px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12.5px;color:var(--fg-1);background:var(--bg-surface);outline:none}
.pp-field select:focus,.pp-field input[type="number"]:focus{border-color:var(--dd-primary-600);box-shadow:0 0 0 2px color-mix(in oklch,var(--dd-primary-600) 15%,transparent)}
.pp-field select.pp-undefined{color:var(--fg-3)}
.pp-renting-cmp{font-size:12px;color:var(--fg-2);line-height:1.5}
.pp-renting-cmp strong{color:var(--fg-1);font-weight:700}
/* ===== REPO TAB ===== */
.repo-suggest-list{display:flex;flex-direction:column;gap:4px;margin-top:8px}
.repo-suggest-item{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:var(--radius-sm);background:var(--bg-app);font-size:12px;color:var(--fg-2)}
.repo-suggest-item.signed{background:var(--green-100);color:var(--green-700)}
.repo-suggest-item.signed .repo-file{color:var(--green-700)}
.repo-suggest-item .repo-folder{font-size:11px;color:var(--fg-3)}
.repo-suggest-item .repo-file{font-weight:500;color:var(--fg-1)}
.repo-suggest-empty{text-align:center;padding:24px;color:var(--fg-3);font-size:12.5px}
.repo-section-label{font-size:10.5px;font-weight:700;color:var(--fg-3);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}
.pp-attach-empty{text-align:center;padding:20px;color:var(--fg-3);font-size:12px;display:flex;flex-direction:column;align-items:center;gap:6px}
.pp-attach-empty span{display:block}
.pp-attach-file{display:flex;align-items:center;gap:8px;padding:8px 12px;border:1px solid var(--border-1);border-radius:var(--radius-sm);margin-bottom:6px;background:var(--bg-surface)}
.pp-attach-file .pp-af-info{flex:1;min-width:0}
.pp-attach-file .pp-af-info span{display:block;font-size:11.5px;color:var(--fg-3)}
/* ===== COMM TAB ===== */
.comm-layout{display:flex;height:100%}
.comm-sidebar{width:280px;flex-shrink:0;border-right:1px solid var(--border-1);display:flex;flex-direction:column;overflow:hidden;background:var(--bg-surface)}
.comm-sidebar-header{padding:14px 16px;border-bottom:1px solid var(--border-1)}
.comm-sidebar-header h3{font-size:13px;font-weight:600;color:var(--fg-1);margin-bottom:8px}
.comm-sidebar-header input{width:100%;height:30px;padding:0 10px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12px;color:var(--fg-1);background:var(--bg-app);outline:none}
.comm-sidebar-header input:focus{border-color:var(--dd-primary-600)}
.comm-client-list{flex:1;overflow-y:auto;padding:8px 0}
.comm-client-item{display:flex;align-items:flex-start;gap:10px;padding:10px 16px;cursor:pointer;transition:background .1s}
.comm-client-item:hover{background:var(--bg-app)}
.comm-client-item.active{background:color-mix(in oklch,var(--dd-primary-900) 6%,transparent)}
.comm-client-item:last-child{border-bottom:none}
.comm-client-dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;margin-top:5px}
.comm-client-name{font-size:12.5px;font-weight:500;color:var(--fg-1)}
.comm-client-meta{font-size:11px;color:var(--fg-3);margin-top:1px}
.comm-client-flags{display:flex;gap:4px;margin-top:4px;flex-wrap:wrap}
.comm-main{flex:1;display:flex;flex-direction:column;overflow:hidden;background:var(--bg-app)}
.comm-toolbar{display:flex;align-items:center;justify-content:space-between;padding:12px 20px;border-bottom:1px solid var(--border-1);background:var(--bg-surface)}
.comm-toolbar h3{font-size:13px;font-weight:600;color:var(--fg-1)}
.email-preview-wrap{flex:1;overflow-y:auto;padding:20px;min-height:0}
/* ===== EMPTY STATE ===== */
.empty-state{text-align:center;padding:48px 24px;color:var(--fg-3)}
.empty-state svg{margin-bottom:12px;opacity:.4}
.empty-state h4{font-size:14px;font-weight:600;color:var(--fg-2);margin-bottom:6px}
.empty-state p{font-size:12.5px;line-height:1.6}
/* ===== ANALYSIS MODAL ===== */
.an-overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:1000;display:flex;align-items:flex-start;justify-content:flex-end;opacity:0;pointer-events:none;transition:opacity .2s}
.an-overlay.open{opacity:1;pointer-events:all}
.an-modal{width:540px;height:100%;background:var(--bg-surface);box-shadow:-4px 0 32px rgba(0,0,0,.12);display:flex;flex-direction:column;overflow:hidden}
.an-close{width:32px;height:32px;display:inline-flex;align-items:center;justify-content:center;background:var(--bg-app);border:1px solid var(--border-1);border-radius:50%;cursor:pointer;color:var(--fg-2);font-size:14px}
.an-close:hover{background:var(--border-1)}
.an-header{padding:20px 24px;border-bottom:1px solid var(--border-1)}
.an-header h3{font-size:15px;font-weight:700;color:var(--fg-1)}
.an-header .an-sub{font-size:12px;color:var(--fg-3);margin-top:3px}
.an-tabs-row{padding:0 24px;border-bottom:1px solid var(--border-1)}
.an-tabs{display:flex}
.an-tab{height:40px;padding:0 14px;background:none;border:none;border-bottom:2px solid transparent;cursor:pointer;font-size:12.5px;font-weight:500;color:var(--fg-2);margin-bottom:-1px}
.an-tab.active{color:var(--fg-1);border-bottom-color:var(--dd-primary-900);font-weight:600}
.an-tab:hover{color:var(--fg-1)}
.an-body{flex:1;overflow-y:auto;padding:20px 24px}
.an-section{margin-bottom:20px}
.an-section:last-child{margin-bottom:0}
.an-section-title{font-size:11px;font-weight:700;color:var(--fg-3);text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px}
.an-section table{width:100%;border-collapse:collapse;font-size:12.5px}
.an-section thead th{padding:6px 10px;text-align:left;font-size:10.5px;font-weight:700;color:var(--fg-3);border-bottom:1px solid var(--border-1)}
.an-section td{padding:8px 10px;border-bottom:1px solid var(--border-1)}
.an-info-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.an-info-section{margin-bottom:16px}
.an-info-section-title{font-size:11px;font-weight:700;color:var(--fg-3);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px}
.an-info-section:last-of-type{margin-bottom:0}
.an-info-item{margin-bottom:8px}
.an-info-label{font-size:11px;color:var(--fg-3)}
.an-info-value{font-size:12.5px;font-weight:500;color:var(--fg-1);margin-top:2px}
.an-info-note{font-size:11px;color:var(--fg-3);margin-top:2px;font-style:italic}
.an-contract-alert{padding:12px;background:var(--bg-app);border-radius:var(--radius-sm);border-left:3px solid var(--red-700);margin-bottom:10px}
.an-contract-alert-title{font-size:12px;font-weight:700;color:var(--red-700)}
.an-contract-alert-text{font-size:12px;color:var(--fg-1);margin-top:4px;line-height:1.5}
.an-contract-alert-list{margin-top:6px;padding-left:16px}
.an-contract-alert-list li{font-size:11.5px;color:var(--fg-2);line-height:1.6}
.an-contract-alert-list li:last-child{margin-bottom:0}
.an-fin-yearrow{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.an-fin-yearrow label{font-size:11.5px;color:var(--fg-2)}
.an-fin-yearrow select{height:28px;padding:0 8px;border:1px solid var(--border-1);border-radius:var(--radius-sm);font-size:12px;background:var(--bg-surface);color:var(--fg-1)}
.an-fin-yearrow select:focus{border-color:var(--dd-primary-600);outline:none}
.an-fin-note{font-size:11.5px;color:var(--fg-3);line-height:1.5}
.an-total-row td{font-weight:700;border-top:2px solid var(--border-1)}
.an-latest-badge{display:inline-flex;align-items:center;padding:1px 6px;border-radius:var(--radius-pill);font-size:9.5px;background:var(--dd-primary-900);color:#fff;margin-left:4px;font-family:var(--font-mono,monospace)}
.an-latest-row{background:color-mix(in oklch,var(--dd-primary-900) 4%,transparent)}
.an-chart-wrap{overflow-x:auto;margin-bottom:12px}
.an-chart-lg{display:flex;align-items:flex-end;gap:5px;height:100px;padding-bottom:4px}
.an-chart-lg i{flex:1;border-radius:3px 3px 0 0;background:var(--dd-primary-600,#3859D0);display:block;min-height:2px}
.an-chart-lg i.dashed{background:repeating-linear-gradient(135deg,var(--dd-primary-600,#3859D0),var(--dd-primary-600,#3859D0) 4px,transparent 4px,transparent 8px)}
.an-chart-legend{display:flex;gap:12px;font-size:11px;color:var(--fg-3);flex-wrap:wrap}
.an-avg-card{background:var(--bg-app);border-radius:var(--radius-sm);padding:12px;margin-bottom:12px}
.an-avg-body{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.an-avg-label{font-size:10.5px;color:var(--fg-3);text-transform:uppercase;font-weight:600}
.an-avg-value{font-size:18px;font-weight:700;color:var(--fg-1);font-family:var(--font-sans);line-height:1}
.an-avg-pct{font-size:12px;font-weight:700;margin-top:2px}
.an-avg-vs{font-size:11px;color:var(--fg-3);margin-top:2px}
.an-analyst{padding:10px 12px;border-radius:var(--radius-sm);margin-bottom:8px;border-left:3px solid transparent}
.an-analyst-label{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;margin-bottom:3px}
.an-analyst-text{font-size:12.5px;line-height:1.5}
.an-analyst.t-verde{background:var(--green-100);border-left-color:var(--green-700)}
.an-analyst.t-verde .an-analyst-label{color:var(--green-700)}
.an-analyst.t-amarelo{background:var(--yellow-100);border-left-color:var(--yellow-700)}
.an-analyst.t-amarelo .an-analyst-label{color:var(--yellow-700)}
.an-analyst.t-vermelho{background:var(--red-100);border-left-color:var(--red-700)}
.an-analyst.t-vermelho .an-analyst-label{color:var(--red-700)}
.an-empty-note{text-align:center;padding:24px;color:var(--fg-3);font-size:12.5px}
/* ===== EMAIL ===== */
.email-outer{max-width:640px;margin:0 auto}
.email-meta{padding:12px 16px;background:var(--bg-app);border-radius:var(--radius-md);margin-bottom:12px;font-size:12px;color:var(--fg-2);display:flex;flex-direction:column;gap:4px;border:1px solid var(--border-1)}
.email-meta strong{color:var(--fg-1)}
.email-frame{border:1px solid var(--border-1);border-radius:var(--radius-lg);overflow:hidden;background:#fff;font-family:Georgia,'Times New Roman',serif}
.email-header-band{background:var(--dd-primary-900);padding:24px 32px;color:#fff}
.email-header-band .co-name{font-size:11px;font-weight:700;color:rgba(255,255,255,.5);letter-spacing:.1em;text-transform:uppercase;margin-bottom:6px}
.email-header-band h2{font-size:18px;font-weight:700;color:#fff;margin-bottom:4px}
.email-header-band .subtitle{color:rgba(255,255,255,.6);font-size:12px}
.email-body{padding:28px 32px}
.email-salutation{font-size:14px;margin-bottom:12px}
.email-intro{font-size:13px;line-height:1.65;margin-bottom:20px;color:#444}
.email-contract-block{border:1px solid #e0e0e0;border-radius:8px;overflow:hidden;margin-bottom:16px}
.ecb-header{padding:12px 16px;background:#f5f5f5;border-bottom:1px solid #e0e0e0;display:flex;align-items:baseline;justify-content:space-between}
.ecb-header .ecb-title{font-size:13px;font-weight:700;color:#1a1a2e}
.ecb-header .ecb-dates{font-size:11px;color:#888}
.ecb-body{padding:16px}
.ecb-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px;margin-bottom:12px}
.ecb-stat{padding:10px 12px;border-radius:6px;background:#f9f9f9;border:1px solid #efefef}
.ecb-stat.bad{background:#fff5f5;border-color:#ffd0d0}
.ecb-stat.good{background:#f0fff8;border-color:#c3e6d4}
.ecb-stat.warn{background:#fffbf0;border-color:#fae5a0}
.ecb-stat .es-label{font-size:10.5px;font-weight:700;text-transform:uppercase;color:#888;letter-spacing:.04em;margin-bottom:4px}
.ecb-stat .es-value{font-size:14px;font-weight:700;color:#1a1a2e}
.ecb-stat .es-sub{font-size:11px;color:#666;margin-top:2px}
.ecb-progress{margin-bottom:12px}
.ecb-progress .ep-label{display:flex;justify-content:space-between;font-size:11px;color:#888;margin-bottom:4px}
.ecb-progress .ep-bar{height:8px;background:#eee;border-radius:4px;overflow:hidden}
.ecb-progress .ep-fill{height:100%;border-radius:4px}
.ecb-alert{display:flex;align-items:center;gap:12px;padding:12px;border-radius:6px;font-size:12.5px;margin-top:12px}
.ecb-alert.crit{background:#fff0f0;border:1px solid #ffcccc;color:#7a2020}
.ecb-alert.exp{background:#fff8e6;border:1px solid #fde68a;color:#7a5000}
.ecb-alert.warn{background:#fffbf0;border:1px solid #fae5a0;color:#7a5f10}
.ecb-alert strong{font-weight:700}
.ecb-focus-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:12px}
.ecb-focus-card{background:#fff;border:1px solid #e0e0e0;border-radius:6px;padding:10px 14px}
.ecb-focus-label{font-size:10.5px;color:#888;text-transform:uppercase;font-weight:700;letter-spacing:.04em;margin-bottom:4px}
.ecb-focus-val{font-size:16px;font-weight:700;color:#7a2020}
.ecb-focus-sub{font-size:11px;color:#888;margin-top:2px}
.email-closing{margin-top:24px;padding-top:16px;border-top:1px solid #eee;font-size:13px;line-height:1.8;color:#444}
.email-footer-band{display:flex;justify-content:space-between;align-items:center;padding:14px 32px;background:#f5f6f8;border-top:1px solid #e0e0e0}
.email-footer-band .ef-company{font-size:12px;font-weight:700;color:#1a1a2e}
.email-footer-band .ef-contact{font-size:11px;color:#888}
/* ===== RESPONSIVE ===== */
@media(max-width:1100px){
  .kpi-row.cols-6{grid-template-columns:repeat(3,1fr)}
  .g2,.g3{grid-template-columns:1fr}
  .ov-banner{grid-template-columns:repeat(3,1fr)}
  .ov-health-rag-layout{grid-template-columns:1fr}
  .ov-health-side{padding-right:0;border-right:none;padding-bottom:16px;border-bottom:1px solid var(--border-1)}
  .ov-rag-side{padding-left:0;padding-top:16px}
}
/* ===== PRINT ===== */
@media print {
  .comm-sidebar-header,nav,button{display:none!important}
  .an-overlay{display:none!important}
  .pp-root .pp-tabnav{display:none!important}
  .comm-sidebar{display:none!important}
  .comm-main{width:100%!important}
  .pp-tab-content{overflow:visible!important;height:auto!important}
}
`;

(function ppInjectCSS(){
  if(document.getElementById('pp-styles'))return;
  const s=document.createElement('style');s.id='pp-styles';
  s.textContent=_PP_CSS;document.head.appendChild(s);
})();

/* ===== RENTING localStorage ===== */
const PP_RENTING_KEY='pp_renting_overrides';
const PP_RENTING_VALOR_KEY='pp_renting_valor_overrides';
function ppLoadRentingOverrides(){
  let saved={};try{saved=JSON.parse(localStorage.getItem(PP_RENTING_KEY)||'{}');}catch(e){}
  let savedVal={};try{savedVal=JSON.parse(localStorage.getItem(PP_RENTING_VALOR_KEY)||'{}');}catch(e){}
  DATA.forEach(r=>{
    let t=saved[r.contrato]||null;
    if(t==='Renting Externo - Digirent')t='Renting Externo';
    r.tipo_renting=t;r.renting_valor=savedVal[r.contrato]!=null?savedVal[r.contrato]:null;
  });
}
function ppSetRentingType(contrato,value){
  const rec=DATA.find(r=>r.contrato===contrato);if(rec)rec.tipo_renting=value||null;
  let s={};try{s=JSON.parse(localStorage.getItem(PP_RENTING_KEY)||'{}');}catch(e){}
  if(value)s[contrato]=value;else delete s[contrato];
  localStorage.setItem(PP_RENTING_KEY,JSON.stringify(s));
}
function ppSetRentingValor(contrato,value){
  const rec=DATA.find(r=>r.contrato===contrato);if(rec)rec.renting_valor=value;
  let s={};try{s=JSON.parse(localStorage.getItem(PP_RENTING_VALOR_KEY)||'{}');}catch(e){}
  if(value!=null)s[contrato]=value;else delete s[contrato];
  localStorage.setItem(PP_RENTING_VALOR_KEY,JSON.stringify(s));
}

/* ===== IndexedDB attachments ===== */
let ppDB=null;
function ppDBOpen(){
  return new Promise((resolve,reject)=>{
    if(ppDB){resolve(ppDB);return;}
    const req=indexedDB.open('printplan_attachments',1);
    req.onupgradeneeded=e=>{const db=e.target.result;if(!db.objectStoreNames.contains('files'))db.createObjectStore('files',{keyPath:'contrato'});};
    req.onsuccess=e=>{ppDB=e.target.result;resolve(ppDB);};
    req.onerror=e=>reject(e.target.error);
  });
}
function ppAttachGet(c){return ppDBOpen().then(db=>new Promise((res,rej)=>{const t=db.transaction('files','readonly');const r=t.objectStore('files').get(c);r.onsuccess=()=>res(r.result);r.onerror=()=>res(null);}));}
function ppAttachPut(rec){return ppDBOpen().then(db=>new Promise((res,rej)=>{const t=db.transaction('files','readwrite');const r=t.objectStore('files').put(rec);r.onsuccess=()=>res();r.onerror=()=>rej(r.error);}));}
function ppAttachDelete(c){return ppDBOpen().then(db=>new Promise((res,rej)=>{const t=db.transaction('files','readwrite');const r=t.objectStore('files').delete(c);r.onsuccess=()=>res();r.onerror=()=>rej(r.error);}));}

/* ===== Helpers ===== */
function fmt(n,dec=0){const num=Number(n);const neg=num<0;const parts=Math.abs(num).toFixed(dec).split('.');parts[0]=parts[0].replace(/\B(?=(\d{3})+(?!\d))/g,' ');return(neg?'-':'')+(dec>0?parts.join(','):parts[0]);}
function fmtE(n){if(n>=1000000)return'€'+fmt(n/1000000,1)+'M';if(n>=1000)return'€'+fmt(n/1000,1)+'K';return'€'+fmt(n,0)}
function fmtEFull(n){return'€'+fmt(n,2)}
function fmtK(n){if(n>=1000000)return fmt(n/1000000,1)+'M';if(n>=1000)return fmt(n/1000,1)+'K';return fmt(n,0)}
function clamp(v,lo,hi){return Math.max(lo,Math.min(hi,v))}
function desvioColor(pct,sign,tol=15){const eff=(pct||0)*(sign||1);if(eff<0)return'var(--green-700)';if(eff>tol)return'var(--red-700)';return'var(--yellow-700)';}
function shortV(v){return(v||'').replace('DECAL PT ','').replace('Decal PT ','').replace('Decal ES ','ES·').replace('DECAL ES ','');}
function matchText(val,q){if(!q)return true;return String(val==null?'':val).toLowerCase().includes(q.toLowerCase());}
function matchNum(val,q){
  if(!q)return true;q=q.trim();if(val===null||val===undefined||val==='')return false;
  const n=Number(val);let m=q.match(/^(>=|<=|>|<)\s*(-?\d+(?:[.,]\d+)?)$/);
  if(m){const op=m[1],lim=parseFloat(m[2].replace(',','.'));if(op==='>=')return n>=lim;if(op==='<=')return n<=lim;if(op==='>')return n>lim;if(op==='<')return n<lim;}
  m=q.match(/^(-?\d+(?:[.,]\d+)?)\s*-\s*(-?\d+(?:[.,]\d+)?)$/);
  if(m){const lo=parseFloat(m[1].replace(',','.')),hi=parseFloat(m[2].replace(',','.'));return n>=lo&&n<=hi;}
  return String(val).toLowerCase().includes(q.toLowerCase());
}
function uniqueVals(arr,key){return[...new Set(arr.map(r=>r[key]).filter(v=>v!=null&&v!==''))].sort((a,b)=>String(a).localeCompare(String(b)));}
function finMargemColor(m){return m==null?'var(--fg-3)':m<0?'var(--red-700)':'var(--green-700)';}
function finPctTxt(m){return m==null?'—':(m>=0?'+':'')+fmt(m,1)+'%';}

/* ===== Data init (runs once) ===== */
const _ppFM={};
(function ppInitData(){
  FINANCEIRA_DATA.forEach(r=>{_ppFM[r.cliente]=r;});
  const cc={};DATA.forEach(r=>{cc[r.cliente]=(cc[r.cliente]||0)+1;});
  DATA.forEach(r=>{
    const fin=_ppFM[r.cliente];const att=fin&&cc[r.cliente]===1;
    r.fin_valor_liquido=att?fin.valor_liquido:null;
    r.fin_custo_total=att?fin.custo_total:null;
    r.fin_margem_pct=att?fin.margem_pct:null;
  });
  const byC={};DATA.forEach(r=>{(byC[r.cliente]=byC[r.cliente]||[]).push(r);});
  CLIENTS_LIST.forEach(c=>{
    const ct=byC[c.cliente]||[];
    c.has_m2_issue=ct.some(r=>r.novo_pvp!=null);
    c.has_tinta_issue=ct.some(r=>r.tinta_tem_dados&&(desvioColor(r.tinta_desvio_pct,1)==='var(--red-700)'||r.tinta_ml_m2_real===0));
    c.has_suporte_issue=ct.some(r=>r.suporte_tem_dados&&desvioColor(r.suporte_desvio_pct,1)==='var(--red-700)');
  });
  ppLoadRentingOverrides();
})();

/* ===== applyMonthlySnapshot ===== */
function applyMonthlySnapshot(r,rows){
  if(!rows||!rows.length){return{...r,acumulado_m2:0,rendas_decorridas:0,m2_impressos_ultimo:0,data_consumo:null,rec_real:0,esperado_m2:0,m2_desvio:0,m2_desvio_pct:0,m2_status:'ABAIXO',rec_falta:Math.max(0,r.rec_pot),m2_mensal:r.num_rendas?r.limite_m2/r.num_rendas:0,perf_mensal_pct:0,rec_pot_atual:0,perf_atual:0,valor_em_atraso:0,m2_restante:Math.max(0,r.limite_m2),novo_pvp:null};}
  const sorted=[...rows].sort((a,b)=>a[0]<b[0]?-1:a[0]>b[0]?1:0);
  const acumulado_m2=sorted.reduce((s,x)=>s+x[1],0);const rendas_decorridas=sorted.length;const ultimo=sorted[sorted.length-1];
  const rec_real=Math.round(acumulado_m2*r.pvp*100)/100;const esperado_m2=r.num_rendas?Math.round(r.limite_m2/r.num_rendas*rendas_decorridas):0;
  const m2_desvio=acumulado_m2-esperado_m2;const m2_desvio_pct=esperado_m2>0?Math.round((m2_desvio/esperado_m2*100)*10)/10:0;
  const m2_status=m2_desvio>=0?'ACIMA':'ABAIXO';const rec_falta=Math.max(0,Math.round((r.rec_pot-rec_real)*100)/100);
  const m2_mensal=r.num_rendas?r.limite_m2/r.num_rendas:0;const perf_mensal_pct=m2_mensal>0?(ultimo[1]/m2_mensal*100):0;
  const rec_pot_atual=esperado_m2*r.pvp;const perf_atual=rec_real-rec_pot_atual;const valor_em_atraso=Math.max(0,-perf_atual);
  const m2_restante=Math.max(0,r.limite_m2-acumulado_m2);
  const novo_pvp=(r.equip===1&&typeof r.pvp==='number'&&valor_em_atraso>0&&m2_restante>0)?(r.pvp+valor_em_atraso/m2_restante):null;
  return{...r,acumulado_m2,rendas_decorridas,m2_impressos_ultimo:ultimo[1],data_consumo:ultimo[0],rec_real,esperado_m2,m2_desvio,m2_desvio_pct,m2_status,rec_falta,m2_mensal,perf_mensal_pct,rec_pot_atual,perf_atual,valor_em_atraso,m2_restante,novo_pvp};
}

/* ===== ppRentingAnalysis ===== */
function ppRentingAnalysis(r){
  const temValor=r.renting_valor!=null&&r.renting_valor>0;const pvp=r.pvp||0;
  const monthlyRows=MONTHLY_REVENUE[r.contrato]||[];const hasHistory=monthlyRows.length>0;
  const rows=monthlyRows.map(([mes,m2])=>({mes,m2,receita:m2*pvp,margem:temValor?(m2*pvp-r.renting_valor):null}));
  const count=rows.length;let custoAcumulado=null,receitaAcumulada=null,deficitEuro=null,deficitM2=null,status='SEM_VALOR',avgMonthly=null;
  if(hasHistory){receitaAcumulada=rows.reduce((s,row)=>s+row.receita,0);avgMonthly=receitaAcumulada/count;}
  else{avgMonthly=r.rendas_decorridas>0?(r.rec_real/r.rendas_decorridas):0;}
  if(temValor){
    if(hasHistory){custoAcumulado=r.renting_valor*count;deficitEuro=receitaAcumulada-custoAcumulado;deficitM2=pvp>0?((custoAcumulado-receitaAcumulada)/pvp):null;}
    else{deficitEuro=avgMonthly-r.renting_valor;}
    status=deficitEuro>=0?'COBRE':'NAO_COBRE';
  }
  return{temValor,hasHistory,count,custoAcumulado,receitaAcumulada,deficitEuro,deficitM2,status,avgMonthly,rows};
}

/* ===== commNeedsComm ===== */
function commNeedsComm(c){return c.has_m2_issue||c.has_tinta_issue||c.has_suporte_issue;}

/* ===== Overview gauge SVG ===== */
function ovGaugeSVG(score){
  const cx=110,cy=100,r=82,sw=16;
  const pt=(a,rad)=>{const rd=a*Math.PI/180;return[cx+rad*Math.cos(rd),cy-rad*Math.sin(rd)];};
  const arc=(a0,a1)=>{const[x1,y1]=pt(a0,r),[x2,y2]=pt(a1,r);const la=a0-a1>180?1:0;return`M${x1.toFixed(1)},${y1.toFixed(1)} A${r},${r} 0 ${la} 1 ${x2.toFixed(1)},${y2.toFixed(1)}`;};
  const bands=[[180,108,'var(--red-700)'],[108,54,'var(--pumpkin-700)'],[54,0,'var(--green-700)']];
  const bandsHTML=bands.map(([a0,a1,col])=>`<path d="${arc(a0,a1)}" stroke="${col}" stroke-width="${sw}" fill="none"/>`).join('');
  const clamped=clamp(score,0,100);const needleAngle=180-(clamped/100*180);const[nx,ny]=pt(needleAngle,r-26);
  const st=clamped<40?{color:'var(--red-700)'}:clamped<70?{color:'var(--pumpkin-700)'}:{color:'var(--green-700)'};
  return`<svg viewBox="0 0 220 112" width="220" height="112">${bandsHTML}<line x1="${cx}" y1="${cy}" x2="${nx.toFixed(1)}" y2="${ny.toFixed(1)}" stroke="${st.color}" stroke-width="4" stroke-linecap="round"/><circle cx="${cx}" cy="${cy}" r="7" fill="${st.color}"/></svg>`;
}

/* ===== commM2BlockHTML & commConsumoBlockHTML ===== */
function commM2BlockHTML(r){
  const util=r.limite_m2>0?(r.acumulado_m2/r.limite_m2*100):0;const progPct=Math.min(util,100);
  return`<div class="email-contract-block">
    <div class="ecb-header"><div class="ecb-title">Contrato ${r.contrato}</div><div class="ecb-dates">${r.data_inicio} → ${r.data_termino} · ${r.rendas_decorridas}/${r.num_rendas} meses</div></div>
    <div class="ecb-body">
      <div class="ecb-progress"><div class="ep-label"><span>M² Impressos: ${fmt(r.acumulado_m2)}</span><span>Esperado: ${fmt(r.esperado_m2)}</span></div><div class="ep-bar"><div class="ep-fill" style="width:${progPct}%;background:#B53B3B"></div></div></div>
      <div class="ecb-grid"><div class="ecb-stat bad"><div class="es-label">Défice face ao Contratado</div><div class="es-value">${fmt(r.m2_desvio)} m² (${fmt(r.m2_desvio_pct,1)}%)</div><div class="es-sub">${fmt(r.acumulado_m2)} m² impressos de ${fmt(r.esperado_m2)} m² esperados</div></div></div>
      <div class="ecb-alert crit" style="flex-direction:column;align-items:stretch;gap:0;background:var(--grey-50);border:1px solid var(--border-1)">
        <strong>Défice de M² Impressos face ao contratualizado</strong>
        <div class="ecb-focus-grid"><div class="ecb-focus-card"><div class="ecb-focus-label">Novo PVP Recalculado</div><div class="ecb-focus-val">${fmtEFull(r.novo_pvp)}/m²</div><div class="ecb-focus-sub">Atual: ${fmtEFull(r.pvp)}/m²</div></div><div class="ecb-focus-card"><div class="ecb-focus-label">Valor em Atraso</div><div class="ecb-focus-val">${fmtEFull(r.valor_em_atraso)}</div><div class="ecb-focus-sub">${fmt(Math.abs(r.m2_desvio))} m² × ${fmtEFull(r.pvp)}/m²</div></div></div>
      </div>
    </div>
  </div>`;
}
function commConsumoBlockHTML(r){
  const blocks=[];
  if(r.tinta_tem_dados&&desvioColor(r.tinta_desvio_pct,1)==='var(--red-700)'){blocks.push(`<div class="ecb-stat bad"><div class="es-label">Consumo Tinta</div><div class="es-value">${fmt(r.tinta_ml_m2_real,1)} ml/m²</div><div class="es-sub">Esperado: ${fmt(r.tinta_ml_m2_esp,1)} ml/m² · ${r.tinta_desvio_pct>=0?'+':''}${fmt(r.tinta_desvio_pct,1)}% acima</div></div>`);}
  if(r.suporte_tem_dados&&desvioColor(r.suporte_desvio_pct,1)==='var(--red-700)'){blocks.push(`<div class="ecb-stat bad"><div class="es-label">Consumo Suportes</div><div class="es-value">${fmt(r.suporte_consumo_mt)} mt</div><div class="es-sub">Impresso: ${fmt(r.suporte_impresso_mt)} mt</div></div>`);}
  return`<div class="email-contract-block"><div class="ecb-header"><div class="ecb-title">Contrato ${r.contrato}</div><div class="ecb-dates">${r.data_inicio} → ${r.data_termino}</div></div><div class="ecb-body"><div class="ecb-grid">${blocks.join('')}</div></div></div>`;
}
function buildEmailHTML(clientName,topic,data){
  const contratos=data.filter(r=>r.cliente===clientName);if(!contratos.length)return'';
  const c=contratos[0];const dateStr=new Date().toLocaleDateString('pt-PT',{day:'2-digit',month:'long',year:'numeric'});
  const m2ct=contratos.filter(r=>r.novo_pvp!=null);const consumoCt=contratos.filter(r=>{const t=r.tinta_tem_dados&&(desvioColor(r.tinta_desvio_pct,1)==='var(--red-700)'||r.tinta_ml_m2_real===0);const s=r.suporte_tem_dados&&desvioColor(r.suporte_desvio_pct,1)==='var(--red-700)';return t||s;});
  const active=topic==='m2'?m2ct:consumoCt;if(!active.length)return'';
  const blocks=active.map(topic==='m2'?commM2BlockHTML:commConsumoBlockHTML).join('');
  const subj=topic==='m2'?`Retificação de PVP — Défice de M² Impressos`:`Controlo de Consumos — Tintas/Suportes`;
  const intro=topic==='m2'?`Vimos por este meio dar-lhe conhecimento da situação do${active.length>1?'s':''} seu${active.length>1?'s':''} contrato${active.length>1?'s':''} PrintPlan relativamente ao volume de m² impressos, à data de <strong>${dateStr}</strong>.`:`Partilhamos o ponto de situação do controlo de consumos do contrato PrintPlan à data de <strong>${dateStr}</strong>.`;
  return`<div class="email-outer"><div class="email-meta"><div><strong>Para:</strong> [email do cliente]</div><div><strong>De:</strong> ${shortV(c.vendedor)} · Decal PrintPlan</div><div><strong>Assunto:</strong> ${subj}</div><div><strong>Data:</strong> ${dateStr}</div></div><div class="email-frame" id="email-print-zone"><div class="email-header-band"><div class="co-name">Decal PrintPlan</div><h2>${subj}</h2><div class="subtitle">${dateStr}</div></div><div class="email-body"><div class="email-salutation">Exmo(a). Sr./Sra.,</div><div class="email-intro">${intro}</div>${blocks}<div class="email-closing">Com os melhores cumprimentos,<br><strong>${shortV(c.vendedor)}</strong><br>Equipa Decal · PrintPlan<br><span style="font-size:11px;color:var(--fg-3)">printplan@decal.pt</span></div></div><div class="email-footer-band"><div><div class="ef-company">Decal · PrintPlan</div><div class="ef-contact">printplan@decal.pt</div></div><div style="font-size:10px;color:var(--fg-3);text-align:right">Gerado em ${dateStr}.</div></div></div></div>`;
}

/* ===== anFinanceiraHTML ===== */
function anFinanceiraHTML(cliente){
  const lines=FINANCEIRA_LINES[cliente]||[];
  if(!lines.length)return'<div class="an-empty-note">Sem dados de faturação para este cliente.</div>';
  const years=[...new Set(lines.map(l=>l[0].slice(0,4)))].sort().reverse();
  const renderYear=(year)=>{
    const all=year==='todos'?lines:lines.filter(l=>l[0].startsWith(year));
    if(!all.length)return'<div class="an-empty-note">Sem linhas neste período.</div>';
    const tVL=all.reduce((s,l)=>s+l[3],0),tCusto=all.reduce((s,l)=>s+l[4],0);
    const marg=tVL>0?(tVL-tCusto)/tVL*100:null;
    const byM={};all.forEach(l=>{if(!byM[l[0]])byM[l[0]]={vl:0,c:0};byM[l[0]].vl+=l[3];byM[l[0]].c+=l[4];});
    const months=Object.keys(byM).sort().reverse();
    const rows=months.map(m=>{const{vl,c}=byM[m];const mp=vl>0?(vl-c)/vl*100:null;return`<tr><td>${m}</td><td class="num">${fmtEFull(vl)}</td><td class="num" style="color:var(--fg-3)">${fmtEFull(c)}</td><td class="num" style="color:${finMargemColor(mp)};font-weight:700">${finPctTxt(mp)}</td></tr>`;}).join('');
    return`<div class="an-avg-card" style="margin-bottom:16px"><div class="an-avg-label">${year==='todos'?'Total histórico':'Total '+year}</div><div class="an-avg-body"><span class="an-avg-value">${fmtEFull(tVL)}</span><span class="an-avg-vs">custo ${fmtEFull(tCusto)}</span><span class="an-avg-pct" style="color:${finMargemColor(marg)}">${finPctTxt(marg)}</span></div></div><div class="tbl-wrap" style="box-shadow:none;border:1px solid var(--border-1)"><table><thead><tr><th>Mês</th><th class="num">Valor Líquido</th><th class="num">Custo</th><th class="num">Margem %</th></tr></thead><tbody>${rows}</tbody></table></div>`;
  };
  return`<div class="an-fin-note">Valores refletem a faturação de todo o cliente (todos os contratos).</div><div style="margin-bottom:12px"><label>Ano: <select onchange="document.getElementById('fin-year-body').innerHTML=anFinanceiraHTMLYear('${cliente.replace(/'/g,"\\'")}',this.value)"><option value="todos">Todos</option>${years.map(y=>`<option value="${y}">${y}</option>`).join('')}</select></label></div><div id="fin-year-body">${renderYear('todos')}</div>`;
}
function anFinanceiraHTMLYear(cliente,year){
  const lines=FINANCEIRA_LINES[cliente]||[];
  const all=year==='todos'?lines:lines.filter(l=>l[0].startsWith(year));
  if(!all.length)return'<div class="an-empty-note">Sem linhas neste período.</div>';
  const tVL=all.reduce((s,l)=>s+l[3],0),tCusto=all.reduce((s,l)=>s+l[4],0);
  const marg=tVL>0?(tVL-tCusto)/tVL*100:null;
  const byM={};all.forEach(l=>{if(!byM[l[0]])byM[l[0]]={vl:0,c:0};byM[l[0]].vl+=l[3];byM[l[0]].c+=l[4];});
  const months=Object.keys(byM).sort().reverse();
  const rows=months.map(m=>{const{vl,c}=byM[m];const mp=vl>0?(vl-c)/vl*100:null;return`<tr><td>${m}</td><td class="num">${fmtEFull(vl)}</td><td class="num" style="color:var(--fg-3)">${fmtEFull(c)}</td><td class="num" style="color:${finMargemColor(mp)};font-weight:700">${finPctTxt(mp)}</td></tr>`;}).join('');
  return`<div class="an-avg-card" style="margin-bottom:16px"><div class="an-avg-label">${year==='todos'?'Total histórico':'Total '+year}</div><div class="an-avg-body"><span class="an-avg-value">${fmtEFull(tVL)}</span><span class="an-avg-vs">custo ${fmtEFull(tCusto)}</span><span class="an-avg-pct" style="color:${finMargemColor(marg)}">${finPctTxt(marg)}</span></div></div><div class="tbl-wrap" style="box-shadow:none;border:1px solid var(--border-1)"><table><thead><tr><th>Mês</th><th class="num">Valor Líquido</th><th class="num">Custo</th><th class="num">Margem %</th></tr></thead><tbody>${rows}</tbody></table></div>`;
}

/* ===== AnalysisModal ===== */
const AnalysisModal = ({row, onClose}) => {
  const [sec, setSec] = React.useState('info');
  const secs = [
    {k:'info',l:'Info'},
    {k:'m2',l:'M²'},
    row.tinta_tem_dados&&{k:'tinta',l:'Tinta'},
    row.suporte_tem_dados&&{k:'suporte',l:'Suportes'},
    FINANCEIRA_LINES[row.cliente]&&{k:'financeira',l:'Financeira'},
    row.armazem&&INVENTARIO_DATA[row.armazem]&&{k:'inventario',l:'Inventário'},
  ].filter(Boolean);

  const renderSec = () => {
    if(sec==='info'){
      const cc={'Care':'b-care','Care Plus':'b-careplus','No Care':'b-nocare','—':'b-neutral'};
      const item=(l,v)=><div className="an-info-item"><div className="an-info-label">{l}</div><div className="an-info-value">{v}</div></div>;
      const section=(t,items)=><div className="an-info-section"><div className="an-info-section-title">{t}</div><div className="an-info-grid">{items}</div></div>;
      return(<div>{section('Identificação',[item('Contrato',row.contrato),item('Cliente',row.cliente),item('Nº Cliente',row.num_cliente||'—'),item('Vendedor',shortV(row.vendedor)),item('Armazém',row.armazem||'—'),item('Cód. Artigo',row.cod_artigo||'—')])}{section('Condições',[item('M² Mensais',fmt(row.m2_mensal)),item('Prazo',`${row.rendas_decorridas}/${row.num_rendas} meses`),item('Total M² Contratados',fmt(row.limite_m2)),item('PVP Contratado',typeof row.pvp==='number'?'€'+fmt(row.pvp,2):row.pvp),item('Data Início',row.data_inicio||'—'),item('Data Término',row.data_termino||'—')])}{section('Assistência',[item('Care',<span className={`badge ${cc[row.care_type]||'b-neutral'}`}>{row.care_type}</span>),item('Tintas',row.tintas_flag?<span className="badge b-acima">Incluídas</span>:<span className="badge b-abaixo">Não incluídas</span>),item('Suportes',row.sup_imp===1?<span className="badge b-acima">Incluído</span>:<span className="badge b-abaixo">Excluído</span>)])}</div>);
    }
    if(sec==='m2'){
      const monthly=(MONTHLY_REVENUE[row.contrato]||[]).slice().sort((a,b)=>a[0]<b[0]?1:-1);
      const esp=row.m2_mensal;
      const statusColor=row.m2_status==='ACIMA'?'var(--green-700)':'var(--red-700)';
      return(<div>
        <div style={{display:'flex',gap:12,flexWrap:'wrap',marginBottom:16}}>
          {[['M² Acumulados',fmt(row.acumulado_m2),''],['M² Esperados',fmt(row.esperado_m2),''],['Desvio',(row.m2_desvio>=0?'+':'')+fmt(row.m2_desvio),statusColor],['Receita Real',fmtEFull(row.rec_real),''],['Valor em Atraso',fmtEFull(row.valor_em_atraso),'var(--red-700)'],row.novo_pvp&&['Novo PVP',fmtEFull(row.novo_pvp)+'/m²','var(--pumpkin-700)']].filter(Boolean).map(([l,v,c])=>(<div key={l} className="an-avg-card" style={{minWidth:120}}><div className="an-avg-label">{l}</div><div className="an-avg-value" style={{color:c||'inherit',fontSize:18}}>{v}</div></div>))}
        </div>
        {monthly.length>0&&<div className="tbl-wrap" style={{boxShadow:'none',border:'1px solid var(--border-1)'}}>
          <table><thead><tr><th>Mês</th><th className="num">M² Real</th><th className="num">M² Esperado</th><th className="num">Desvio</th><th className="num">Desvio %</th></tr></thead>
          <tbody>{monthly.map(row2=>{const real=row2[1]||0;const dev=real-esp;const pct=esp>0?(dev/esp*100):0;const col=dev>=0?'var(--green-700)':'var(--red-700)';return(<tr key={row2[0]}><td>{row2[0]}</td><td className="num">{fmt(real)}</td><td className="num" style={{color:'var(--fg-3)'}}>{fmt(esp)}</td><td className="num" style={{color:col,fontWeight:700}}>{(dev>=0?'+':'')+fmt(dev)}</td><td className="num" style={{color:col}}>{(pct>=0?'+':'')+fmt(pct,1)}%</td></tr>);})}</tbody>
          </table>
        </div>}
      </div>);
    }
    if(sec==='tinta'){
      return(<div>
        <div style={{display:'flex',gap:12,flexWrap:'wrap',marginBottom:16}}>
          {[['ml/m² Real',fmt(row.tinta_ml_m2_real,1)],['ml/m² Esperado',fmt(row.tinta_ml_m2_esp,1)],['Desvio %',(row.tinta_desvio_pct>=0?'+':'')+fmt(row.tinta_desvio_pct,1)+'%']].map(([l,v])=>(<div key={l} className="an-avg-card" style={{minWidth:120}}><div className="an-avg-label">{l}</div><div className="an-avg-value" style={{fontSize:18}}>{v}</div></div>))}
        </div>
        {row.tinta_ml_m2_real===0&&<div className="an-empty-note" style={{color:'var(--pumpkin-700)'}}>Consumo de tinta zero neste período.</div>}
      </div>);
    }
    if(sec==='suporte'){
      return(<div>
        <div style={{display:'flex',gap:12,flexWrap:'wrap',marginBottom:16}}>
          {[['mt Impressos',fmt(row.suporte_impresso_mt)],['mt Consumidos',fmt(row.suporte_consumo_mt)],['Desvio %',(row.suporte_desvio_pct>=0?'+':'')+fmt(row.suporte_desvio_pct,1)+'%']].map(([l,v])=>(<div key={l} className="an-avg-card" style={{minWidth:120}}><div className="an-avg-label">{l}</div><div className="an-avg-value" style={{fontSize:18}}>{v}</div></div>))}
        </div>
      </div>);
    }
    if(sec==='financeira'){
      return <div dangerouslySetInnerHTML={{__html:anFinanceiraHTML(row.cliente)}}/>;
    }
    if(sec==='inventario'){
      const armazem=row.armazem;const items=INVENTARIO_DATA[armazem]||[];
      const descArm=INVENTARIO_ARMAZEM_DESC[armazem]||'';
      const meses=Math.max(1,row.rendas_decorridas||1);
      const totalValor=items.reduce((s,i)=>s+(i.valor||0),0);
      return(<div>
        <div className="an-avg-card" style={{marginBottom:16}}><div className="an-avg-label">Armazém {armazem}{descArm?` · ${descArm}`:''}</div><div className="an-avg-body"><span className="an-avg-value">{fmtEFull(totalValor)}</span><span className="an-avg-vs">{items.length} artigos</span></div></div>
        <div className="tbl-wrap" style={{boxShadow:'none',border:'1px solid var(--border-1)'}}>
          <table><thead><tr><th>Artigo</th><th>Descrição</th><th className="num">Stock</th><th className="num">Valor</th></tr></thead>
          <tbody>{items.slice().sort((a,b)=>(b.valor||0)-(a.valor||0)).map(it=>{const c=INVENTARIO_CONSUMO[armazem]&&INVENTARIO_CONSUMO[armazem][it.artigo];const mq=c?c.qtd/meses:null;const dias=mq&&mq>0?it.stock/mq*30:null;const excesso=it.stock>0&&(dias==null||dias>60);return(<tr key={it.artigo}><td><div className="ccode" title={it.artigo}>{it.artigo}</div></td><td style={{fontSize:11,color:'var(--fg-3)'}}>{it.descricao||'—'}</td><td className="num" style={{color:excesso?'var(--red-700)':'inherit',fontWeight:excesso?700:'normal'}}>{fmt(it.stock,it.stock%1!==0?1:0)}</td><td className="num" style={{fontWeight:700}}>{fmtEFull(it.valor)}</td></tr>);})}</tbody>
          </table>
        </div>
      </div>);
    }
    return null;
  };

  return(
    <div className="an-overlay open" onClick={e=>e.target===e.currentTarget&&onClose()}>
      <div className="an-modal">
        <div className="an-header">
          <div><h3 id="an-title">{row.cliente}</h3><div className="an-sub">{row.contrato} · {row.vendedor&&shortV(row.vendedor)}</div></div>
          <button className="an-close" onClick={onClose}>✕</button>
        </div>
        <div style={{display:'flex',gap:4,padding:'0 20px',borderBottom:'1px solid var(--border-1)',flexWrap:'wrap'}}>
          {secs.map(s=><button key={s.k} className={`an-tab${sec===s.k?' active':''}`} onClick={()=>setSec(s.k)}>{s.l}</button>)}
        </div>
        <div className="an-body">{renderSec()}</div>
      </div>
    </div>
  );
};

/* ===== OverviewTab ===== */
const OverviewTab = ({data}) => {
  const [drillKey, setDrillKey] = React.useState(null);
  const ovData = React.useMemo(()=>{
    const expirados=data.filter(r=>r.expirado).length;
    const alertasRen=data.filter(r=>r.dias_termino!=null&&r.dias_termino>=0&&r.dias_termino<=90).length;
    const pvpARever=data.filter(r=>r.novo_pvp!=null);
    const m2Acima=data.filter(r=>r.m2_status==='ACIMA');
    const m2Abaixo=data.filter(r=>r.m2_status==='ABAIXO');
    const tintaAll=data.filter(r=>r.tinta_tem_dados);
    const tintaV=[],tintaA=[],tintaR=[];
    tintaAll.forEach(r=>{const c=desvioColor(r.tinta_desvio_pct,1);(c==='var(--green-700)'?tintaV:c==='var(--red-700)'?tintaR:tintaA).push(r);});
    const supAll=data.filter(r=>r.suporte_tem_dados);
    const supV=[],supA=[],supR=[];
    supAll.forEach(r=>{const c=desvioColor(r.suporte_desvio_pct,1);(c==='var(--green-700)'?supV:c==='var(--red-700)'?supR:supA).push(r);});
    const finV=[],finA=[],finR=[];
    FINANCEIRA_DATA.forEach(f=>{if(f.margem_pct==null)return;(f.margem_pct<0?finR:f.margem_pct<20?finA:finV).push(f);});
    const pvpOk=data.filter(r=>r.novo_pvp==null);
    const tFat=FINANCEIRA_DATA.reduce((s,r)=>s+r.valor_liquido,0);
    const tCusto=FINANCEIRA_DATA.reduce((s,r)=>s+r.custo_total,0);
    const margemMedia=tFat>0?(tFat-tCusto)/tFat*100:0;
    const tEsp=data.reduce((s,r)=>s+(r.rec_pot_atual||0),0);
    const h1=data.length?m2Acima.length/data.length*100:null;
    const h2=tintaAll.length?(tintaV.length+0.5*tintaA.length)/tintaAll.length*100:null;
    const h3=supAll.length?(supV.length+0.5*supA.length)/supAll.length*100:null;
    const h4=FINANCEIRA_DATA.length?(finV.length+0.5*finA.length)/FINANCEIRA_DATA.length*100:null;
    const h5=data.length?pvpOk.length/data.length*100:null;
    const hp=[h1,h2,h3,h4,h5].filter(v=>v!=null);
    const health=hp.length?hp.reduce((s,v)=>s+v,0)/hp.length:0;
    const segments={
      m2_acima:{title:'M² Acima do Esperado',rows:m2Acima,cols:['M² Acumulados','M² Esperado','Desvio'],vals:r=>[fmt(r.acumulado_m2),fmt(r.esperado_m2),(r.m2_desvio>=0?'+':'')+fmt(r.m2_desvio)+' m²'],colors:[null,null,r=>r.m2_desvio>=0?'var(--green-700)':'var(--red-700)']},
      m2_abaixo:{title:'M² Abaixo do Esperado',rows:m2Abaixo,cols:['M² Acumulados','M² Esperado','Desvio'],vals:r=>[fmt(r.acumulado_m2),fmt(r.esperado_m2),(r.m2_desvio>=0?'+':'')+fmt(r.m2_desvio)+' m²'],colors:[null,null,r=>r.m2_desvio>=0?'var(--green-700)':'var(--red-700)']},
      tinta_r:{title:'Tintas — Em incumprimento',rows:tintaR,cols:['ml/m² Real','ml/m² Esperado','Desvio %'],vals:r=>[fmt(r.tinta_ml_m2_real,1),fmt(r.tinta_ml_m2_esp,1),(r.tinta_desvio_pct>=0?'+':'')+fmt(r.tinta_desvio_pct,1)+'%'],colors:[null,null,r=>desvioColor(r.tinta_desvio_pct,1)]},
      tinta_a:{title:'Tintas — A monitorizar',rows:tintaA,cols:['ml/m² Real','ml/m² Esperado','Desvio %'],vals:r=>[fmt(r.tinta_ml_m2_real,1),fmt(r.tinta_ml_m2_esp,1),(r.tinta_desvio_pct>=0?'+':'')+fmt(r.tinta_desvio_pct,1)+'%'],colors:[null,null,r=>desvioColor(r.tinta_desvio_pct,1)]},
      tinta_v:{title:'Tintas — Dentro do esperado',rows:tintaV,cols:['ml/m² Real','ml/m² Esperado'],vals:r=>[fmt(r.tinta_ml_m2_real,1),fmt(r.tinta_ml_m2_esp,1)],colors:[null,null]},
      sup_r:{title:'Suportes — Em incumprimento',rows:supR,cols:['mt Impressos','mt Consumidos','Desvio %'],vals:r=>[fmt(r.suporte_impresso_mt),fmt(r.suporte_consumo_mt),(r.suporte_desvio_pct>=0?'+':'')+fmt(r.suporte_desvio_pct,1)+'%'],colors:[null,null,r=>desvioColor(r.suporte_desvio_pct,1)]},
      sup_a:{title:'Suportes — A monitorizar',rows:supA,cols:['mt Impressos','mt Consumidos','Desvio %'],vals:r=>[fmt(r.suporte_impresso_mt),fmt(r.suporte_consumo_mt),(r.suporte_desvio_pct>=0?'+':'')+fmt(r.suporte_desvio_pct,1)+'%'],colors:[null,null,r=>desvioColor(r.suporte_desvio_pct,1)]},
      sup_v:{title:'Suportes — Dentro do esperado',rows:supV,cols:['mt Impressos','mt Consumidos'],vals:r=>[fmt(r.suporte_impresso_mt),fmt(r.suporte_consumo_mt)],colors:[null,null]},
      fin_r:{title:'Margem Negativa',rows:finR,cols:['Faturação','Custo','Margem %'],vals:r=>[fmtEFull(r.valor_liquido),fmtEFull(r.custo_total),finPctTxt(r.margem_pct)],colors:[null,null,r=>finMargemColor(r.margem_pct)]},
      fin_a:{title:'Margem 0–20%',rows:finA,cols:['Faturação','Custo','Margem %'],vals:r=>[fmtEFull(r.valor_liquido),fmtEFull(r.custo_total),finPctTxt(r.margem_pct)],colors:[null,null,r=>finMargemColor(r.margem_pct)]},
      pvp_rever:{title:'Contratos com PVP a Rever',rows:pvpARever,cols:['PVP Atual','PVP Novo','Valor em Atraso'],vals:r=>[typeof r.pvp==='number'?'€'+fmt(r.pvp,2):r.pvp,r.novo_pvp?fmtEFull(r.novo_pvp)+'/m²':'—',r.valor_em_atraso>0?fmtEFull(r.valor_em_atraso):'—'],colors:[null,r=>r.novo_pvp!=null?'var(--pumpkin-700)':null,r=>r.valor_em_atraso>0?'var(--red-700)':null]},
    };
    const rag=[
      {label:'M² Impressos vs. Contratualizado',segs:[{c:'var(--green-700)',n:m2Acima.length,k:'m2_acima',l:'Acima'},{c:'var(--red-700)',n:m2Abaixo.length,k:'m2_abaixo',l:'Abaixo'}],total:data.length},
      {label:'Controlo de Tintas',segs:[{c:'var(--green-700)',n:tintaV.length,k:'tinta_v',l:'OK'},{c:'var(--yellow-700)',n:tintaA.length,k:'tinta_a',l:'Monitorizar'},{c:'var(--red-700)',n:tintaR.length,k:'tinta_r',l:'Incumprimento'}],total:tintaAll.length},
      {label:'Controlo de Suportes',segs:[{c:'var(--green-700)',n:supV.length,k:'sup_v',l:'OK'},{c:'var(--yellow-700)',n:supA.length,k:'sup_a',l:'Monitorizar'},{c:'var(--red-700)',n:supR.length,k:'sup_r',l:'Incumprimento'}],total:supAll.length},
      {label:'Análise Financeira (Margem)',segs:[{c:'var(--green-700)',n:finV.length,k:null,l:'≥20%'},{c:'var(--yellow-700)',n:finA.length,k:'fin_a',l:'0–20%'},{c:'var(--red-700)',n:finR.length,k:'fin_r',l:'Negativa'}],total:FINANCEIRA_DATA.length},
      {label:'PVP a Rever',segs:[{c:'var(--green-700)',n:pvpOk.length,k:null,l:'OK'},{c:'var(--red-700)',n:pvpARever.length,k:'pvp_rever',l:'A Rever'}],total:data.length},
    ];
    const maxPot=Math.max(...VEND_DATA.map(v=>v.rec_pot),1);
    return{expirados,alertasRen,pvpARever,m2Acima,m2Abaixo,tintaR,supR,finR,health,segments,rag,tFat,tCusto,margemMedia,tEsp,maxPot,pvpOk};
  },[data]);

  const drill = drillKey&&ovData.segments[drillKey];
  const hst = ovData.health<40?{l:'Crítico',c:'var(--red-700)'}:ovData.health<70?{l:'Médio',c:'var(--pumpkin-700)'}:{l:'Saudável',c:'var(--green-700)'};

  const RagBar = ({label,segs,total}) => {
    const tot=segs.reduce((s,sg)=>s+sg.n,0)||1;
    return(<div className="rag-row">
      <div className="rag-label">{label}</div>
      <div className="rag-track">{segs.filter(sg=>sg.n>0).map((sg,i)=>(<div key={i} className="rag-seg" style={{width:`${sg.n/tot*100}%`,background:sg.c,cursor:sg.k?'pointer':'default'}} onClick={()=>sg.k&&setDrillKey(prev=>prev===sg.k?null:sg.k)} title={`${sg.l}: ${sg.n}`}/>))}</div>
      <div className="rag-count">{segs.map((sg,i)=><span key={i} style={{color:sg.c,fontWeight:600,marginRight:8,cursor:sg.k?'pointer':'default'}} onClick={()=>sg.k&&setDrillKey(prev=>prev===sg.k?null:sg.k)}>{sg.n}</span>)}</div>
    </div>);
  };

  return(
    <div className="pp-tab-content"><div className="page">
      {/* Banner */}
      <div className="ov-banner">
        {[
          ['Contratos',fmt(data.length),''],
          ['Receita Potencial',fmtE(data.reduce((s,r)=>s+(r.rec_pot||0),0)),''],
          ['Faturado Real',fmtE(ovData.tFat),''],
          ['Margem Média',fmt(ovData.margemMedia,1)+'%',ovData.margemMedia<0?'var(--red-700)':'var(--green-700)'],
          ['PVP a Rever',String(ovData.pvpARever.length),ovData.pvpARever.length>0?'var(--pumpkin-700)':''],
          ['Expirados',String(ovData.expirados),ovData.expirados>0?'var(--red-700)':''],
        ].map(([l,v,c])=><div key={l} className="ov-banner-item"><div className="ov-banner-v" style={{color:c||undefined}}>{v}</div><div className="ov-banner-l">{l}</div></div>)}
      </div>

      {/* Health + RAG combined */}
      <div className="card" style={{marginBottom:14}}>
        <div className="ov-health-rag-layout">
          <div className="ov-health-side">
            <div className="card-title">Saúde dos Contratos</div>
            <div className="ov-health-body">
              <div className="ov-gauge-wrap" dangerouslySetInnerHTML={{__html:ovGaugeSVG(ovData.health)}}/>
              <div className="ov-health-text">
                <div className="ov-health-score" style={{color:hst.c}}>{fmt(ovData.health,0)}%</div>
                <div className="ov-health-label" style={{color:hst.c}}>{hst.l}</div>
              </div>
            </div>
          </div>
          <div className="ov-rag-side">
            <div className="card-title">5 Indicadores-Chave <span className="ct">Clique numa barra para ver o detalhe</span></div>
            {ovData.rag.map(row=><RagBar key={row.label} label={row.label} segs={row.segs} total={row.total}/>)}
          </div>
        </div>
      </div>

      {/* Risk table */}
      <div className="card" style={{marginBottom:'var(--space-4)'}}>
        <div className="card-title">Riscos e Ações <span className="ct">Clique num número para ver os contratos</span></div>
        <table className="ov-risk-table"><thead><tr><th>Indicador</th><th className="num">Crítico</th><th className="num">A Monitorizar</th></tr></thead>
        <tbody>
          {[['M² Impressos vs. Contratualizado','m2_abaixo',ovData.m2Abaixo.length,null,0],['Controlo de Tintas','tinta_r',ovData.tintaR&&ovData.tintaR.length,'tinta_a',ovData.segments.tinta_a&&ovData.segments.tinta_a.rows.length],['Controlo de Suportes','sup_r',ovData.supR&&ovData.supR.length,'sup_a',ovData.segments.sup_a&&ovData.segments.sup_a.rows.length],['Análise Financeira (Margem)','fin_r',ovData.finR&&ovData.finR.length,'fin_a',ovData.segments.fin_a&&ovData.segments.fin_a.rows.length],['PVP a Rever','pvp_rever',ovData.pvpARever.length,null,0]].map(([l,kCrit,nCrit,kMon,nMon])=>(
            <tr key={l}><td>{l}</td>
              <td className="num"><span className="ov-risk-cell" style={{color:'var(--red-700)',cursor:kCrit&&nCrit?'pointer':'default'}} onClick={()=>kCrit&&nCrit&&setDrillKey(prev=>prev===kCrit?null:kCrit)}>{nCrit||0}</span></td>
              <td className="num"><span className="ov-risk-cell" style={{color:'var(--yellow-700)',cursor:kMon&&nMon?'pointer':'default'}} onClick={()=>kMon&&nMon&&setDrillKey(prev=>prev===kMon?null:kMon)}>{nMon||0}</span></td>
            </tr>
          ))}
        </tbody></table>
      </div>

      {/* Drilldown */}
      {drill&&<div className="card" style={{marginBottom:'var(--space-4)'}}>
        <div className="ov-drill-header">
          <div className="ov-drill-title">{drill.title} <span className="ct">{drill.rows.length} registos</span></div>
          <button className="ov-drill-close" onClick={()=>setDrillKey(null)}>✕</button>
        </div>
        <div className="tbl-wrap" style={{boxShadow:'none',border:'1px solid var(--border-1)',maxHeight:380,overflowY:'auto'}}>
          <table><thead><tr><th>Cliente / Contrato</th>{drill.cols.map(c=><th key={c} className="num">{c}</th>)}</tr></thead>
          <tbody>{drill.rows.map((r,i)=><tr key={i}><td><div className="cname" title={r.cliente}>{r.cliente}</div>{r.contrato&&<div className="ccode">{r.contrato}</div>}</td>{drill.vals(r).map((v,j)=><td key={j} className="num" style={drill.colors[j]?(typeof drill.colors[j]==='function'?{color:drill.colors[j](r),fontWeight:700}:{color:drill.colors[j],fontWeight:700}):{}}>{v}</td>)}</tr>)}</tbody>
          </table>
        </div>
      </div>}

      {/* Vendor bars */}
      <div className="g2" style={{marginBottom:'var(--space-4)'}}>
        <div className="card">
          <div className="card-title">Receita por Vendedor</div>
          {[...VEND_DATA].sort((a,b)=>b.rec_pot-a.rec_pot).map(v=>{
            const w=v.rec_pot/ovData.maxPot*100;const rw=clamp(v.rec_real/v.rec_pot*100,0,100);
            const color=v.util_pct>=100?'var(--green-700)':v.util_pct>=50?'var(--dd-primary-600)':v.util_pct>=25?'var(--pumpkin-700)':'var(--red-700)';
            return(<div key={v.vendedor} className="vbar">
              <div className="vname">{v.vendedor}</div>
              <div className="vtracks">
                <div className="vtrack-label">Potencial: {fmtE(v.rec_pot)} · Realizado: {fmtE(v.rec_real)}</div>
                <div className="vtrack-bar" style={{width:`${w}%`}}><div className="vtrack-fill" style={{width:`${rw}%`,background:color}}/></div>
              </div>
              <div className="vpct-badge" style={{color}}>{fmt(v.util_pct,0)}%</div>
            </div>);
          })}
        </div>
        <div className="card">
          <div className="card-title">Distribuição Care</div>
          {Object.entries(data.reduce((acc,r)=>{acc[r.care_type]=(acc[r.care_type]||0)+1;return acc;},{})).map(([k,v])=>{
            const pct=v/data.length*100;const cc={'Care Plus':'var(--purple-700)','Care':'var(--dd-primary-600)','No Care':'var(--fg-3)'}[k]||'var(--fg-3)';
            return(<div key={k} className="ov-metric"><label><span>{k}</span><strong>{v} ({fmt(pct,0)}%)</strong></label><div className="gauge"><div className="gauge-fill" style={{width:`${pct}%`,background:cc}}/></div></div>);
          })}
        </div>
      </div>

      {/* Top lists */}
      <div className="g4">
        <div className="card"><div className="card-title">Receita em Falta — Top 5</div><table><tbody>{data.filter(r=>r.rec_falta>0).sort((a,b)=>b.rec_falta-a.rec_falta).slice(0,5).map(r=><tr key={r.contrato}><td><div className="cname" title={r.cliente}>{r.cliente}</div><div className="ccode">{r.contrato}</div></td><td className="num" style={{color:'var(--red-700)',fontWeight:700}}>{fmtEFull(r.rec_falta)}</td></tr>)}</tbody></table></div>
        <div className="card"><div className="card-title">Desvio M² Negativo — Top 5</div><table><tbody>{data.filter(r=>r.m2_status==='ABAIXO').sort((a,b)=>a.m2_desvio-b.m2_desvio).slice(0,5).map(r=><tr key={r.contrato}><td><div className="cname" title={r.cliente}>{r.cliente}</div></td><td className="num" style={{color:'var(--red-700)',fontWeight:700}}>{fmt(r.m2_desvio)} m²</td></tr>)}</tbody></table></div>
        <div className="card"><div className="card-title">PVP a Rever — Top 5</div><table><tbody>{data.filter(r=>r.novo_pvp!=null).sort((a,b)=>b.valor_em_atraso-a.valor_em_atraso).slice(0,5).map(r=><tr key={r.contrato}><td><div className="cname" title={r.cliente}>{r.cliente}</div></td><td className="num" style={{color:'var(--pumpkin-700)',fontWeight:700}}>{fmtEFull(r.novo_pvp)}/m²</td></tr>)}</tbody></table></div>
        <div className="card"><div className="card-title">Margem Negativa — Top 5</div><table><tbody>{FINANCEIRA_DATA.filter(r=>r.margem_pct!=null&&r.margem_pct<0).sort((a,b)=>b.valor_liquido-a.valor_liquido).slice(0,5).map(r=><tr key={r.cliente}><td><div className="cname" title={r.cliente}>{r.cliente}</div></td><td className="num" style={{color:'var(--red-700)',fontWeight:700}}>{finPctTxt(r.margem_pct)}</td></tr>)}</tbody></table></div>
      </div>
    </div></div>
  );
};

/* ===== M2Tab ===== */
const M2Tab = ({data, onOpenAnalysis}) => {
  const [search, setSearch] = React.useState('');
  const [sortKey, setSortKey] = React.useState('m2_desvio_pct');
  const [sortDir, setSortDir] = React.useState('asc');
  const [timeMode, setTimeMode] = React.useState('atual');
  const [refMonth, setRefMonth] = React.useState('');
  const [intFrom, setIntFrom] = React.useState('');
  const [intTo, setIntTo] = React.useState('');
  const [groupBy, setGroupBy] = React.useState(true);
  const [expanded, setExpanded] = React.useState({});

  const ALL_MONTHS = React.useMemo(()=>[...new Set(Object.values(MONTHLY_REVENUE).flatMap(rows=>rows.map(r=>r[0])))].sort(),[]);

  const baseData = React.useMemo(()=>{
    if(timeMode==='refmonth'&&refMonth){
      return data.map(r=>{const m=MONTHLY_REVENUE[r.contrato];if(!m)return r;return applyMonthlySnapshot(r,m.filter(x=>x[0]<=refMonth));});
    }
    if(timeMode==='interval'&&intFrom&&intTo){
      return data.map(r=>{const m=MONTHLY_REVENUE[r.contrato];if(!m)return{...r,periodo_m2:null,periodo_receita:null};const rows=m.filter(x=>x[0]>=intFrom&&x[0]<=intTo);return{...r,periodo_m2:rows.reduce((s,x)=>s+x[1],0),periodo_receita:Math.round(rows.reduce((s,x)=>s+x[1],0)*r.pvp*100)/100};});
    }
    return data;
  },[data,timeMode,refMonth,intFrom,intTo]);

  const filtered = React.useMemo(()=>{
    const q=search.toLowerCase();
    let arr=baseData.filter(r=>!q||(r.cliente.toLowerCase().includes(q)||r.contrato.toLowerCase().includes(q)));
    arr.sort((a,b)=>{let va=a[sortKey],vb=b[sortKey];if(typeof va==='string')va=va.toLowerCase();if(typeof vb==='string')vb=vb.toLowerCase();if(va==null)va=-Infinity;if(vb==null)vb=-Infinity;return sortDir==='asc'?(va<vb?-1:va>vb?1:0):(va>vb?-1:va<vb?1:0);});
    return arr;
  },[baseData,search,sortKey,sortDir]);

  const doSort = k => {setSortDir(prev=>sortKey===k?(prev==='asc'?'desc':'asc'):'asc');setSortKey(k);};

  const aggGroup = (cliente, contratos) => {
    const sum=k=>contratos.reduce((s,r)=>s+(r[k]||0),0);
    const limite_m2=sum('limite_m2'),acumulado_m2=sum('acumulado_m2'),esperado_m2=sum('esperado_m2'),rec_real=sum('rec_real'),rec_pot=sum('rec_pot'),rec_falta=sum('rec_falta'),valor_em_atraso=sum('valor_em_atraso');
    const m2_desvio=acumulado_m2-esperado_m2;const m2_desvio_pct=esperado_m2>0?Math.round(m2_desvio/esperado_m2*1000)/10:0;
    return{cliente,contrato:'',vendedor:contratos[0].vendedor,rendas_decorridas:null,num_rendas:null,limite_m2,acumulado_m2,esperado_m2,rec_real,rec_pot,rec_falta,m2_desvio,m2_desvio_pct,m2_status:m2_desvio>=0?'ACIMA':'ABAIXO',pvp:null,m2_mensal:null,novo_pvp:contratos.some(r=>r.novo_pvp!=null)?true:null,valor_em_atraso,tinta_tem_dados:false,suporte_tem_dados:false,fin_valor_liquido:null,fin_margem_pct:null,_isGroup:true,_n:contratos.length};
  };

  const SortTh = ({k,label,tip,className}) => {
    const active=sortKey===k;
    return <th className={className} title={tip} onClick={()=>doSort(k)} style={{cursor:'pointer',userSelect:'none'}}>{label}{active?` ${sortDir==='asc'?'↑':'↓'}`:''}</th>;
  };

  const m2StatusCell = r => {
    const ratio=r.esperado_m2>0?r.acumulado_m2/r.esperado_m2*100:null;
    const color=r.m2_status==='ACIMA'?'var(--green-700)':'var(--red-700)';
    return <span style={{color,fontWeight:700}}>{ratio!=null?fmt(ratio,0)+'%':'—'}</span>;
  };

  const renderRow = (r, isGroup=false, groupId=null, hidden=false) => {
    const key = r.contrato||r.cliente;
    if(hidden) return null;
    return(
      <tr key={key} className={isGroup?'m2-group-row':groupId?`m2-detail-${groupId}`:''} onClick={isGroup?()=>setExpanded(prev=>({...prev,[groupId]:!prev[groupId]})):null} style={isGroup?{cursor:'pointer'}:{}}>
        <td><div className="cname" title={r.cliente}>{isGroup&&<span className="m2-arrow-chip">{expanded[groupId]?'▾':'▸'}</span>}{r.cliente}</div></td>
        <td><div className="ccode" title={r.contrato||''}>{isGroup?<span className="m2-group-count">{r._n} contratos</span>:r.contrato}</div></td>
        <td>{!isGroup&&<button className="btn-analisar" onClick={e=>{e.stopPropagation();onOpenAnalysis(r);}}>Analisar</button>}</td>
        <td className="num" style={{color:'var(--fg-3)'}}>{isGroup?<span className="m2-group-count">{r._n} contratos</span>:`${r.rendas_decorridas}/${r.num_rendas}`}</td>
        <td className="num">{r.limite_m2!=null?fmt(r.limite_m2):'—'}</td>
        <td className="num">{r.m2_mensal!=null?fmt(r.m2_mensal):'—'}</td>
        <td className="num">{typeof r.pvp==='number'?'€'+fmt(r.pvp,2):r.pvp||'—'}</td>
        <td className="num">{m2StatusCell(r)}</td>
        <td className="num">{r.novo_pvp!=null?<span style={{color:'var(--pumpkin-700)',fontWeight:700}}>{isGroup?'Rever':fmtEFull(r.novo_pvp)+'/m²'}</span>:'—'}</td>
        <td className="num">{r.tinta_tem_dados?<span style={{color:desvioColor(r.tinta_desvio_pct,1),fontWeight:700}}>{(r.tinta_desvio_pct>=0?'+':'')+fmt(r.tinta_desvio_pct,1)}%</span>:<span style={{color:'var(--fg-3)'}}>—</span>}</td>
        <td className="num">{r.suporte_tem_dados?<span style={{color:desvioColor(r.suporte_desvio_pct,1),fontWeight:700}}>{(r.suporte_desvio_pct>=0?'+':'')+fmt(r.suporte_desvio_pct,1)}%</span>:<span style={{color:'var(--fg-3)'}}>—</span>}</td>
        <td className="num">{r.fin_valor_liquido!=null?fmtEFull(r.fin_valor_liquido):<span style={{color:'var(--fg-3)'}}>—</span>}</td>
        <td className="num">{r.fin_margem_pct!=null?<span style={{color:finMargemColor(r.fin_margem_pct),fontWeight:700}}>{finPctTxt(r.fin_margem_pct)}</span>:<span style={{color:'var(--fg-3)'}}>—</span>}</td>
      </tr>
    );
  };

  const rows = React.useMemo(()=>{
    if(!groupBy) return filtered.map(r=>renderRow(r));
    const map={};filtered.forEach(r=>{(map[r.cliente]=map[r.cliente]||[]).push(r);});
    return Object.entries(map).flatMap(([cliente,contratos])=>{
      if(contratos.length===1) return [renderRow(contratos[0])];
      const safeId=cliente.replace(/[^a-zA-Z0-9]/g,'_');
      const rep=aggGroup(cliente,contratos);
      const groupRow=renderRow(rep,true,safeId);
      const detailRows=expanded[safeId]?contratos.map(r=>renderRow(r,false,safeId)):[];
      return[groupRow,...detailRows];
    });
  },[filtered,groupBy,expanded,sortKey,sortDir]);

  return(
    <div className="pp-tab-content"><div className="page">
      <div className="frow" style={{flexWrap:'wrap',gap:8,marginBottom:12}}>
        <input type="text" value={search} onChange={e=>setSearch(e.target.value)} placeholder="Pesquisar cliente ou contrato…" style={{flex:1,minWidth:200}}/>
        <button className={`fb${groupBy?' on':''}`} onClick={()=>setGroupBy(v=>!v)}>Agrupar por cliente</button>
        <span className="rc">{filtered.length} contratos</span>
      </div>
      <div className="time-mode-row" style={{marginBottom:12,display:'flex',gap:8,alignItems:'center',flexWrap:'wrap'}}>
        {[['atual','Atual (YTD)'],['refmonth','Por Mês'],['interval','Por Intervalo']].map(([k,l])=><button key={k} className={`tm-btn${timeMode===k?' active':''}`} onClick={()=>setTimeMode(k)}>{l}</button>)}
        {timeMode==='refmonth'&&<select value={refMonth} onChange={e=>setRefMonth(e.target.value)}><option value="">Mês de referência…</option>{ALL_MONTHS.map(m=><option key={m} value={m}>{m}</option>)}</select>}
        {timeMode==='interval'&&<><select value={intFrom} onChange={e=>setIntFrom(e.target.value)}><option value="">De…</option>{ALL_MONTHS.map(m=><option key={m} value={m}>{m}</option>)}</select><select value={intTo} onChange={e=>setIntTo(e.target.value)}><option value="">Até…</option>{ALL_MONTHS.filter(m=>!intFrom||m>=intFrom).map(m=><option key={m} value={m}>{m}</option>)}</select></>}
      </div>
      <div className="tbl-wrap">
        <table>
          <colgroup><col style={{width:'10%'}}/><col style={{width:'7%'}}/><col style={{width:'6%'}}/><col style={{width:'5%'}}/><col style={{width:'7%'}}/><col style={{width:'6%'}}/><col style={{width:'6%'}}/><col style={{width:'9%'}}/><col style={{width:'10%'}}/><col style={{width:'7%'}}/><col style={{width:'7%'}}/><col style={{width:'8%'}}/><col style={{width:'6%'}}/></colgroup>
          <thead><tr>
            <SortTh k="cliente" label="Cliente"/>
            <SortTh k="contrato" label="Contrato"/>
            <th>Análise</th>
            <SortTh k="rendas_decorridas" label="Meses" className="num" tip="Rendas decorridas / total rendas"/>
            <SortTh k="limite_m2" label="M² Total" className="num"/>
            <SortTh k="m2_mensal" label="M²/Mês" className="num"/>
            <SortTh k="pvp" label="PVP €/m²" className="num"/>
            <SortTh k="acumulado_m2" label="M² Impressos %" className="num" tip="M² acumulados / M² esperados até à data × 100"/>
            <SortTh k="novo_pvp" label="PVP a Rever" className="num"/>
            <SortTh k="tinta_desvio_pct" label="Tinta %" className="num" tip="Desvio % consumo tinta vs esperado"/>
            <SortTh k="suporte_desvio_pct" label="Suportes %" className="num" tip="Desvio % consumo suportes vs esperado"/>
            <SortTh k="fin_valor_liquido" label="Faturado" className="num"/>
            <SortTh k="fin_margem_pct" label="Margem %" className="num"/>
          </tr></thead>
          <tbody>{rows}</tbody>
        </table>
      </div>
    </div></div>
  );
};

/* ===== AlertasTab ===== */
const AlertasTab = ({data}) => {
  const [search, setSearch] = React.useState('');
  const [tema, setTema] = React.useState('');
  const [pcList, setPcList] = React.useState(()=>{try{return JSON.parse(localStorage.getItem('pp_casos_particulares')||'[]');}catch(e){return[];}});
  const [pcCliente, setPcCliente] = React.useState('');
  const [pcContrato, setPcContrato] = React.useState('');
  const [pcNota, setPcNota] = React.useState('');

  const alertRows = React.useMemo(()=>{
    const rows=[];
    data.forEach(r=>{
      if(r.novo_pvp!=null)rows.push({cliente:r.cliente,contrato:r.contrato,vendedor:r.vendedor,tema:'M²',detalhe:`Défice ${fmt(r.m2_desvio)} m² (${fmt(r.m2_desvio_pct,1)}%) · Novo PVP ${fmtEFull(r.novo_pvp)}/m²`});
      if(r.tinta_tem_dados){const c=desvioColor(r.tinta_desvio_pct,1);if(c==='var(--red-700)')rows.push({cliente:r.cliente,contrato:r.contrato,vendedor:r.vendedor,tema:'Tinta',detalhe:`${r.tinta_desvio_pct>=0?'+':''}${fmt(r.tinta_desvio_pct,1)}% acima do esperado`});else if(r.tinta_ml_m2_real===0)rows.push({cliente:r.cliente,contrato:r.contrato,vendedor:r.vendedor,tema:'Tinta',detalhe:'Consumo de tinta zero'});}
      if(r.suporte_tem_dados&&desvioColor(r.suporte_desvio_pct,1)==='var(--red-700)')rows.push({cliente:r.cliente,contrato:r.contrato,vendedor:r.vendedor,tema:'Suportes',detalhe:`${r.suporte_desvio_pct>=0?'+':''}${fmt(r.suporte_desvio_pct,1)}% acima do esperado`});
    });
    return rows;
  },[data]);

  const filteredAlerts = React.useMemo(()=>{
    const q=search.toLowerCase();
    return alertRows.filter(r=>(!tema||r.tema===tema)&&(!q||(r.cliente.toLowerCase().includes(q)||r.contrato.toLowerCase().includes(q))));
  },[alertRows,search,tema]);

  const addPc = () => {
    if(!pcCliente||!pcNota)return;
    const item={id:'pc_'+Date.now(),cliente:pcCliente,contrato:pcContrato,nota:pcNota,data:new Date().toLocaleDateString('pt-PT')};
    const next=[...pcList,item];
    setPcList(next);localStorage.setItem('pp_casos_particulares',JSON.stringify(next));
    setPcCliente('');setPcContrato('');setPcNota('');
  };
  const deletePc = id => {
    if(!confirm('Remover este registo?'))return;
    const next=pcList.filter(x=>x.id!==id);
    setPcList(next);localStorage.setItem('pp_casos_particulares',JSON.stringify(next));
  };

  const temaColor={'M²':'var(--red-700)','Tinta':'var(--pumpkin-700)','Suportes':'var(--pumpkin-700)'};

  return(
    <div className="pp-tab-content"><div className="page">
      <div className="card">
        <div className="card-title">Casos Identificados <span className="ct">défice de M² / desvios de tinta e suportes</span></div>
        <div className="frow" style={{marginBottom:12}}>
          <input type="text" value={search} onChange={e=>setSearch(e.target.value)} placeholder="Pesquisar cliente ou contrato…"/>
          <select value={tema} onChange={e=>setTema(e.target.value)}><option value="">Todos os temas</option><option value="M²">M² (PVP a Rever)</option><option value="Tinta">Tinta</option><option value="Suportes">Suportes</option></select>
          <span className="rc">{filteredAlerts.length} de {alertRows.length}</span>
        </div>
        <div className="tbl-wrap">
          <table><thead><tr><th>Cliente</th><th>Contrato</th><th>Tema</th><th>Detalhe</th><th>Vendedor</th></tr></thead>
          <tbody>{filteredAlerts.length===0?<tr><td colSpan={5} style={{textAlign:'center',padding:24,color:'var(--fg-3)',fontSize:12}}>Nenhum caso identificado.</td></tr>:filteredAlerts.map((r,i)=><tr key={i}><td><div className="cname" title={r.cliente}>{r.cliente}</div></td><td><div className="ccode">{r.contrato}</div></td><td><span className="badge b-warn" style={{color:temaColor[r.tema]}}>{r.tema}</span></td><td style={{fontSize:11,color:'var(--fg-3)'}}>{r.detalhe}</td><td style={{fontSize:11,color:'var(--fg-3)'}}>{shortV(r.vendedor)}</td></tr>)}</tbody>
          </table>
        </div>
      </div>

      <div className="card" style={{marginTop:'var(--space-4)'}}>
        <div className="card-title">Casos Particulares <span className="ct">acordos e exceções registadas manualmente</span></div>
        <div id="alert-pc-list">
          {pcList.length===0?<p style={{fontSize:12,color:'var(--fg-3)',padding:'8px 0'}}>Nenhum caso particular registado.</p>:pcList.slice().reverse().map(item=><div key={item.id} className="alert-item pc"><div className="alert-body"><strong>{item.cliente}{item.contrato?' · '+item.contrato:''}</strong><span>{item.nota}</span><span style={{display:'block',marginTop:4,fontSize:10,color:'var(--fg-3)'}}>Registado em {item.data}</span></div><button className="pc-del" onClick={()=>deletePc(item.id)} title="Remover">×</button></div>)}
        </div>
        <div className="pc-form" style={{marginTop:12}}>
          <select value={pcCliente} onChange={e=>setPcCliente(e.target.value)}><option value="">Selecione o cliente…</option>{[...new Set(data.map(r=>r.cliente))].sort().map(c=><option key={c} value={c}>{c}</option>)}</select>
          <input type="text" value={pcContrato} onChange={e=>setPcContrato(e.target.value)} placeholder="Contrato (opcional)"/>
          <textarea value={pcNota} onChange={e=>setPcNota(e.target.value)} rows={2} placeholder="Descreva o acordo ou caso particular…"/>
          <button className="btn-primary" style={{alignSelf:'flex-start'}} onClick={addPc}>Adicionar registo</button>
        </div>
      </div>
    </div></div>
  );
};

/* ===== RentingTab ===== */
const RentingTab = ({data}) => {
  const [search, setSearch] = React.useState('');
  const [filter, setFilter] = React.useState('all');
  const [sortKey, setSortKey] = React.useState('deficitEuro');
  const [sortDir, setSortDir] = React.useState('asc');
  const [expanded, setExpanded] = React.useState({});
  const [rentingTypes, setRentingTypes] = React.useState(()=>{const m={};data.forEach(r=>{m[r.contrato]=r.tipo_renting||null;});return m;});
  const [rentingValores, setRentingValores] = React.useState(()=>{const m={};data.forEach(r=>{m[r.contrato]=r.renting_valor??null;});return m;});

  const rentingAll = React.useMemo(()=>{
    return data.filter(r=>(rentingTypes[r.contrato]||r.tipo_renting)==='Renting Interno').map(r=>{
      const rr={...r,tipo_renting:'Renting Interno',renting_valor:rentingValores[r.contrato]??null};
      return{...rr,...ppRentingAnalysis(rr)};
    });
  },[data,rentingTypes,rentingValores]);

  const filtered = React.useMemo(()=>{
    const q=search.toLowerCase();
    let arr=rentingAll.filter(r=>{
      if(filter==='cobre'&&r.status!=='COBRE')return false;
      if(filter==='naocobre'&&r.status!=='NAO_COBRE')return false;
      if(filter==='semvalor'&&r.status!=='SEM_VALOR')return false;
      if(q&&!r.cliente.toLowerCase().includes(q)&&!r.contrato.toLowerCase().includes(q))return false;
      return true;
    });
    arr.sort((a,b)=>{let va=a[sortKey],vb=b[sortKey];if(va==null)va=-Infinity;if(vb==null)vb=-Infinity;return sortDir==='asc'?(va<vb?-1:va>vb?1:0):(va>vb?-1:va<vb?1:0);});
    return arr;
  },[rentingAll,filter,search,sortKey,sortDir]);

  const badgeCount=rentingAll.filter(r=>r.status==='NAO_COBRE').length;
  const setRentingType=(contrato,value)=>{ppSetRentingType(contrato,value);setRentingTypes(prev=>({...prev,[contrato]:value||null}));};
  const setRentingValorState=(contrato,value)=>{const n=value===''?null:Number(value);ppSetRentingValor(contrato,n);setRentingValores(prev=>({...prev,[contrato]:n}));};

  return(
    <div className="pp-tab-content"><div className="page">
      {badgeCount>0&&<div style={{padding:'8px 14px',background:'color-mix(in srgb,var(--red-100) 60%,white)',borderRadius:8,marginBottom:12,fontSize:12,color:'var(--red-700)',fontWeight:600}}>{badgeCount} contrato{badgeCount>1?'s não cobrem':'não cobre'} a despesa de renting</div>}
      <div className="frow" style={{marginBottom:12}}>
        <input type="text" value={search} onChange={e=>setSearch(e.target.value)} placeholder="Pesquisar…"/>
        {[['all','Todos'],['cobre','Cobre'],['naocobre','Não cobre'],['semvalor','Sem valor']].map(([k,l])=><button key={k} className={`fb${filter===k?' on':''}${k==='naocobre'?' on-r':''&&filter===k}`} onClick={()=>setFilter(k)}>{l}</button>)}
        <span className="rc">{filtered.length} contratos Renting Interno</span>
      </div>
      <div className="tbl-wrap">
        <table><thead><tr>
          <th onClick={()=>{setSortDir(p=>sortKey==='cliente'?(p==='asc'?'desc':'asc'):'asc');setSortKey('cliente');}}>Cliente</th>
          <th>Contrato</th>
          <th style={{fontSize:11}}>Vendedor</th>
          <th style={{fontSize:11}}>Armazém</th>
          <th className="num" style={{fontSize:11}}>Valor Renting/mês</th>
          <th className="num" style={{fontSize:11}}>Meses</th>
          <th className="num" style={{fontSize:11}}>Custo Acumulado</th>
          <th className="num" style={{fontSize:11}}>Receita Acumulada</th>
          <th className="num" onClick={()=>{setSortDir(p=>sortKey==='deficitEuro'?(p==='asc'?'desc':'asc'):'asc');setSortKey('deficitEuro');}} style={{cursor:'pointer',fontSize:11}}>Défice/Superávit {sortKey==='deficitEuro'?sortDir==='asc'?'↑':'↓':''}</th>
          <th>Estado</th>
        </tr></thead>
        <tbody>{filtered.length===0?<tr><td colSpan={10} style={{textAlign:'center',padding:40,color:'var(--fg-3)',fontSize:12}}>Nenhum contrato Renting Interno{filter!=='all'?' com este filtro':''}.</td></tr>:filtered.map(r=>{
          const safeId=r.contrato.replace(/[^a-zA-Z0-9]/g,'_');
          const badge=r.status==='SEM_VALOR'?<span className="badge b-neutral">sem valor</span>:r.status==='COBRE'?<span className="badge b-acima">▲ Cobre</span>:<span className="badge b-abaixo">▼ Não cobre</span>;
          return(<React.Fragment key={r.contrato}>
            <tr style={{cursor:'pointer'}} onClick={()=>setExpanded(prev=>({...prev,[safeId]:!prev[safeId]}))}>
              <td><span style={{color:'var(--fg-3)',display:'inline-block',width:12,marginRight:4}}>{expanded[safeId]?'▾':'▸'}</span><div className="cname" style={{display:'inline-block'}} title={r.cliente}>{r.cliente}</div></td>
              <td><div className="ccode">{r.contrato}</div></td>
              <td style={{fontSize:11,color:'var(--fg-3)'}}>{shortV(r.vendedor)}</td>
              <td style={{fontSize:11,color:'var(--fg-3)'}}>{r.armazem||'—'}</td>
              <td className="num">{r.renting_valor!=null?fmtEFull(r.renting_valor):'—'}</td>
              <td className="num">{r.hasHistory?`${r.count}/${r.num_rendas}`:'—'}</td>
              <td className="num">{r.custoAcumulado!=null?fmtEFull(r.custoAcumulado):'—'}</td>
              <td className="num">{r.receitaAcumulada!=null?fmtEFull(r.receitaAcumulada):'—'}</td>
              <td className="num" style={{fontWeight:700,color:r.deficitEuro==null?'var(--fg-3)':r.deficitEuro>=0?'var(--green-700)':'var(--red-700)'}}>{r.deficitEuro!=null?(r.deficitEuro>=0?'+':'')+fmtEFull(r.deficitEuro):'—'}</td>
              <td>{badge}</td>
            </tr>
            {expanded[safeId]&&<tr><td colSpan={10} style={{background:'var(--grey-50)',padding:'12px 20px'}}>
              <div style={{marginBottom:8,fontSize:12}}>
                <strong>Tipo de Renting:</strong>{' '}
                <select value={rentingTypes[r.contrato]||''} onChange={e=>setRentingType(r.contrato,e.target.value)} style={{fontSize:12,marginLeft:8}}><option value="">Não definido</option><option value="Renting Interno">Renting Interno</option><option value="Renting Externo">Renting Externo</option></select>
                {' '}<strong style={{marginLeft:16}}>Valor mensal (€):</strong>{' '}
                <input type="number" value={rentingValores[r.contrato]??''} onChange={e=>setRentingValorState(r.contrato,e.target.value)} style={{width:100,fontSize:12,marginLeft:8}} placeholder="ex: 350"/>
              </div>
              {r.hasHistory&&<table style={{width:'100%',borderCollapse:'collapse',fontSize:11}}><thead><tr style={{borderBottom:'1px solid var(--border-1)'}}><th style={{textAlign:'left',padding:'3px 6px',color:'var(--fg-3)'}}>Mês</th><th style={{textAlign:'right',padding:'3px 6px',color:'var(--fg-3)'}}>m²</th><th style={{textAlign:'right',padding:'3px 6px',color:'var(--fg-3)'}}>Receita</th><th style={{textAlign:'right',padding:'3px 6px',color:'var(--fg-3)'}}>Margem</th></tr></thead><tbody>{r.rows.map(row=><tr key={row.mes} style={{borderBottom:'1px solid var(--border-1)'}}><td style={{padding:'3px 6px'}}>{row.mes}</td><td style={{textAlign:'right',padding:'3px 6px'}}>{fmt(row.m2)}</td><td style={{textAlign:'right',padding:'3px 6px'}}>{fmtEFull(row.receita)}</td><td style={{textAlign:'right',padding:'3px 6px',fontWeight:700,color:row.margem==null?'var(--fg-3)':row.margem>=0?'var(--green-700)':'var(--red-700)'}}>{row.margem==null?'—':(row.margem>=0?'+':'')+fmtEFull(row.margem)}</td></tr>)}</tbody></table>}
            </td></tr>}
          </React.Fragment>);
        })}</tbody>
        </table>
      </div>
    </div></div>
  );
};

/* ===== RepoTab ===== */
const RepoTab = ({data}) => {
  const [search, setSearch] = React.useState('');
  const [selected, setSelected] = React.useState(null);
  const [attachments, setAttachments] = React.useState({});

  const clientsSorted = React.useMemo(()=>[...new Set(data.map(r=>r.cliente))].sort(),[data]);
  const filteredClients = React.useMemo(()=>{const q=search.toLowerCase();return clientsSorted.filter(c=>c.toLowerCase().includes(q));},[clientsSorted,search]);

  const loadAttachment = async (contrato) => {
    const rec = await ppAttachGet(contrato);
    setAttachments(prev=>({...prev,[contrato]:rec||null}));
  };

  const selectClient = async (cliente) => {
    setSelected(cliente);
    const contratos = data.filter(r=>r.cliente===cliente);
    for(const r of contratos) loadAttachment(r.contrato);
  };

  const handleFile = (contrato, file) => {
    if(!file) return;
    const reader = new FileReader();
    reader.onload = () => {
      ppAttachPut({contrato,filename:file.name,type:file.type,size:file.size,data:reader.result}).then(()=>loadAttachment(contrato));
    };
    reader.readAsArrayBuffer(file);
  };

  const removeAttach = async (contrato) => {
    if(!confirm('Remover o ficheiro anexado?'))return;
    await ppAttachDelete(contrato);
    setAttachments(prev=>({...prev,[contrato]:undefined}));
  };

  const viewAttach = (contrato) => {
    const rec=attachments[contrato];if(!rec)return;
    const blob=new Blob([rec.data],{type:rec.type||'application/pdf'});
    window.open(URL.createObjectURL(blob),'_blank');
  };

  const repoEstadoLabel = (estado) => {
    if(estado==='confiante')return{cls:'b-acima',label:'Confiante'};
    if(estado==='revisar')return{cls:'b-warn',label:'A confirmar'};
    if(estado==='ambiguo')return{cls:'b-warn',label:'Ambíguo'};
    return{cls:'b-abaixo',label:'Não encontrado'};
  };

  const selectedContratos = selected ? data.filter(r=>r.cliente===selected) : [];

  return(
    <div className="pp-tab-content"><div className="page" style={{padding:0}}>
      <div className="comm-layout">
        <div className="comm-sidebar">
          <div className="comm-sidebar-header"><h3>Clientes</h3><input type="text" value={search} onChange={e=>setSearch(e.target.value)} placeholder="Pesquisar cliente…"/></div>
          <div className="comm-client-list">
            {filteredClients.map(c=><div key={c} className={`comm-client-item${selected===c?' active':''}`} onClick={()=>selectClient(c)}><div><strong style={{fontSize:13}}>{c}</strong></div></div>)}
          </div>
        </div>
        <div className="comm-main">
          <div className="comm-toolbar"><h3 id="repo-toolbar-title">{selected||'Selecione um cliente para ver os contratos'}</h3></div>
          <div id="repo-cards" style={{padding:'16px 24px'}}>
            {selectedContratos.map(r=>{
              const entry=DOC_MATCH[r.contrato]||['nao_encontrado',[]];
              const est=repoEstadoLabel(entry[0]);
              const attach=attachments[r.contrato];
              return(<div key={r.contrato} className="pp-contract-card">
                <div className="pp-contract-header"><div><div className="pp-contract-code">{r.contrato}</div><div className="pp-contract-sub">{r.vendedor||''} · {r.armazem||''}</div></div><span className={`badge ${est.cls}`}>{est.label}</span></div>
                <div className="repo-contract-body">
                  <div>
                    <div className="repo-section-label">Documentos sugeridos (na pasta de contratos)</div>
                    {entry[1].length===0?<div className="repo-suggest-empty">Sem sugestão — não encontrado no arquivo.</div>:<div className="repo-suggest-list">{entry[1].map(([path,signed],idx)=>{const parts=path.split(/[\\/]/);const file=parts.pop();const folder=parts.join('/');return(<div key={idx} className={`repo-suggest-item${signed?' signed':''}`}><span className="repo-folder">{folder}/</span><span className="repo-file">{file}</span></div>);})}</div>}
                  </div>
                  <div>
                    <div className="repo-section-label">Ficheiro anexado</div>
                    {attach===undefined?<span style={{color:'var(--fg-3)',fontSize:12}}>A carregar…</span>:attach?<div className="pp-attach-file"><div className="pp-af-info"><span style={{fontWeight:600,fontSize:12}}>{attach.filename}</span><span style={{fontSize:11,color:'var(--fg-3)'}}>{Math.round(attach.size/1024)} KB</span></div><div style={{display:'flex',gap:6}}><button className="pp-btn-mini" onClick={()=>viewAttach(r.contrato)}>Ver</button><button className="pp-btn-mini" style={{color:'var(--red-700)'}} onClick={()=>removeAttach(r.contrato)}>Remover</button></div></div>:<div className="pp-attach-empty"><span>Nenhum ficheiro anexado</span><label className="pp-btn-mini" style={{cursor:'pointer'}}>Adicionar<input type="file" style={{display:'none'}} onChange={e=>handleFile(r.contrato,e.target.files[0])}/></label></div>}
                  </div>
                </div>
              </div>);
            })}
          </div>
        </div>
      </div>
    </div></div>
  );
};

/* ===== CommTab ===== */
const CommTab = ({data}) => {
  const [search, setSearch] = React.useState('');
  const [selected, setSelected] = React.useState(null);
  const [topic, setTopic] = React.useState('m2');

  const allClients = React.useMemo(()=>CLIENTS_LIST.filter(commNeedsComm),[]);
  const filtered = React.useMemo(()=>{const q=search.toLowerCase();return allClients.filter(c=>c.cliente.toLowerCase().includes(q));},[allClients,search]);

  const selectClient = (name) => {
    setSelected(name);
    const c=CLIENTS_LIST.find(x=>x.cliente===name);
    if(c) setTopic(c.has_m2_issue?'m2':'consumo');
  };

  const emailHTML = React.useMemo(()=>{
    if(!selected) return '';
    return buildEmailHTML(selected,topic,data);
  },[selected,topic,data]);

  const copyEmail = () => {
    const zone=document.getElementById('email-print-zone');if(!zone)return;
    const text=zone.innerText;
    navigator.clipboard.writeText(text).then(()=>{alert('Email copiado para a área de transferência');}).catch(()=>{});
  };

  const selectedClient = selected && CLIENTS_LIST.find(c=>c.cliente===selected);
  const hasM2 = selectedClient && selectedClient.has_m2_issue;
  const hasConsumo = selectedClient && (selectedClient.has_tinta_issue||selectedClient.has_suporte_issue);

  return(
    <div className="pp-tab-content"><div className="page" style={{padding:0}}>
      <div className="comm-layout">
        <div className="comm-sidebar">
          <div className="comm-sidebar-header"><h3>Clientes</h3><input type="text" value={search} onChange={e=>setSearch(e.target.value)} placeholder="Pesquisar cliente…"/></div>
          <div className="comm-client-list">
            {filtered.map(c=>{
              const flags=[];
              if(c.has_m2_issue)flags.push(<span key="m2" className="badge b-exp" style={{fontSize:'9px',padding:'1px 6px'}}>M²</span>);
              if(c.has_tinta_issue)flags.push(<span key="t" className="badge b-warn" style={{fontSize:'9px',padding:'1px 6px'}}>Tinta</span>);
              if(c.has_suporte_issue)flags.push(<span key="s" className="badge b-warn" style={{fontSize:'9px',padding:'1px 6px'}}>Suportes</span>);
              return(<div key={c.cliente} className={`comm-client-item${selected===c.cliente?' active':''}`} onClick={()=>selectClient(c.cliente)}>
                <div className="comm-client-dot" style={{background:'var(--pumpkin-700)',marginTop:5}}/>
                <div>
                  <div className="comm-client-name">{c.cliente}</div>
                  <div className="comm-client-meta">{c.n_contratos} contrato{c.n_contratos>1?'s':''} · {shortV(c.vendedor)}</div>
                  {flags.length>0&&<div className="comm-client-flags">{flags}</div>}
                </div>
              </div>);
            })}
          </div>
        </div>
        <div className="comm-main">
          <div className="comm-toolbar">
            <h3>{selected||'Selecione um cliente para gerar a comunicação'}</h3>
            {selected&&<><button className="btn-secondary" onClick={copyEmail}><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copiar</button><button className="btn-primary" onClick={()=>window.print()}>Exportar PDF</button></>}
          </div>
          {selected&&hasM2&&hasConsumo&&<div style={{padding:'8px 20px',display:'flex',gap:8}}>
            <button className={`an-tab${topic==='m2'?' active':''}`} onClick={()=>setTopic('m2')}>Défice de M²</button>
            <button className={`an-tab${topic==='consumo'?' active':''}`} onClick={()=>setTopic('consumo')}>Desvios de Consumo</button>
          </div>}
          <div className="email-preview-wrap" dangerouslySetInnerHTML={{__html:emailHTML||'<div class="empty-state"><svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg><h4>Nenhum cliente selecionado</h4><p>Escolha um cliente na lista para gerar o email de ponto de situação.</p></div>'}}/>
        </div>
      </div>
    </div></div>
  );
};

/* ===== PrintPlanScreen (main) ===== */
const PrintPlanScreen = () => {
  const [tab, setTab] = React.useState('overview');
  const [analysisRow, setAnalysisRow] = React.useState(null);

  const TABS = [
    {k:'overview',l:'Overview'},
    {k:'m2',l:'Dashboard M²'},
    {k:'alertas',l:()=>{const n=DATA.filter(r=>r.expirado||r.novo_pvp!=null).length;return<>Alertas{n>0&&<span className="tab-pill">{n}</span>}</>;}},
    {k:'renting',l:'Renting Interno'},
    {k:'repo',l:'Repositório de Contratos'},
    {k:'comunicacao',l:<><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{marginRight:5}}><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>Comunicação</>},
  ];

  return(
    <div className="pp-root">
      <div className="pp-tabnav">
        <div className="topbar-logo" style={{padding:'0 16px 0 0',display:'flex',alignItems:'center'}}>
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" style={{marginRight:8}}><rect width="24" height="24" rx="6" fill="#3859D0"/><path d="M6 8h8a4 4 0 0 1 0 8H6V8Z" fill="#fff" opacity=".9"/><path d="M6 12h6" stroke="#fff" strokeWidth="2" strokeLinecap="round"/></svg>
          <span style={{color:'var(--fg-1)',fontWeight:700,fontSize:13,fontFamily:'var(--font-sans)'}}>PrintPlan</span>
        </div>
        {TABS.map(t=><button key={t.k} className={`tab-btn${tab===t.k?' active':''}`} onClick={()=>setTab(t.k)}>{typeof t.l==='function'?t.l():t.l}</button>)}
        <span className="topbar-date" style={{marginLeft:'auto',paddingRight:16}}>{new Date().toLocaleDateString('pt-PT',{day:'2-digit',month:'long',year:'numeric'})}</span>
      </div>
      {tab==='overview'&&<OverviewTab data={DATA}/>}
      {tab==='m2'&&<M2Tab data={DATA} onOpenAnalysis={r=>setAnalysisRow(r)}/>}
      {tab==='alertas'&&<AlertasTab data={DATA}/>}
      {tab==='renting'&&<RentingTab data={DATA}/>}
      {tab==='repo'&&<RepoTab data={DATA}/>}
      {tab==='comunicacao'&&<CommTab data={DATA}/>}
      {analysisRow&&<AnalysisModal row={analysisRow} onClose={()=>setAnalysisRow(null)}/>}
    </div>
  );
};

window.PrintPlanScreen = PrintPlanScreen;
