{"id":10284,"date":"2026-03-19T16:26:43","date_gmt":"2026-03-19T15:26:43","guid":{"rendered":"https:\/\/www.rldatix.com\/en-uki\/?page_id=10284"},"modified":"2026-04-07T14:52:14","modified_gmt":"2026-04-07T13:52:14","slug":"legacy-data-roi-calculator","status":"publish","type":"page","link":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/","title":{"rendered":"ROI Calculator"},"content":{"rendered":"\n        <section id=\"\" class=\"hero-primary text-bg-teal pt-46 overflow-hidden position-relative\">\n        <div class=\"container position-relative z-2\">\n            <div class=\"breadcrumb  text-center text-md-start mb-10 mb-md-21\"><span><span><a href=\"https:\/\/www.rldatix.com\/en-uki\/\"><i class=\"icon-home\"><\/i><span class=\"hide-breadcrumb-text\">RLDatix \u2013 UK &amp; Ireland<span><\/a><\/span><\/span><\/div>        <\/div>\n        <div class=\"container-small\">\n            \n\n<h5 class=\"wp-block-heading text-small-normal fw-bold mb-2 has-text-align-center\" class=\"wp-block-heading text-small-normal fw-bold mb-2 has-text-align-center\" id=\"\">\n\t\t\t\t<\/h5>\n\n\n\n<h1 class=\"wp-block-heading heading-h1 mb-4 has-text-align-center\" class=\"wp-block-heading heading-h1 mb-4 has-text-align-center\" id=\"legacy-data-migration-and-archiving-roi-calculator\">\n\t\t\t\tLegacy data migration and archiving <br>ROI calculator<\/h1>\n\n\n\n<p class=\"text-medium-normal mb-8 has-text-align-center\">Migrating to a new EPR, or looking to decommission legacy systems? Quickly estimate your potential costs saved, clinician time freed, and the patient safety case.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-block-buttons-is-layout-flex\"><\/div>\n\n\n        <\/div>\n\n\n\n        <div\n            class=\"img-wrapper text-center position-relative mt-10 mt-lg-18\">\n\n            \n        <\/div>\n\n        \n    <\/section>\n\n\n\n\n\n    \n\n    <section\n        id=\"\"\n        style=\"background-image:url()\"\n        class=\"statement text-bg-white py-17 py-md-26 overflow-hidden has-intro\"\n        data-bs-theme=\"white\">\n\n        <div class=\"container-medium position-relative z-2\">\n            <div class=\"mb-0\">\n                \n\n<h6 class=\"wp-block-heading text-small-normal has-text-align-center fw-bold mc-2\" class=\"wp-block-heading text-small-normal has-text-align-center fw-bold mc-2\" id=\"\">\n\t\t\t\t<\/h6>\n\n\n\n<h3 class=\"wp-block-heading section-title heading-h3 mb-4 has-text-align-center\" class=\"wp-block-heading section-title heading-h3 mb-4 has-text-align-center\" id=\"\">\n\t\t\t\t<\/h3>\n\n\n\n<p class=\"mb-8 has-text-align-center ms-auto me-auto\">Simply enter your aims, bed count, legacy systems and programme stage. The calculator models decommission savings, clinician time freed and patient safety impact, then generates a personalised PDF report you can save, share and discuss.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-block-buttons-is-layout-flex\">\n\n<\/div>\n\n\n            <\/div>\n        <\/div>\n\n        \n    <\/section>\n\n\n\n\n\n<style>\n    *, *::before, *::after { box-sizing: border-box; }\n    body { margin: 0; padding: 0; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background: transparent; overflow-x: hidden; }\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   ROI Calculator \u2014 Animations & Interactions\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n\/* \u2500\u2500 Keyframe animations \u2500\u2500 *\/\n@keyframes pulse-btn {\n  0%, 100% { box-shadow: 0 2px 12px rgba(15,65,70,.3); }\n  50% { box-shadow: 0 2px 24px rgba(15,65,70,.55); }\n}\n@keyframes fade-in {\n  from { opacity: 0; transform: translateY(12px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n@keyframes think-fill {\n  0% { width: 0%; }\n  15% { width: 20%; }\n  40% { width: 45%; }\n  65% { width: 70%; }\n  85% { width: 88%; }\n  100% { width: 100%; }\n}\n@keyframes think-pulse {\n  0%, 100% { opacity: .4; }\n  50% { opacity: 1; }\n}\n@keyframes calc-fade {\n  from { opacity: 0; transform: translateY(6px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n@keyframes calc-spin {\n  to { transform: rotate(360deg); }\n}\n\n\/* \u2500\u2500 Card hover \u2500\u2500 *\/\n.roi-card {\n  transition: border-color .2s ease, box-shadow .2s ease, transform .15s ease;\n}\n.roi-card:hover {\n  border-color: rgba(15,65,70,.25) !important;\n  box-shadow: 0 2px 12px rgba(15,65,70,.08);\n}\n\n\/* \u2500\u2500 Generic button hover \u2500\u2500 *\/\n.roi-btn {\n  transition: border-color .2s ease, box-shadow .2s ease, transform .15s ease, background .15s ease;\n}\n.roi-btn:hover {\n  border-color: rgba(15,65,70,.35) !important;\n  box-shadow: 0 2px 16px rgba(15,65,70,.1);\n  transform: translateY(-1px);\n}\n.roi-btn:active {\n  transform: translateY(0);\n  box-shadow: 0 1px 4px rgba(15,65,70,.12);\n}\n\n\/* \u2500\u2500 Toggle pill (overrides roi-btn lift for flush buttons) \u2500\u2500 *\/\n.roi-toggle-pill .roi-btn {\n  transform: none !important;\n  box-shadow: none !important;\n  border-color: transparent !important;\n}\n.roi-toggle-pill .roi-btn:hover {\n  transform: none !important;\n  box-shadow: none !important;\n  opacity: .85;\n}\n\n\/* \u2500\u2500 Preset buttons \u2500\u2500 *\/\n.roi-preset {\n  transition: border-color .2s ease, box-shadow .2s ease, transform .15s ease, background .15s ease;\n}\n.roi-preset:hover {\n  border-color: rgba(15,65,70,.3) !important;\n  box-shadow: 0 4px 20px rgba(15,65,70,.1);\n  transform: translateY(-1px);\n}\n.roi-preset:active {\n  transform: translateY(0);\n}\n\n\/* \u2500\u2500 Tier cards \u2500\u2500 *\/\n.roi-tier {\n  transition: border-color .2s ease, box-shadow .2s ease;\n}\n.roi-tier:hover {\n  box-shadow: 0 2px 14px rgba(15,65,70,.08);\n  border-color: rgba(15,65,70,.2) !important;\n}\n\n\/* \u2500\u2500 Text links \u2500\u2500 *\/\n.roi-link {\n  transition: color .15s ease, opacity .15s ease;\n}\n.roi-link:hover {\n  opacity: .7;\n}\n\n\/* \u2500\u2500 Clickable cost display \u2500\u2500 *\/\n.roi-cost {\n  transition: border-color .2s ease, color .15s ease;\n}\n.roi-cost:hover {\n  border-bottom-color: rgba(15,65,70,.5) !important;\n}\n\n\/* \u2500\u2500 Result cards \u2500\u2500 *\/\n.roi-result {\n  transition: box-shadow .2s ease, transform .15s ease, border-color .2s ease;\n}\n.roi-result:hover {\n  box-shadow: 0 4px 20px rgba(15,65,70,.1);\n  transform: translateY(-2px);\n  border-color: rgba(15,65,70,.15) !important;\n}\n\n\/* \u2500\u2500 Scenario chooser \u2500\u2500 *\/\n.roi-scenario {\n  transition: border-color .2s ease, box-shadow .2s ease, transform .15s ease, background .15s ease;\n}\n.roi-scenario:hover {\n  border-color: rgba(15,65,70,.3) !important;\n  box-shadow: 0 2px 14px rgba(15,65,70,.1);\n  transform: translateY(-1px);\n}\n.roi-scenario:active {\n  transform: translateY(0);\n}\n\n\/* \u2500\u2500 Flagship rows \u2500\u2500 *\/\n.roi-flag {\n  transition: border-color .2s ease, box-shadow .2s ease;\n}\n.roi-flag:hover {\n  border-color: rgba(15,65,70,.2) !important;\n  box-shadow: 0 2px 10px rgba(15,65,70,.06);\n}\n\n\/* \u2500\u2500 Tab buttons \u2500\u2500 *\/\n.roi-tab {\n  transition: background .15s ease, color .15s ease;\n}\n.roi-tab:hover {\n  background: rgba(15,65,70,.04) !important;\n}\n\n\/* \u2500\u2500 Slider hover \u2500\u2500 *\/\n.roi-slider {\n  transition: opacity .15s ease;\n}\n.roi-slider:hover input[type=\"range\"] {\n  filter: brightness(.95);\n}\n.roi-slider input[type=\"range\"] {\n  transition: filter .15s ease;\n}\n\n\/* \u2500\u2500 Per-org rows \u2500\u2500 *\/\n.roi-org {\n  transition: border-color .2s ease, box-shadow .2s ease;\n}\n.roi-org:hover {\n  border-color: rgba(15,65,70,.2) !important;\n  box-shadow: 0 1px 8px rgba(15,65,70,.06);\n}\n\n\/* \u2500\u2500 CTA button \u2500\u2500 *\/\n.roi-cta {\n  transition: box-shadow .2s ease, transform .15s ease;\n}\n.roi-cta:hover {\n  box-shadow: 0 4px 24px rgba(15,65,70,.2) !important;\n  transform: translateY(-1px);\n}\n.roi-cta:active {\n  transform: translateY(0);\n  box-shadow: 0 2px 8px rgba(15,65,70,.15) !important;\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   Mobile & Touch\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\n\/* On touch devices, disable hover transforms to prevent sticky states *\/\n@media (hover: none) {\n  .roi-btn:hover,\n  .roi-preset:hover,\n  .roi-result:hover,\n  .roi-scenario:hover {\n    transform: none !important;\n  }\n\n  \/* Use :active for tactile feedback instead *\/\n  .roi-btn:active,\n  .roi-preset:active,\n  .roi-scenario:active {\n    background: rgba(15,65,70,.06) !important;\n  }\n\n  .roi-result:active {\n    box-shadow: 0 2px 10px rgba(15,65,70,.1) !important;\n  }\n\n  .roi-tier:active {\n    border-color: rgba(15,65,70,.25) !important;\n  }\n}\n\n\/* Narrow screens *\/\n@media (max-width: 480px) {\n  \/* Ensure range inputs are fat enough to thumb-drag *\/\n  input[type=\"range\"] {\n    height: 32px;\n    min-height: 32px;\n  }\n\n  \/* Stack flagship rows vertically *\/\n  .roi-flag {\n    flex-direction: column !important;\n    align-items: stretch !important;\n    gap: 8px !important;\n  }\n\n  \/* Toggle pill: ensure text doesn't clip *\/\n  .roi-toggle-pill .roi-btn {\n    padding: 10px 12px !important;\n    font-size: 12px !important;\n  }\n}\n\n\n\/* Email form placeholder *\/\n.roi-lead-input::placeholder {\n  color: rgba(255, 255, 255, 0.5) !important;\n  opacity: 1;\n}\n.roi-lead-input::-webkit-input-placeholder { color: rgba(255, 255, 255, 0.5) !important; }\n.roi-lead-input::-moz-placeholder { color: rgba(255, 255, 255, 0.5) !important; }\n  <\/style>\n\n\n\n<div id=\"root\"><\/div>\n\n\n\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/react\/18.3.1\/umd\/react.production.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/react-dom\/18.3.1\/umd\/react-dom.production.min.js\"><\/script>\n  <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/babel-standalone\/7.26.4\/babel.min.js\"><\/script>\n  <script>\n    \/\/ Pre-load jsPDF with multi-CDN fallback\n    (function(){\n      var cdns = [\n        [\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.2\/jspdf.umd.min.js\",\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.8.4\/jspdf.plugin.autotable.min.js\"],\n        [\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf@2.5.2\/dist\/jspdf.umd.min.js\",\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf-autotable@3.8.4\/dist\/jspdf.plugin.autotable.min.js\"],\n        [\"https:\/\/unpkg.com\/jspdf@2.5.2\/dist\/jspdf.umd.min.js\",\"https:\/\/unpkg.com\/jspdf-autotable@3.8.4\/dist\/jspdf.plugin.autotable.min.js\"]\n      ];\n      function tryLoad(i){\n        if(i>=cdns.length||window.jspdf)return;\n        var s1=document.createElement(\"script\");s1.src=cdns[i][0];\n        s1.onload=function(){\n          var s2=document.createElement(\"script\");s2.src=cdns[i][1];\n          s2.onerror=function(){tryLoad(i+1);};\n          document.head.appendChild(s2);\n        };\n        s1.onerror=function(){tryLoad(i+1);};\n        document.head.appendChild(s1);\n      }\n      tryLoad(0);\n    })();\n  <\/script>\n\n  <script type=\"text\/babel\">\nconst { useState, useMemo, useCallback, useRef, useEffect } = React;\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   HOOKS\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\nfunction useCountUp(target, duration = 1200, active = true) {\n  const [display, setDisplay] = useState(0);\n  const ref = useRef({ from: 0, to: 0, startTime: 0, raf: null, hasRun: false });\n  useEffect(() => {\n    if (!active) { setDisplay(0); ref.current.hasRun = false; return; }\n    const r = ref.current;\n    const from = r.hasRun ? display : 0;\n    const dur = r.hasRun ? 600 : duration;\n    r.from = from; r.to = target; r.startTime = performance.now(); r.hasRun = true;\n    if (r.raf) cancelAnimationFrame(r.raf);\n    const tick = (now) => {\n      const p = Math.min((now - r.startTime) \/ dur, 1);\n      const eased = 1 - Math.pow(1 - p, 3);\n      setDisplay(r.from + (r.to - r.from) * eased);\n      if (p < 1) r.raf = requestAnimationFrame(tick);\n    };\n    r.raf = requestAnimationFrame(tick);\n    return () => { if (r.raf) cancelAnimationFrame(r.raf); };\n  }, [target, active]);\n  return Math.round(display);\n}\nfunction AnimatedNum({ value, active }) { const v = useCountUp(value, 1200, active); return <span>{v.toLocaleString(\"en-GB\")}<\/span>; }\nfunction AnimatedK({ value, active }) { const v = useCountUp(value, 1200, active); return <span>{v >= 1e6 ? `\u00a3${(v\/1e6).toFixed(1)}m` : v >= 1000 ? `\u00a3${Math.round(v\/1000).toLocaleString(\"en-GB\")}k` : `\u00a3${v.toLocaleString(\"en-GB\")}`}<\/span>; }\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   CALC ENGINE \u2013 tiered systems, bed-scaled costs, no parallel-run\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\nconst SCENARIO = {\n  CONSERVATIVE: { decom_pct: 0.85, realisation: 0.20, safety: 0.15 },\n  EXPECTED:     { decom_pct: 1.00, realisation: 0.30, safety: 0.25 },\n  STRETCH:      { decom_pct: 1.10, realisation: 0.40, safety: 0.35 },\n};\nconst CX = { LOW: 0.7, TYPICAL: 1.0, HIGH: 1.45 };\nconst DQ = { CLEAN: 0.75, MIXED: 1.0, POOR: 1.4 };\n\nfunction tierCost(tier, beds, cx) {\n  const base = tier === \"enterprise\" ? 300000 : tier === \"departmental\" ? 75000 : 14000;\n  const perBed = tier === \"enterprise\" ? 700 : tier === \"departmental\" ? 160 : 20;\n  return Math.round(((base + beds * perBed) * cx) \/ 1000) * 1000;\n}\nfunction clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)); }\n\nfunction calc(inp, mode, ov = {}, flagships = []) {\n  const sc = SCENARIO[mode], cx = CX[inp.complexity_level], dq = DQ[inp.data_quality_level];\n  const isArchiveOnly = inp.journey === \"HAVE_EPR\";\n  const ent = ov.enterprise != null ? ov.enterprise : inp.tiers.enterprise;\n  const dep = ov.departmental != null ? ov.departmental : inp.tiers.departmental;\n  const nic = ov.niche != null ? ov.niche : inp.tiers.niche;\n  const tieredLegacy = ent + dep + nic;\n  const entCost = ov.entCost != null ? ov.entCost : tierCost(\"enterprise\", inp.bed_count, cx);\n  const depCost = ov.depCost != null ? ov.depCost : tierCost(\"departmental\", inp.bed_count, cx);\n  const nicCost = ov.nicCost != null ? ov.nicCost : tierCost(\"niche\", inp.bed_count, cx);\n  const tieredEstate = ent * entCost + dep * depCost + nic * nicCost;\n  \/\/ Flagships\n  const flagshipTotal = flagships.reduce((s,f) => s + (f.cost || 0), 0);\n  const flagshipDecomSave = flagships.filter(f => f.retire).reduce((s,f) => s + (f.cost || 0), 0);\n  const flagshipCount = flagships.length;\n  const flagshipRetireCount = flagships.filter(f => f.retire).length;\n  const legacy = tieredLegacy + flagshipCount;\n  const totalEstate = tieredEstate + flagshipTotal;\n  const blendedCost = legacy > 0 ? Math.round(totalEstate \/ legacy) : 0;\n  const rawDecom = tieredLegacy * inp.decom_retire_rate * sc.decom_pct;\n  const decom = Math.min(Math.round(rawDecom), tieredLegacy) + flagshipRetireCount;\n  const decomFrac = tieredLegacy > 0 ? Math.min(Math.round(rawDecom), tieredLegacy) \/ tieredLegacy : 0;\n  const entDecom = Math.min(Math.round(ent * decomFrac), ent);\n  const depDecom = Math.min(Math.round(dep * decomFrac), dep);\n  const nicDecom = clamp(Math.min(Math.round(rawDecom), tieredLegacy) - entDecom - depDecom, 0, nic);\n  const decomSave = entDecom * entCost + depDecom * depCost + nicDecom * nicCost + Math.round(flagshipDecomSave * sc.decom_pct);\n  const clinicians = ov.clinicians != null ? ov.clinicians : Math.round(inp.bed_count * STAFF_PER_BED + inp.org_count * CORPORATE_STAFF_PER_ORG);\n  const switchPenalty = Math.max(0, legacy - 1) * SWITCH_PENALTY_PER_SYSTEM;\n  const baseMin = isArchiveOnly ? 5 : 12;\n  const minsWasted = ov.minsWasted != null ? ov.minsWasted : Math.round(baseMin * dq * cx * (1 + switchPenalty));\n  const residual = isArchiveOnly ? 1 : 2;\n  const hrsSaved = Math.round((clinicians * Math.max(0, minsWasted - residual) * WORKING_WEEKS) \/ 60);\n  const timeSave = Math.round(hrsSaved * BLENDED_HOURLY_RATE * sc.realisation);\n  const ticketsBaselineMonthly = ov.ticketsBaseline != null ? ov.ticketsBaseline : Math.round(legacy * TICKETS_PER_SYSTEM * dq);\n  const ticketsAfter = Math.min(Math.round((legacy - decom) * TICKETS_PER_SYSTEM * dq * SURVIVING_SYSTEM_TICKET_FACTOR), ticketsBaselineMonthly);\n  const ticketsReductionPct = Math.round((ticketsBaselineMonthly - ticketsAfter) \/ Math.max(1, ticketsBaselineMonthly) * 100);\n  const sarDaysBefore = ov.sarDaysBefore != null ? ov.sarDaysBefore : Math.round((SAR_BASE_DAYS + legacy * SAR_DAYS_PER_SYSTEM_BEFORE * dq) * 10) \/ 10;\n  const sarDaysAfter = Math.min(Math.round((SAR_BASE_DAYS + (legacy - decom) * SAR_DAYS_PER_SYSTEM_AFTER) * 10) \/ 10, sarDaysBefore);\n  const sarReductionPct = Math.round((sarDaysBefore - sarDaysAfter) \/ Math.max(1, sarDaysBefore) * 100);\n  const hasClinicalScope = legacy >= 3 && inp.bed_count > 0;\n  let safetyMedErrorsAvoided=0, safetyPatientsProtected=0, safetyBedDaysAvoided=0, safetyMedErrorsBaseline=0;\n  if (hasClinicalScope) {\n    const frag = 0.6 + Math.min(legacy, 20) \/ 20 * 0.6;\n    safetyMedErrorsBaseline = Math.round(inp.bed_count * MED_ERRORS_PER_BED * frag * dq);\n    const harmedBl = Math.round(inp.bed_count * PATIENTS_HARMED_PER_BED * frag * dq);\n    const bedDaysBl = Math.round(inp.bed_count * EXCESS_BED_DAYS_PER_BED * frag * dq);\n    safetyMedErrorsAvoided = Math.round(safetyMedErrorsBaseline * sc.safety);\n    safetyPatientsProtected = Math.round(harmedBl * sc.safety);\n    safetyBedDaysAvoided = Math.round(bedDaysBl * sc.safety);\n  }\n  const annual = decomSave + timeSave;\n  const total3 = annual * 3;\n  return {\n    legacy, ent, dep, nic, entCost, depCost, nicCost, blendedCost, totalEstate,\n    decom, entDecom, depDecom, nicDecom, decomSave,\n    clinicians, minsWasted, hrsSaved, timeSave, baseMin,\n    ticketsBaselineMonthly, ticketsAfter, ticketsReductionPct,\n    sarDaysBefore, sarDaysAfter, sarReductionPct,\n    hasClinicalScope, safetyMedErrorsAvoided, safetyPatientsProtected,\n    safetyBedDaysAvoided, safetyMedErrorsBaseline,\n    annual, total3, realisation: sc.realisation, isArchiveOnly,\n    flagshipTotal, flagshipDecomSave: Math.round(flagshipDecomSave * sc.decom_pct),\n    flagshipCount, flagshipRetireCount,\n  };\n}\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   PRESETS\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\nconst emptyDt = () => ({ EPR_PAS:0,ED:0,MATERNITY:0,THEATRES:0,LAB:0,RAD_REPORTS:0,PACS_LINKS:0,DOC_ECM:0,SCANNED_NOTES:0,REFERRALS:0,COMMUNITY:0,MENTAL_HEALTH:0,OTHER:0 });\nconst PRESETS = {\n  SMALL: { label:\"Specialist \/ Smaller Trust\", desc:\"~250 beds \u00b7 1 org \u00b7 8 systems\", data:{ bed_count:250, org_count:1, journey:\"HAVE_EPR\", tiers:{enterprise:0,departmental:3,niche:5}, data_types:{...emptyDt(),EPR_PAS:1,DOC_ECM:1,SCANNED_NOTES:1,RAD_REPORTS:1}, complexity_level:\"LOW\", data_quality_level:\"MIXED\", decom_retire_rate:0.75 }},\n  TYPICAL: { label:\"Typical Acute Trust\", desc:\"~800 beds \u00b7 1 org \u00b7 15 systems\", data:{ bed_count:800, org_count:1, journey:\"EVALUATING\", tiers:{enterprise:1,departmental:5,niche:9}, data_types:{...emptyDt(),EPR_PAS:1,ED:1,THEATRES:1,LAB:1,RAD_REPORTS:1,DOC_ECM:1,SCANNED_NOTES:1}, complexity_level:\"TYPICAL\", data_quality_level:\"MIXED\", decom_retire_rate:0.75 }},\n  LARGE: { label:\"Large Acute Trust\", desc:\"~1400 beds \u00b7 1 org \u00b7 25 systems\", data:{ bed_count:1400, org_count:1, journey:\"EVALUATING\", tiers:{enterprise:2,departmental:8,niche:15}, data_types:{...emptyDt(),EPR_PAS:1,ED:1,THEATRES:1,LAB:2,RAD_REPORTS:1,DOC_ECM:2,SCANNED_NOTES:1,MATERNITY:1,COMMUNITY:1}, complexity_level:\"HIGH\", data_quality_level:\"MIXED\", decom_retire_rate:0.75 }},\n  REGIONAL: { label:\"Multi-Trust \/ Regional Programme\", desc:\"~3500 beds \u00b7 4 orgs \u00b7 70+ systems\", data:{ bed_count:3500, org_count:4, journey:\"EVALUATING\", tiers:{enterprise:5,departmental:22,niche:45}, data_types:{...emptyDt(),EPR_PAS:3,ED:2,THEATRES:2,LAB:3,RAD_REPORTS:2,DOC_ECM:3,SCANNED_NOTES:2,MATERNITY:2,COMMUNITY:2,MENTAL_HEALTH:1,REFERRALS:1}, complexity_level:\"HIGH\", data_quality_level:\"POOR\", decom_retire_rate:0.70 }},\n};\n\n\/\/ \u2500\u2500 Model constants \u2500\u2500\nconst STAFF_PER_BED = 2.8;\nconst CORPORATE_STAFF_PER_ORG = 80;\nconst BLENDED_HOURLY_RATE = 55;\nconst WORKING_WEEKS = 48;\nconst TICKETS_PER_SYSTEM = 2.5;\nconst SWITCH_PENALTY_PER_SYSTEM = 0.04;\nconst SURVIVING_SYSTEM_TICKET_FACTOR = 0.6;\nconst SAR_BASE_DAYS = 1.5;\nconst SAR_DAYS_PER_SYSTEM_BEFORE = 0.4;\nconst SAR_DAYS_PER_SYSTEM_AFTER = 0.15;\nconst MED_ERRORS_PER_BED = 18;\nconst PATIENTS_HARMED_PER_BED = 0.315;\nconst EXCESS_BED_DAYS_PER_BED = 0.365;\n\nconst fmt = n => \"\u00a3\" + Math.round(n).toLocaleString(\"en-GB\");\nconst fmtK = n => n >= 1e6 ? `\u00a3${(n\/1e6).toFixed(1)}m` : n >= 1000 ? `\u00a3${Math.round(n\/1000).toLocaleString(\"en-GB\")}k` : fmt(n);\nconst fmtNum = n => Math.round(n).toLocaleString(\"en-GB\");\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   THEME & SMALL COMPONENTS\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\nconst C = {\n  bg:\"#FFFFFF\",surface:\"#ffffff\",border:\"#d4e0dd\",borderLight:\"#e8efec\",\n  text:\"#0F4146\",textMid:\"#3d5a5e\",textMuted:\"#6e8a8e\",\n  accent:\"#0F4146\",accentLight:\"#EEF7F1\",accentPale:\"#f6fbf8\",\n  green:\"#0F4146\",greenPale:\"#EEF7F1\",\n  amber:\"#2a6e6e\",amberLight:\"#e8f5f3\",\n  teal:\"#0F4146\",tealLight:\"#BEFAF0\",tealPale:\"#e8faf6\",\n  navy:\"#0F4146\",navyMid:\"#1a5459\",\n  rose:\"#8a2e4a\",rosePale:\"#faf0f4\",\n  seafoam:\"#80F8E4\",seafoamLight:\"#BEFAF0\",\n  blue:\"#73D2E1\",blue75:\"rgba(115,210,225,0.75)\",blue50:\"rgba(115,210,225,0.5)\",blue25:\"rgba(115,210,225,0.25)\",\n  override:\"#1a6b5a\",overridePale:\"#f0faf6\"\n};\n\nfunction Card({children,style,dimmed,className}){return <div className={className||\"roi-card\"} style={{background:C.surface,border:`1px solid ${C.border}`,borderRadius:12,padding:\"24px 28px 20px\",marginBottom:0,opacity:dimmed?0.3:1,pointerEvents:dimmed?\"none\":\"auto\",filter:dimmed?\"grayscale(.5)\":\"none\",transition:\"opacity .4s,filter .4s\",...style}}>{children}<\/div>;}\n\nfunction InfoTip({text}){\n  const[show,setShow]=useState(false);const[pos,setPos]=useState(\"above\");const ref=useRef(null);\n  const enter=()=>{setShow(true);if(ref.current)setPos(ref.current.getBoundingClientRect().top<120?\"below\":\"above\");};\n  return <span ref={ref} style={{position:\"relative\",display:\"inline-flex\",marginLeft:4}} onMouseEnter={enter} onMouseLeave={()=>setShow(false)}>\n    <span style={{width:18,height:18,borderRadius:\"50%\",background:\"#e8ecf1\",color:C.textMid,display:\"inline-flex\",alignItems:\"center\",justifyContent:\"center\",fontSize:11,fontWeight:700,cursor:\"help\",border:\"1px solid #d0d5dd\",lineHeight:1}}>i<\/span>\n    {show && (\n      <span style={{\n        position: \"absolute\", [pos === \"above\" ? \"bottom\" : \"top\"]: \"calc(100% + 8px)\",\n        left: \"50%\", transform: \"translateX(-50%)\", background: C.navy, color: \"#fff\",\n        fontSize: 12, lineHeight: 1.5, padding: \"10px 14px\", borderRadius: 8,\n        width: 280, maxWidth: \"80vw\", boxShadow: \"0 4px 16px rgba(0,0,0,.22)\",\n        zIndex: 20, pointerEvents: \"none\", fontWeight: 400,\n      }}>\n        {text}\n        <span style={{\n          position: \"absolute\", [pos === \"above\" ? \"bottom\" : \"top\"]: -5,\n          left: \"50%\", transform: \"translateX(-50%) rotate(45deg)\",\n          width: 10, height: 10, background: C.navy,\n        }} \/>\n      <\/span>\n    )}\n  <\/span>;\n}\n\nfunction PencilBtn({onClick,isOverridden}){return<button type=\"button\" onClick={onClick} style={{width:24,height:24,border:`1px solid ${isOverridden?C.override:C.border}`,borderRadius:6,background:isOverridden?C.overridePale:\"#fff\",color:isOverridden?C.override:C.textMuted,fontSize:12,cursor:\"pointer\",display:\"inline-flex\",alignItems:\"center\",justifyContent:\"center\",padding:0,flexShrink:0}}>{\"\u270e\"}<\/button>;}\n\nfunction OverridableStat({label,computed,override,onOverride,prefix,suffix,step:st,info,inline}){\n  const[editing,setEditing]=useState(false);const val=override!=null?override:computed;const isOv=override!=null;const dc=isOv?C.override:C.text;\n  if(editing)return<div style={{minWidth:inline?0:130,flex:inline?undefined:\"1 1 130px\"}}>\n    {label&&<div style={{fontSize:12,fontWeight:600,color:C.textMuted,letterSpacing:\".01em\",marginBottom:2,display:\"flex\",alignItems:\"center\"}}>{label}{info&&<InfoTip text={info}\/>}<\/div>}\n    <div style={{ display: \"flex\", alignItems: \"center\", gap: 6 }}>\n      <input autoFocus type=\"number\" step={st || 1} defaultValue={val}\n        onBlur={e => { const v = parseFloat(e.target.value); if (!isNaN(v) && v !== computed) onOverride(v); else if (v === computed) onOverride(null); setEditing(false); }}\n        onKeyDown={e => { if (e.key === \"Enter\") e.target.blur(); if (e.key === \"Escape\") { onOverride(null); setEditing(false); } }}\n        style={{ width: 100, padding: \"4px 8px\", border: `2px solid ${C.accent}`, borderRadius: 6, fontSize: 16, fontWeight: 700, fontFamily: \"inherit\" }} \/>\n      <span style={{ fontSize: 11, color: C.textMuted }}>Enter {\"\u00b7\"} Esc reset<\/span>\n    <\/div><\/div>;\n  if(inline)return<span style={{display:\"inline-flex\",alignItems:\"center\",gap:6}}>\n    <span style={{fontSize:16,fontWeight:700,color:dc,background:isOv?C.overridePale:\"transparent\",padding:isOv?\"1px 6px\":0,borderRadius:4}}>{prefix||\"\"}{typeof val===\"number\"?fmtNum(val):val}{suffix||\"\"}<\/span>\n    <PencilBtn onClick={()=>setEditing(true)} isOverridden={isOv}\/>{info&&<InfoTip text={info}\/>}<\/span>;\n  return<div style={{minWidth:130,flex:\"1 1 130px\"}}>\n    {label&&<div style={{fontSize:12,fontWeight:600,color:C.textMuted,letterSpacing:\".01em\",marginBottom:2,display:\"flex\",alignItems:\"center\"}}>{label}{info&&<InfoTip text={info}\/>}<\/div>}\n    <div style={{display:\"flex\",alignItems:\"center\",gap:8}}>\n      <span style={{fontSize:26,fontWeight:800,color:dc,letterSpacing:\"-.5px\",background:isOv?C.overridePale:\"transparent\",padding:isOv?\"2px 10px\":0,borderRadius:6}}>{prefix||\"\"}{typeof val===\"number\"?fmtNum(val):val}{suffix||\"\"}<\/span>\n      <PencilBtn onClick={()=>setEditing(true)} isOverridden={isOv}\/>\n    <\/div>\n    {isOv&&<div style={{fontSize:11,color:C.override,marginTop:2}}>Your figure (est:{prefix||\"\"}{fmtNum(computed)}{suffix||\"\"}) <button type=\"button\" onClick={()=>onOverride(null)} style={{border:\"none\",background:\"none\",color:C.override,cursor:\"pointer\",fontSize:11,textDecoration:\"underline\",padding:0}}>reset<\/button><\/div>}\n  <\/div>;\n}\n\nfunction Stat({label,value,sub,color,info}){return<div style={{minWidth:130,flex:\"1 1 130px\"}}><div style={{fontSize:12,fontWeight:600,color:C.textMuted,letterSpacing:\".01em\",display:\"flex\",alignItems:\"center\"}}>{label}{info&&<InfoTip text={info}\/>}<\/div><div style={{fontSize:26,fontWeight:800,color:color||C.text,letterSpacing:\"-.5px\",margin:\"2px 0\"}}>{value}<\/div>{sub&&<div style={{fontSize:12,color:C.textMuted}}>{sub}<\/div>}<\/div>;}\nfunction ResultCard({ label, value, sub, bg, border, valueColor, icon, dimmed }) {\n  return (\n    <div className=\"roi-result\" style={{\n      background: bg, border: `1px solid ${border}`, borderRadius: 12,\n      padding: \"22px 24px\", flex: \"1 1 220px\", minWidth: 200,\n      opacity: dimmed ? 0.3 : 1, filter: dimmed ? \"grayscale(.5)\" : \"none\",\n      transition: \"all .3s\",\n    }}>\n      <div style={{ fontSize: 12, fontWeight: 600, letterSpacing: \".01em\", color: C.textMid, marginBottom: 2 }}>\n        {icon && <span style={{ marginRight: 6 }}>{icon}<\/span>}{label}\n      <\/div>\n      <div style={{ fontSize: 30, fontWeight: 800, letterSpacing: \"-.5px\", color: valueColor, margin: \"4px 0\" }}>{value}<\/div>\n      {sub && <div style={{ fontSize: 13, color: C.textMuted, lineHeight: 1.4 }}>{sub}<\/div>}\n    <\/div>\n  );\n}\nfunction Callout({bg,border,children}){return<div style={{padding:\"14px 18px\",background:bg,borderRadius:8,border:`1px solid ${border}`,fontSize:13,color:C.textMid,lineHeight:1.6}}>{children}<\/div>;}\n\nfunction TogglePill({active, onToggle, labelA, labelB, labelAText, labelBText}) {\n  return <div className=\"roi-toggle-pill\" style={{display:\"inline-flex\",borderRadius:8,border:`1px solid ${C.border}`,overflow:\"hidden\",background:C.bg}}>\n    {[{k:labelA,l:labelAText||labelA},{k:labelB,l:labelBText||labelB}].map(o => (\n      <button type=\"button\" key={o.l} onClick={()=>onToggle(o.k)}\n        className=\"roi-btn\"\n        style={{\n          padding:\"9px 18px\",fontSize:13,fontWeight:active===o.k?700:500,\n          border:\"none\",borderRadius:0,cursor:\"pointer\",\n          background:active===o.k?C.accent:\"transparent\",\n          color:active===o.k?\"#fff\":C.textMid,\n          transition:\"background .15s, color .15s\",\n        }}>\n        {o.l}\n      <\/button>\n    ))}\n  <\/div>;\n}\nfunction LockedConnector(){return<div style={{display:\"flex\",justifyContent:\"center\",padding:\"8px 0\"}}><div style={{width:2,height:28,background:C.border}}\/><\/div>;}\nfunction ContinuePrompt({ onClick, label }) {\n  return (\n    <div style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\", padding: \"14px 0\" }}>\n      <div style={{ width: 2, height: 18, background: `linear-gradient(to bottom,${C.border},${C.accent})` }} \/>\n      <button type=\"button\" onClick={onClick} className=\"roi-btn\" style={{\n        display: \"flex\", alignItems: \"center\", gap: 8, padding: \"10px 22px\",\n        background: C.accent, color: \"#fff\", border: \"none\", borderRadius: 24,\n        fontSize: 14, fontWeight: 700, cursor: \"pointer\",\n        boxShadow: \"0 2px 12px rgba(0,94,184,.3)\", animation: \"pulse-btn 2s infinite\",\n      }}>\n        {label} <span style={{ fontSize: 18 }}>{\"\\u2193\"}<\/span>\n      <\/button>\n      <div style={{ width: 2, height: 18, background: `linear-gradient(to bottom,${C.accent},${C.border})`, marginTop: 6 }} \/>\n    <\/div>\n  );\n}\n\nfunction AnimatedSlider({ value, min, max, step, onChange, label, suffix, display, hideRange }) {\n  const [av, setAv] = useState(value);\n  const [drag, setDrag] = useState(false);\n  const trackRef = useRef(null), animRef = useRef(null);\n  useEffect(() => { if (drag) return; if (animRef.current) cancelAnimationFrame(animRef.current); const from=av,to=value; if(from===to)return; const start=performance.now(); const tick=now=>{const p=Math.min((now-start)\/350,1);setAv(Math.round((from+(to-from)*(1-Math.pow(1-p,3)))\/step)*step);if(p<1)animRef.current=requestAnimationFrame(tick);}; animRef.current=requestAnimationFrame(tick); }, [value]);\n  const pct=((av-min)\/(max-min))*100;\n  return <div className=\"roi-slider\" style={{flex:\"1 1 200px\",minWidth:180}}>\n    <div style={{display:\"flex\",justifyContent:\"space-between\",alignItems:\"baseline\",marginBottom:6}}>\n      <label style={{fontSize:13,fontWeight:600,color:C.textMid}}>{label}<\/label>\n      <span style={{fontSize:20,fontWeight:800,color:C.accent}}>{display!=null?display:av}{suffix||\"\"}<\/span>\n    <\/div>\n    <div ref={trackRef} style={{position:\"relative\",height:24,cursor:\"pointer\",display:\"flex\",alignItems:\"center\"}}>\n      <div style={{position:\"absolute\",left:0,right:0,height:6,borderRadius:3,background:\"#e0e4ea\"}}\/>\n      <div style={{position:\"absolute\",left:0,width:`${pct}%`,height:6,borderRadius:3,background:C.accent,transition:drag?\"none\":\"width .3s\"}}\/>\n      <div style={{position:\"absolute\",left:`${pct}%`,width:18,height:18,borderRadius:\"50%\",background:\"#fff\",border:`3px solid ${C.accent}`,transform:\"translateX(-50%)\",boxShadow:\"0 1px 4px rgba(0,0,0,.15)\",transition:drag?\"none\":\"left .3s\",zIndex:1}}\/>\n      <input type=\"range\" min={min} max={max} step={step} value={av}\n        onInput={e=>{setDrag(true);const v=+e.target.value;setAv(v);onChange(v);}}\n        onMouseDown={()=>setDrag(true)} onMouseUp={()=>setDrag(false)}\n        onTouchStart={()=>setDrag(true)} onTouchEnd={()=>setDrag(false)}\n        style={{position:\"absolute\",width:\"100%\",height:\"100%\",opacity:0,cursor:\"pointer\",zIndex:2,margin:0}}\/>\n    <\/div>\n    {!hideRange&&<div style={{display:\"flex\",justifyContent:\"space-between\",fontSize:11,color:C.textMuted,marginTop:2}}><span>{min}{suffix||\"\"}<\/span><span>{max}{suffix||\"\"}<\/span><\/div>}\n  <\/div>;\n}\n\nconst th_={textAlign:\"left\",padding:\"10px 12px\",borderBottom:`2px solid ${C.border}`,fontWeight:600,fontSize:12,letterSpacing:\".01em\",color:C.textMuted};\nconst tdR_={padding:\"10px 12px\",borderBottom:`1px solid ${C.borderLight}`,textAlign:\"right\",fontVariantNumeric:\"tabular-nums\"};\n\n\/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n   MAIN COMPONENT\n   \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 *\/\n\nfunction ROICalculator() {\n  const [inputs, setInputs] = useState({...PRESETS.TYPICAL.data, data_types:{...PRESETS.TYPICAL.data.data_types}, tiers:{...PRESETS.TYPICAL.data.tiers}});\n  const [activePreset, setActivePreset] = useState(null);\n  const [scenarioMode, setScenarioMode] = useState(null);\n  const [page, setPage] = useState(\"inputs\");\n  const [calcStep, setCalcStep] = useState(0);\n  const [step, setStep] = useState(0);\n  const [thinking, setThinking] = useState(false);\n\n  \/\/ Per-org breakdowns (optional, for multi-org)\n  const [perOrgBeds, setPerOrgBeds] = useState(false);\n  const [orgBedsList, setOrgBedsList] = useState([800]);\n  const [perOrgSystems, setPerOrgSystems] = useState(false);\n  const [orgTiersList, setOrgTiersList] = useState([{enterprise:1,departmental:5,niche:9}]);\n\n  \/\/ Flagship systems (named high-cost outliers outside the tiers)\n  const [flagships, setFlagships] = useState([]);\n  const addFlagship = useCallback(() => setFlagships(p => [...p, {name:\"\", cost:250000, retire:true}]), []);\n  const removeFlagship = useCallback(i => setFlagships(p => p.filter((_,j)=>j!==i)), []);\n  const updateFlagship = useCallback((i, k, v) => { setFlagships(p => p.map((f,j)=>j===i?{...f,[k]:v}:f)); setActivePreset(null); }, []);\n\n  \/\/ Editable tier costs (inline override on input page)\n  const [tierCostEdits, setTierCostEdits] = useState({enterprise:null,departmental:null,niche:null});\n  const [editingTierCost, setEditingTierCost] = useState(null);\n\n  \/\/ Known costs mode: user enters total legacy spend directly\n  const [costMode, setCostMode] = useState(\"estimate\");\n  const [knownAnnualSpend, setKnownAnnualSpend] = useState(null);\n\n  const [overrides, setOverrides] = useState({enterprise:null,departmental:null,niche:null,entCost:null,depCost:null,nicCost:null,clinicians:null,minsWasted:null,ticketsBaseline:null,sarDaysBefore:null});\n  const [leadForm, setLeadForm] = useState({name:\"\",email:\"\",org:\"\",role:\"\"});\n  const [leadSubmitted, setLeadSubmitted] = useState(false);\n  const [leadSending, setLeadSending] = useState(false);\n  const [leadError, setLeadError] = useState(null);\n\n  \/\/ \u2500\u2500 Integration config (developer: replace these values) \u2500\u2500\n  const HUBSPOT_PORTAL_ID = \"27174408\";\n  const HUBSPOT_FORM_GUID = \"3f860858-5a58-4f1b-8419-a561af17adbe\";\n  const HUBSPOT_REGION = \"eu1\";  \/\/ EU data centre\n  const EMAIL_ENDPOINT = \"\";      \/\/ e.g. \"https:\/\/your-worker.your-domain.workers.dev\/send-report\"\n\n  \/\/ \u2500\u2500 PDF branding \u2500\u2500\n  \/\/ Paste a base64-encoded PNG\/JPEG logo below (data URI without the prefix).\n  \/\/ Recommended size: 600\u00d7150px or similar wide-format. Leave empty for text-only header.\n  \/\/ To convert: in browser console run `const r=new FileReader();r.onload=()=>console.log(r.result.split(\",\")[1]);r.readAsDataURL(file)`\n  const PDF_LOGO_BASE64 = \"\";     \/\/ e.g. \"iVBORw0KGgo...\"\n  const PDF_LOGO_FORMAT = \"PNG\";  \/\/ \"PNG\" or \"JPEG\"\n  const PDF_LOGO_WIDTH = 42;      \/\/ mm \u2014 adjust to taste (height auto-scales)\n  const PDF_COMPANY_NAME = \"RLDatix Data Solutions Group\";\n  const setOv = useCallback((k,v) => setOverrides(p=>({...p,[k]:v})),[]);\n  const overrideCount = Object.values(overrides).filter(v=>v!=null).length;\n  const resetOv = useCallback(()=>setOverrides({enterprise:null,departmental:null,niche:null,entCost:null,depCost:null,nicCost:null,clinicians:null,minsWasted:null,ticketsBaseline:null,sarDaysBefore:null}),[]);\n\n  const ref1=useRef(null),ref2=useRef(null),ref3=useRef(null),refCta=useRef(null);\n  const scrollTo=ref=>setTimeout(()=>ref.current?.scrollIntoView({behavior:\"smooth\",block:\"start\"}),120);\n  const scrollToTop=()=>{window.scrollTo(0,0);document.documentElement.scrollTop=0;document.body.scrollTop=0;if(window.self!==window.top)window.parent.postMessage({type:\"roi-calculator-scroll-top\"},\"*\");};\n  const sOk = scenarioMode!=null;\n  const am = scenarioMode||\"EXPECTED\";\n  const update=useCallback((k,v)=>{setInputs(p=>({...p,[k]:v}));setActivePreset(null);},[]);\n  const updateTier=useCallback((tier,v)=>{setInputs(p=>({...p,tiers:{...p.tiers,[tier]:v}}));setActivePreset(null);},[]);\n  const touch=useCallback(s=>setStep(p=>Math.max(p,s)),[]);\n\n  \/\/ When org_count changes, resize per-org arrays\n  const handleOrgCountChange = useCallback((v) => {\n    update(\"org_count\", v);\n    setOrgBedsList(prev => {\n      const next = [...prev];\n      while (next.length < v) next.push(next.length > 0 ? next[0] : 400);\n      return next.slice(0, v);\n    });\n    setOrgTiersList(prev => {\n      const next = [...prev];\n      while (next.length < v) next.push({enterprise:0, departmental:2, niche:3});\n      return next.slice(0, v);\n    });\n    touch(1);\n  }, [update, touch]);\n\n  \/\/ Toggle per-org beds: initialise from current total\n  const togglePerOrgBeds = useCallback(() => {\n    if (!perOrgBeds) {\n      const n = inputs.org_count;\n      const each = Math.round(inputs.bed_count \/ n \/ 10) * 10;\n      const remainder = inputs.bed_count - each * (n - 1);\n      const list = Array.from({length: n}, (_, i) => i === 0 ? remainder : each);\n      setOrgBedsList(list);\n    }\n    setPerOrgBeds(p => !p);\n  }, [perOrgBeds, inputs.bed_count, inputs.org_count]);\n\n  \/\/ Update single org's beds then sync total\n  const setOrgBeds = useCallback((idx, v) => {\n    setOrgBedsList(prev => {\n      const next = [...prev]; next[idx] = v;\n      setInputs(p => ({...p, bed_count: next.reduce((a,b) => a+b, 0)}));\n      return next;\n    });\n    setActivePreset(null); touch(1);\n  }, [touch]);\n\n  \/\/ Toggle per-org systems: initialise from current tiers\n  const togglePerOrgSystems = useCallback(() => {\n    if (!perOrgSystems) {\n      const n = inputs.org_count;\n      const split = (total) => { const each = Math.floor(total \/ n); const rem = total - each * (n-1); return Array.from({length:n}, (_,i) => i===0 ? rem : each); };\n      const ents = split(inputs.tiers.enterprise);\n      const deps = split(inputs.tiers.departmental);\n      const nics = split(inputs.tiers.niche);\n      setOrgTiersList(Array.from({length:n}, (_,i) => ({enterprise:ents[i], departmental:deps[i], niche:nics[i]})));\n    }\n    setPerOrgSystems(p => !p);\n  }, [perOrgSystems, inputs.tiers, inputs.org_count]);\n\n  \/\/ Update single org's tier then sync totals\n  const setOrgTier = useCallback((idx, tier, v) => {\n    setOrgTiersList(prev => {\n      const next = prev.map((o,i) => i===idx ? {...o, [tier]: v} : o);\n      const totals = {enterprise:0, departmental:0, niche:0};\n      next.forEach(o => { totals.enterprise += o.enterprise; totals.departmental += o.departmental; totals.niche += o.niche; });\n      setInputs(p => ({...p, tiers: totals}));\n      return next;\n    });\n    setActivePreset(null); touch(2);\n  }, [touch]);\n\n  const applyPreset=useCallback(key=>{\n    const d = PRESETS[key].data;\n    setInputs({...d,data_types:{...d.data_types},tiers:{...d.tiers}});\n    setActivePreset(key);resetOv();setThinking(true);scrollToTop();\n    setPerOrgBeds(false); setPerOrgSystems(false); setFlagships([]); setTierCostEdits({enterprise:null,departmental:null,niche:null}); setEditingTierCost(null); setCostMode(\"estimate\"); setKnownAnnualSpend(null);\n    \/\/ Size per-org arrays to match org count, split evenly\n    const n = d.org_count;\n    const bedEach = Math.round(d.bed_count \/ n \/ 10) * 10;\n    const bedRem = d.bed_count - bedEach * (n - 1);\n    setOrgBedsList(Array.from({length:n}, (_,i) => i===0 ? bedRem : bedEach));\n    const splitT = (total) => { const each = Math.floor(total \/ n); return Array.from({length:n}, (_,i) => i===0 ? total - each*(n-1) : each); };\n    setOrgTiersList(Array.from({length:n}, (_,i) => ({enterprise:splitT(d.tiers.enterprise)[i], departmental:splitT(d.tiers.departmental)[i], niche:splitT(d.tiers.niche)[i]})));\n    setTimeout(()=>{setThinking(false);setStep(3);},1800);\n  },[resetOv]);\n  const goTo=useCallback((s,ref)=>{setStep(p=>Math.max(p,s));scrollTo(ref);},[]);\n\n  const totalSystems = inputs.tiers.enterprise + inputs.tiers.departmental + inputs.tiers.niche;\n  const knownCostOv = useMemo(() => {\n    if (costMode !== \"known\" || !knownAnnualSpend || knownAnnualSpend <= 0) return {};\n    const flagSpend = flagships.reduce((s,f) => s + (f.cost || 0), 0);\n    const rem = Math.max(0, knownAnnualSpend - flagSpend);\n    const e = inputs.tiers.enterprise, d = inputs.tiers.departmental, n = inputs.tiers.niche;\n    const w = e * 5 + d * 2 + n;\n    if (w === 0) return {};\n    return {\n      entCost: e > 0 ? Math.round((rem * e * 5 \/ w) \/ e \/ 1000) * 1000 : 0,\n      depCost: d > 0 ? Math.round((rem * d * 2 \/ w) \/ d \/ 1000) * 1000 : 0,\n      nicCost: n > 0 ? Math.round((rem * n \/ w) \/ n \/ 1000) * 1000 : 0,\n    };\n  }, [costMode, knownAnnualSpend, inputs.tiers, flagships]);\n\n  const mergedOv = useMemo(() => {\n    const base = {\n      ...overrides,\n      entCost: tierCostEdits.enterprise != null ? tierCostEdits.enterprise : overrides.entCost,\n      depCost: tierCostEdits.departmental != null ? tierCostEdits.departmental : overrides.depCost,\n      nicCost: tierCostEdits.niche != null ? tierCostEdits.niche : overrides.nicCost,\n    };\n    if (costMode === \"known\" && knownAnnualSpend > 0) return { ...base, ...knownCostOv };\n    return base;\n  }, [overrides, tierCostEdits, costMode, knownAnnualSpend, knownCostOv]);\n  const rCon=useMemo(()=>calc(inputs,\"CONSERVATIVE\",mergedOv,flagships),[inputs,mergedOv,flagships]);\n  const rExp=useMemo(()=>calc(inputs,\"EXPECTED\",mergedOv,flagships),[inputs,mergedOv,flagships]);\n  const rStr=useMemo(()=>calc(inputs,\"STRETCH\",mergedOv,flagships),[inputs,mergedOv,flagships]);\n  const r=am===\"CONSERVATIVE\"?rCon:am===\"STRETCH\"?rStr:rExp;\n  const isArchiveOnly = inputs.journey === \"HAVE_EPR\";\n\n  \/\/ \u2500\u2500 Data layer: exposes all values for external PDF engines \/ integrations \u2500\u2500\n  const roiData = useMemo(() => ({\n    \/\/ Meta\n    _version: \"2.0\",\n    _generated: new Date().toISOString(),\n    _scenario: am===\"CONSERVATIVE\"?\"Conservative\":am===\"STRETCH\"?\"Stretch\":\"Expected\",\n\n    \/\/ User info\n    user_name: leadForm.name || \"\",\n    user_email: leadForm.email || \"\",\n    user_org: leadForm.org || \"\",\n    user_role: leadForm.role || \"\",\n\n    \/\/ Programme inputs\n    journey: isArchiveOnly ? \"Archive only\" : \"Migration + archiving\",\n    bed_count: inputs.bed_count,\n    org_count: inputs.org_count,\n    complexity: inputs.complexity_level,\n    data_quality: inputs.data_quality_level,\n    decom_target_pct: Math.round(inputs.decom_retire_rate * 100),\n\n    \/\/ System counts\n    systems_enterprise: r.ent,\n    systems_departmental: r.dep,\n    systems_standalone: r.nic,\n    systems_flagship: r.flagshipCount,\n    systems_total: r.legacy,\n\n    \/\/ Tier costs (per system per year)\n    cost_enterprise: r.entCost,\n    cost_departmental: r.depCost,\n    cost_standalone: r.nicCost,\n    cost_blended_avg: r.blendedCost,\n    cost_total_estate: r.totalEstate,\n    cost_flagship_total: r.flagshipTotal,\n\n    \/\/ Flagships detail\n    flagships: flagships.map(f => ({ name: f.name, cost: f.cost, retire: f.retire })),\n\n    \/\/ Decommission\n    decom_total: r.decom,\n    decom_enterprise: r.entDecom,\n    decom_departmental: r.depDecom,\n    decom_standalone: r.nicDecom,\n    decom_flagship_retired: r.flagshipRetireCount,\n    decom_savings_annual: r.decomSave,\n    decom_flagship_savings: r.flagshipDecomSave,\n\n    \/\/ Clinician time\n    clinicians_in_scope: r.clinicians,\n    mins_wasted_per_week: r.minsWasted,\n    hours_freed_per_year: r.hrsSaved,\n    time_savings_annual: r.timeSave,\n    realisation_rate_pct: Math.round(r.realisation * 100),\n    blended_hourly_rate: 55,\n\n    \/\/ Totals\n    annual_savings: r.annual,\n    three_year_total: r.total3,\n\n    \/\/ Scenario comparison\n    scenarios: {\n      conservative: { annual: rCon.annual, total3: rCon.total3, decom: rCon.decomSave, time: rCon.timeSave },\n      expected:     { annual: rExp.annual, total3: rExp.total3, decom: rExp.decomSave, time: rExp.timeSave },\n      stretch:      { annual: rStr.annual, total3: rStr.total3, decom: rStr.decomSave, time: rStr.timeSave },\n    },\n\n    \/\/ Support & SAR\n    tickets_baseline_monthly: r.ticketsBaselineMonthly,\n    tickets_after_monthly: r.ticketsAfter,\n    tickets_reduction_pct: r.ticketsReductionPct,\n    sar_days_before: r.sarDaysBefore,\n    sar_days_after: r.sarDaysAfter,\n    sar_reduction_pct: r.sarReductionPct,\n\n    \/\/ Patient safety (not in financial totals)\n    safety_med_errors_baseline: r.safetyMedErrorsBaseline,\n    safety_med_errors_avoided: r.safetyMedErrorsAvoided,\n    safety_patients_protected: r.safetyPatientsProtected,\n    safety_bed_days_avoided: r.safetyBedDaysAvoided,\n\n    \/\/ Formatted values (ready to drop into templates)\n    fmt: {\n      annual_savings: fmtK(r.annual),\n      three_year_total: fmtK(r.total3),\n      decom_savings: fmtK(r.decomSave),\n      time_savings: fmtK(r.timeSave),\n      total_estate: fmtK(r.totalEstate),\n      hours_freed: fmtNum(r.hrsSaved),\n      clinicians: fmtNum(r.clinicians),\n      systems_total: String(r.legacy),\n      decom_total: String(r.decom),\n      cost_enterprise: fmtK(r.entCost),\n      cost_departmental: fmtK(r.depCost),\n      cost_standalone: fmtK(r.nicCost),\n      conservative_annual: fmtK(rCon.annual),\n      conservative_total3: fmtK(rCon.total3),\n      stretch_annual: fmtK(rStr.annual),\n      stretch_total3: fmtK(rStr.total3),\n      date: new Date().toLocaleDateString(\"en-GB\",{day:\"numeric\",month:\"long\",year:\"numeric\"}),\n    },\n  }), [r, rCon, rExp, rStr, inputs, flagships, leadForm, am, isArchiveOnly]);\n\n  \/\/ Expose globally for external scripts \/ PDF engines\n  useEffect(() => {\n    window.roiCalculatorData = roiData;\n    \/\/ Dispatch custom event for same-page listeners\n    window.dispatchEvent(new CustomEvent(\"roi-calculator-update\", { detail: roiData }));\n    \/\/ Post to parent if in iframe\n    if (window.self !== window.top) {\n      window.parent.postMessage({ type: \"roi-calculator-data\", data: roiData }, \"*\");\n    }\n  }, [roiData]);\n\n  \/\/ \u2500\u2500 Lazy-load jsPDF with multi-CDN fallback \u2500\u2500\n  const loadJsPDF = useCallback(() => {\n    return new Promise((resolve, reject) => {\n      if (window.jspdf) return resolve(window.jspdf.jsPDF);\n      const cdns = [\n        [\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf\/2.5.2\/jspdf.umd.min.js\",\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/jspdf-autotable\/3.8.4\/jspdf.plugin.autotable.min.js\"],\n        [\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf@2.5.2\/dist\/jspdf.umd.min.js\",\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf-autotable@3.8.4\/dist\/jspdf.plugin.autotable.min.js\"],\n        [\"https:\/\/unpkg.com\/jspdf@2.5.2\/dist\/jspdf.umd.min.js\",\"https:\/\/unpkg.com\/jspdf-autotable@3.8.4\/dist\/jspdf.plugin.autotable.min.js\"],\n      ];\n      const load = (src) => new Promise((res, rej) => {\n        const s = document.createElement(\"script\"); s.src = src;\n        s.onload = res; s.onerror = rej; document.head.appendChild(s);\n      });\n      const tryCdn = (i) => {\n        if (window.jspdf) return resolve(window.jspdf.jsPDF);\n        if (i >= cdns.length) return reject(new Error(\"Could not load PDF library from any CDN. Check network or Content Security Policy.\"));\n        load(cdns[i][0])\n          .then(() => load(cdns[i][1]))\n          .then(() => { if (window.jspdf) resolve(window.jspdf.jsPDF); else tryCdn(i + 1); })\n          .catch(() => tryCdn(i + 1));\n      };\n      tryCdn(0);\n    });\n  }, []);\n\n  const generatePDF = useCallback(async (returnBlob) => {\n    let jsPDF;\n    try { jsPDF = await loadJsPDF(); } catch (e) {\n      console.error(\"PDF library load failed:\", e);\n      alert(\"Unable to load the PDF library. This may be caused by your network or browser security settings blocking external scripts. Please try again or contact your IT team to allow scripts from cdn.jsdelivr.net or unpkg.com.\");\n      return null;\n    }\n    const doc = new jsPDF({ unit: \"mm\", format: \"a4\" });\n    const pw = 210, ph = 297, ml = 16, mr = 16, mt = 16, cw = pw - ml - mr;\n    let y = 0;\n    const scenarioLabel = am===\"CONSERVATIVE\"?\"Conservative\":am===\"STRETCH\"?\"Stretch\":\"Expected\";\n    const dateStr = new Date().toLocaleDateString(\"en-GB\",{day:\"numeric\",month:\"long\",year:\"numeric\"});\n\n    \/\/ Colours\n    const TEAL = [15,65,70], WHITE = [255,255,255], SEAFOAM = [190,250,240];\n    const GREY = [61,90,94], LTBG = [238,247,241], TEALBG = [232,250,246];\n    const DARK = [31,41,55], MID = [110,138,142];\n    const setC = (c) => { doc.setTextColor(c[0],c[1],c[2]); };\n\n    \/\/ Footer helper\n    const addFooter = (pageNum, totalPages) => {\n      doc.setDrawColor(15,65,70); doc.setLineWidth(0.6);\n      doc.line(ml, ph - 14, pw - mr, ph - 14);\n      doc.setFontSize(7); setC(MID);\n      doc.text(PDF_COMPANY_NAME, ml, ph - 10);\n      doc.text(\"ROI Calculator  |  \" + dateStr, pw \/ 2, ph - 10, { align: \"center\" });\n      doc.text(\"Page \" + pageNum + \" of \" + totalPages, pw - mr, ph - 10, { align: \"right\" });\n      \/\/ Seafoam accent bar at very bottom\n      doc.setFillColor(SEAFOAM[0],SEAFOAM[1],SEAFOAM[2]);\n      doc.rect(0, ph - 4, pw, 4, \"F\");\n    };\n\n    \/\/ Repeating header for pages 2+\n    const addPageHeader = () => {\n      doc.setFillColor(TEAL[0],TEAL[1],TEAL[2]);\n      doc.rect(0, 0, pw, 14, \"F\");\n      doc.setFontSize(8); doc.setFont(\"helvetica\",\"bold\"); setC(WHITE);\n      doc.text(\"EPR Migration & Archiving  |  ROI Estimate\", ml, 9);\n      doc.setFont(\"helvetica\",\"normal\"); doc.setTextColor(190,230,220);\n      doc.text(leadForm.org || \"\", pw - mr, 9, { align: \"right\" });\n      if (PDF_LOGO_BASE64) {\n        try {\n          const lh = 8, lw = lh * (PDF_LOGO_WIDTH \/ 20);\n          doc.addImage(PDF_LOGO_BASE64, PDF_LOGO_FORMAT, pw - mr - lw - 1, 3, lw, lh);\n        } catch(e) {}\n      }\n    };\n\n    \/\/ \u2500\u2500 PAGE 1: Header + Summary \u2500\u2500\n    \/\/ Full-width teal header bar\n    doc.setFillColor(TEAL[0],TEAL[1],TEAL[2]);\n    doc.rect(0, 0, pw, 42, \"F\");\n    \/\/ Seafoam accent strip under header\n    doc.setFillColor(SEAFOAM[0],SEAFOAM[1],SEAFOAM[2]);\n    doc.rect(0, 42, pw, 1.5, \"F\");\n\n    \/\/ Logo (right side of header)\n    if (PDF_LOGO_BASE64) {\n      try {\n        const logoH = 16;\n        const logoW = logoH * (PDF_LOGO_WIDTH \/ 20);\n        doc.addImage(PDF_LOGO_BASE64, PDF_LOGO_FORMAT, pw - mr - logoW, 6, logoW, logoH);\n      } catch (e) { console.warn(\"Logo failed:\", e); }\n    }\n\n    \/\/ Title text (left side)\n    const titleMaxW = PDF_LOGO_BASE64 ? cw - PDF_LOGO_WIDTH - 8 : cw;\n    doc.setFontSize(20); doc.setFont(\"helvetica\",\"bold\"); setC(WHITE);\n    doc.text(\"EPR Migration & Archiving\", ml, 14);\n    doc.setFontSize(24); doc.setTextColor(128,248,228);\n    doc.text(\"ROI Estimate\", ml, 25);\n    doc.setFontSize(9); doc.setFont(\"helvetica\",\"normal\"); setC([190,230,220]);\n    doc.text(isArchiveOnly ? \"Archiving + decommission pathway\" : \"Migration + archiving pathway\", ml, 34);\n\n    y = 50;\n\n    \/\/ Meta row (2x2 grid for long org\/name support)\n    const metaCol1 = ml, metaCol2 = ml + cw \/ 2;\n    const metaW = cw \/ 2 - 8;\n    doc.setFontSize(8); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Organisation:\", metaCol1, y);\n    doc.text(\"Prepared for:\", metaCol2, y);\n    doc.setFont(\"helvetica\",\"normal\"); setC(GREY);\n    const orgLines = doc.splitTextToSize(leadForm.org || \"Not specified\", metaW);\n    doc.text(orgLines[0], metaCol1, y + 4);\n    doc.text(doc.splitTextToSize(leadForm.name || \"Not specified\", metaW)[0], metaCol2, y + 4);\n    y += 12;\n    doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Scenario:\", metaCol1, y);\n    doc.text(\"Date:\", metaCol2, y);\n    doc.setFont(\"helvetica\",\"normal\"); setC(GREY);\n    doc.text(scenarioLabel, metaCol1, y + 4);\n    doc.text(dateStr, metaCol2, y + 4);\n    y += 8;\n    doc.setDrawColor(TEAL[0],TEAL[1],TEAL[2]); doc.setLineWidth(0.8);\n    doc.line(ml, y, pw - mr, y);\n    y += 8;\n\n    \/\/ Summary cards\n    const cards = [\n      { label: \"3-YEAR TOTAL\", value: fmtK(r.total3), sub: \"Decom + clinician time\", bg: LTBG },\n      { label: \"ANNUAL RECURRING\", value: fmtK(r.annual), sub: fmtK(r.decomSave) + \" decom + \" + fmtK(r.timeSave) + \" time\", bg: TEALBG },\n      { label: \"HOURS FREED \/ YR\", value: fmtNum(r.hrsSaved), sub: Math.round(r.realisation*100) + \"% realisation\", bg: [230,248,252] },\n    ];\n    const cardW = (cw - 8) \/ 3;\n    cards.forEach((cd, i) => {\n      const cx = ml + i * (cardW + 4);\n      doc.setFillColor(cd.bg[0],cd.bg[1],cd.bg[2]);\n      doc.roundedRect(cx, y, cardW, 28, 3, 3, \"F\");\n      doc.setFontSize(6.5); doc.setFont(\"helvetica\",\"bold\"); setC(GREY);\n      doc.text(cd.label, cx + cardW\/2, y + 7, { align: \"center\" });\n      doc.setFontSize(18); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n      doc.text(cd.value, cx + cardW\/2, y + 17, { align: \"center\" });\n      doc.setFontSize(6.5); doc.setFont(\"helvetica\",\"normal\"); setC(MID);\n      doc.text(cd.sub, cx + cardW\/2, y + 23, { align: \"center\" });\n    });\n    y += 36;\n\n    \/\/ Programme inputs\n    doc.setFontSize(12); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Programme Inputs\", ml, y); y += 2;\n    doc.autoTable({\n      startY: y,\n      margin: { left: ml, right: mr },\n      theme: \"plain\",\n      styles: { fontSize: 9, cellPadding: { top: 2.5, bottom: 2.5, left: 4, right: 4 }, textColor: DARK, lineWidth: 0.2, lineColor: [224,232,228] },\n      columnStyles: { 0: { fontStyle: \"bold\", cellWidth: 55 }, 1: { halign: \"right\" } },\n      body: [\n        [\"Journey\", isArchiveOnly ? \"Have enterprise EPR\" : \"Evaluating EPRs\"],\n        [\"Beds\", fmtNum(inputs.bed_count)],\n        [\"Organisations\", String(inputs.org_count)],\n        [\"Legacy systems\", r.legacy + \" (\" + r.ent + \" enterprise, \" + r.dep + \" departmental, \" + r.nic + \" standalone\" + (flagships.length > 0 ? \", \" + flagships.length + \" named\" : \"\") + \")\"],\n        [\"Complexity\", inputs.complexity_level],\n        [\"Data quality\", inputs.data_quality_level],\n      ],\n    });\n    y = doc.lastAutoTable.finalY + 10;\n\n    \/\/ Decommission savings\n    doc.setFontSize(12); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Decommission Savings\", ml, y); y += 2;\n    const decomBody = [];\n    if (r.ent > 0) decomBody.push([\"Enterprise\", String(r.ent), fmtK(r.entCost), String(r.entDecom), fmtK(r.entDecom * r.entCost) + \"\/yr\"]);\n    if (r.dep > 0) decomBody.push([\"Departmental\", String(r.dep), fmtK(r.depCost), String(r.depDecom), fmtK(r.depDecom * r.depCost) + \"\/yr\"]);\n    if (r.nic > 0) decomBody.push([\"Standalone\", String(r.nic), fmtK(r.nicCost), String(r.nicDecom), fmtK(r.nicDecom * r.nicCost) + \"\/yr\"]);\n    flagships.forEach(f => { decomBody.push([f.name || \"Named system\", \"1\", fmtK(f.cost), f.retire ? \"1\" : \"0\", f.retire ? fmtK(f.cost) + \"\/yr\" : \"\\u2013\"]); });\n    decomBody.push([{ content: \"Total\", styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: String(r.legacy), styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmtK(r.blendedCost) + \" avg\", styles: { fillColor: LTBG } }, { content: String(r.decom), styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmtK(r.decomSave) + \"\/yr\", styles: { fontStyle: \"bold\", textColor: TEAL, fillColor: LTBG } }]);\n    doc.autoTable({\n      startY: y,\n      margin: { left: ml, right: mr },\n      head: [[\"Tier\", \"Systems\", \"Cost\/sys\/yr\", \"Retired\", \"Saving\"]],\n      body: decomBody,\n      headStyles: { fillColor: TEAL, textColor: WHITE, fontStyle: \"bold\", fontSize: 7.5, cellPadding: { top: 3, bottom: 3, left: 4, right: 4 } },\n      styles: { fontSize: 9, cellPadding: { top: 2.5, bottom: 2.5, left: 4, right: 4 }, textColor: DARK, lineWidth: 0.2, lineColor: [224,232,228] },\n      columnStyles: { 1: { halign: \"right\" }, 2: { halign: \"right\" }, 3: { halign: \"right\" }, 4: { halign: \"right\" } },\n    });\n    y = doc.lastAutoTable.finalY + 10;\n\n    \/\/ Check if we need page break\n    if (y > 220) { doc.addPage(); addPageHeader(); y = 22; }\n\n    \/\/ Clinician time\n    doc.setFontSize(12); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Clinician Time\", ml, y); y += 2;\n    doc.autoTable({\n      startY: y,\n      margin: { left: ml, right: mr },\n      theme: \"plain\",\n      styles: { fontSize: 9, cellPadding: { top: 2.5, bottom: 2.5, left: 4, right: 4 }, textColor: DARK, lineWidth: 0.2, lineColor: [224,232,228] },\n      columnStyles: { 0: { fontStyle: \"bold\", cellWidth: 65 }, 1: { halign: \"right\" } },\n      body: [\n        [\"Clinical staff in scope\", fmtNum(r.clinicians)],\n        [\"Minutes wasted \/ week\", r.minsWasted + \" min\"],\n        [\"Hours freed \/ year\", fmtNum(r.hrsSaved)],\n        [{ content: \"Capacity value\", styles: { fontStyle: \"bold\" } }, { content: fmtK(r.timeSave) + \"\/yr (at \" + Math.round(r.realisation*100) + \"% realisation)\", styles: { fontStyle: \"bold\", textColor: TEAL } }],\n      ],\n    });\n    y = doc.lastAutoTable.finalY + 10;\n\n    \/\/ Patient safety\n    if (r.hasClinicalScope) {\n      if (y > 210) { doc.addPage(); addPageHeader(); y = 22; }\n      doc.setFillColor(250,245,247); doc.setDrawColor(224,196,207);\n      doc.roundedRect(ml, y, cw, 52, 3, 3, \"FD\");\n      doc.setFontSize(11); doc.setFont(\"helvetica\",\"bold\"); doc.setTextColor(138,46,74);\n      doc.text(isArchiveOnly ? \"Patient Safety Context\" : \"Patient Safety Case for Migration\", ml + 6, y + 8);\n      doc.autoTable({\n        startY: y + 12,\n        margin: { left: ml + 4, right: mr + 4 },\n        theme: \"plain\",\n        tableWidth: cw - 8,\n        styles: { fontSize: 8.5, cellPadding: { top: 1.8, bottom: 1.8, left: 3, right: 3 }, textColor: DARK, lineWidth: 0 },\n        columnStyles: { 1: { halign: \"right\", fontStyle: \"bold\" } },\n        body: [\n          [\"Medication errors at transitions (baseline)\", fmtNum(r.safetyMedErrorsBaseline) + \"\/yr\"],\n          [\"Potentially avoidable\", fmtNum(r.safetyMedErrorsAvoided) + \"\/yr\"],\n          [\"Patients potentially protected\", fmtNum(r.safetyPatientsProtected) + \"\/yr\"],\n          [\"Excess bed days avoidable\", fmtNum(r.safetyBedDaysAvoided) + \"\/yr\"],\n        ],\n      });\n      const safetyEnd = doc.lastAutoTable.finalY + 2;\n      doc.setFontSize(7); doc.setFont(\"helvetica\",\"normal\"); setC(GREY);\n      const litigNote = \"Litigation context: NHS trusts collectively pay \\u00a33.6bn\/yr in clinical negligence settlements (NAO, Oct 2025). CNST contributions are directly influenced by claims history. These figures are not included in the financial ROI total.\";\n      doc.text(doc.splitTextToSize(litigNote, cw - 16), ml + 6, safetyEnd + 2);\n      \/\/ Resize safety box to fit\n      const boxH = Math.max(52, safetyEnd + 10 - y);\n      doc.setFillColor(250,245,247); doc.setDrawColor(224,196,207);\n      \/\/ Redraw box at correct size (draw over)\n      y = safetyEnd + 14;\n    }\n\n    \/\/ Year-by-year\n    if (y > 220) { doc.addPage(); addPageHeader(); y = 22; }\n    doc.setFontSize(12); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Year-by-Year Projection\", ml, y); y += 2;\n    doc.autoTable({\n      startY: y,\n      margin: { left: ml, right: mr },\n      head: [[\"Benefit\", \"Year 1\", \"Year 2\", \"Year 3\", \"Total\"]],\n      body: [\n        [\"Decom savings\", fmt(r.decomSave), fmt(r.decomSave), fmt(r.decomSave), { content: fmt(r.decomSave * 3), styles: { fontStyle: \"bold\" } }],\n        [\"Clinician time\", fmt(r.timeSave), fmt(r.timeSave), fmt(r.timeSave), { content: fmt(r.timeSave * 3), styles: { fontStyle: \"bold\" } }],\n        [{ content: \"Total\", styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmt(r.annual), styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmt(r.annual), styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmt(r.annual), styles: { fontStyle: \"bold\", fillColor: LTBG } }, { content: fmt(r.total3), styles: { fontStyle: \"bold\", textColor: TEAL, fillColor: LTBG, fontSize: 11 } }],\n      ],\n      headStyles: { fillColor: TEAL, textColor: WHITE, fontStyle: \"bold\", fontSize: 7.5, cellPadding: { top: 3, bottom: 3, left: 4, right: 4 } },\n      styles: { fontSize: 9, cellPadding: { top: 2.5, bottom: 2.5, left: 4, right: 4 }, textColor: DARK, lineWidth: 0.2, lineColor: [224,232,228] },\n      columnStyles: { 1: { halign: \"right\" }, 2: { halign: \"right\" }, 3: { halign: \"right\" }, 4: { halign: \"right\" } },\n    });\n    y = doc.lastAutoTable.finalY + 8;\n\n    \/\/ Scenario range note\n    doc.setFillColor(TEALBG[0],TEALBG[1],TEALBG[2]);\n    doc.roundedRect(ml, y, cw, 14, 2, 2, \"F\");\n    doc.setFontSize(8); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n    doc.text(\"Range across scenarios:\", ml + 4, y + 5);\n    doc.setFont(\"helvetica\",\"normal\"); setC(GREY);\n    doc.text(fmtK(rCon.total3) + \" (conservative)  to  \" + fmtK(rStr.total3) + \" (stretch).  All figures are user-overridable. Clinical safety metrics are not included in financial totals.\", ml + 4, y + 10);\n    y += 22;\n\n    \/\/ Scenario comparison mini-table\n    if (y < 240) {\n      doc.setFontSize(10); doc.setFont(\"helvetica\",\"bold\"); setC(TEAL);\n      doc.text(\"Scenario Comparison\", ml, y); y += 2;\n      doc.autoTable({\n        startY: y,\n        margin: { left: ml, right: mr },\n        head: [[\"\", \"Conservative\", \"Expected\", \"Stretch\"]],\n        body: [\n          [\"Decom savings\", fmtK(rCon.decomSave) + \"\/yr\", fmtK(r.decomSave) + \"\/yr\", fmtK(rStr.decomSave) + \"\/yr\"],\n          [\"Time savings\", fmtK(rCon.timeSave) + \"\/yr\", fmtK(r.timeSave) + \"\/yr\", fmtK(rStr.timeSave) + \"\/yr\"],\n          [{ content: \"Annual total\", styles: { fontStyle: \"bold\" } }, { content: fmtK(rCon.annual) + \"\/yr\", styles: { fontStyle: \"bold\" } }, { content: fmtK(r.annual) + \"\/yr\", styles: { fontStyle: \"bold\", textColor: TEAL } }, { content: fmtK(rStr.annual) + \"\/yr\", styles: { fontStyle: \"bold\" } }],\n          [{ content: \"3-year total\", styles: { fontStyle: \"bold\" } }, { content: fmtK(rCon.total3), styles: { fontStyle: \"bold\" } }, { content: fmtK(r.total3), styles: { fontStyle: \"bold\", textColor: TEAL } }, { content: fmtK(rStr.total3), styles: { fontStyle: \"bold\" } }],\n        ],\n        headStyles: { fillColor: TEAL, textColor: WHITE, fontStyle: \"bold\", fontSize: 7.5, cellPadding: { top: 3, bottom: 3, left: 4, right: 4 } },\n        styles: { fontSize: 8.5, cellPadding: { top: 2.2, bottom: 2.2, left: 4, right: 4 }, textColor: DARK, lineWidth: 0.2, lineColor: [224,232,228] },\n        columnStyles: { 1: { halign: \"right\" }, 2: { halign: \"right\" }, 3: { halign: \"right\" } },\n      });\n    }\n\n    \/\/ Footer on all pages\n    const totalPages = doc.internal.getNumberOfPages();\n    for (let i = 1; i <= totalPages; i++) {\n      doc.setPage(i);\n      addFooter(i, totalPages);\n    }\n\n    \/\/ CTA bar on last page\n    doc.setPage(totalPages);\n    const ctaY = ph - 24;\n    doc.setFillColor(TEAL[0],TEAL[1],TEAL[2]);\n    doc.roundedRect(ml, ctaY, cw, 12, 2, 2, \"F\");\n    doc.setFontSize(8); doc.setFont(\"helvetica\",\"bold\"); setC(WHITE);\n    doc.text(\"To discuss your programme, contact the \" + PDF_COMPANY_NAME + \" team.\", pw \/ 2, ctaY + 5, { align: \"center\" });\n    doc.setFontSize(7); doc.setFont(\"helvetica\",\"normal\"); doc.setTextColor(190,230,220);\n    doc.text(\"All assumptions documented in the methodology report. Every figure is adjustable.\", pw \/ 2, ctaY + 9, { align: \"center\" });\n\n    \/\/ Save or return\n    const filename = \"ROI-Estimate-\" + (leadForm.org || \"Report\").replace(\/[^a-zA-Z0-9]\/g, \"-\") + \".pdf\";\n    if (returnBlob) return { blob: doc.output(\"blob\"), filename };\n    doc.save(filename);\n  }, [am, r, rCon, rStr, inputs, leadForm, isArchiveOnly, flagships, loadJsPDF]);\n\n  \/\/ \u2500\u2500 Lead submission: HubSpot + PDF email \u2500\u2500\n  const submitLead = useCallback(async () => {\n    if (!leadForm.name || !leadForm.email) return;\n    setLeadSending(true); setLeadError(null);\n\n    \/\/ 1. Generate PDF blob\n    const pdf = await generatePDF(true);\n    if (!pdf) { setLeadSending(false); return; }\n\n    \/\/ 2. Always download locally\n    const url = URL.createObjectURL(pdf.blob);\n    const a = document.createElement(\"a\"); a.href = url; a.download = pdf.filename;\n    document.body.appendChild(a); a.click(); document.body.removeChild(a);\n    setTimeout(() => URL.revokeObjectURL(url), 10000);\n\n    \/\/ 3. Submit to HubSpot (EU1 region, fire-and-forget)\n    if (HUBSPOT_PORTAL_ID && HUBSPOT_FORM_GUID) {\n      try {\n        const scenarioLabel = am===\"CONSERVATIVE\"?\"Conservative\":am===\"STRETCH\"?\"Stretch\":\"Expected\";\n        const calcContext = `ROI Calculator submission | Scenario: ${scenarioLabel} | Beds: ${inputs.bed_count} | Orgs: ${inputs.org_count} | Legacy systems: ${r.legacy} | Annual savings: ${fmtK(r.annual)} | 3-year total: ${fmtK(r.total3)} | Complexity: ${inputs.complexity_level} | Journey: ${isArchiveOnly?\"Archive only\":\"Migration + archiving\"}`;\n        await fetch(`https:\/\/forms-${HUBSPOT_REGION}.hsforms.com\/submissions\/v3\/integration\/submit\/${HUBSPOT_PORTAL_ID}\/${HUBSPOT_FORM_GUID}`, {\n          method: \"POST\",\n          headers: { \"Content-Type\": \"application\/json\" },\n          body: JSON.stringify({\n            fields: [\n              { name: \"firstname\", value: leadForm.name.split(\" \")[0] },\n              { name: \"lastname\", value: leadForm.name.split(\" \").slice(1).join(\" \") || \"\" },\n              { name: \"email\", value: leadForm.email },\n              { name: \"company\", value: leadForm.org || \"\" },\n              { name: \"jobtitle\", value: leadForm.role || \"\" },\n              { name: \"message\", value: calcContext },\n            ],\n            context: {\n              pageUri: window.location.href,\n              pageName: \"ROI Calculator\",\n            },\n            legalConsentOptions: {\n              consent: {\n                consentToProcess: true,\n                text: \"I agree to receive communications about my ROI estimate.\",\n              },\n            },\n          }),\n        });\n      } catch (e) { console.warn(\"HubSpot submission failed:\", e); }\n    }\n\n    \/\/ 4. Send PDF via email endpoint (fire-and-forget)\n    if (EMAIL_ENDPOINT) {\n      try {\n        const reader = new FileReader();\n        reader.onload = async () => {\n          const base64 = reader.result.split(\",\")[1];\n          try {\n            await fetch(EMAIL_ENDPOINT, {\n              method: \"POST\",\n              headers: { \"Content-Type\": \"application\/json\" },\n              body: JSON.stringify({\n                to: leadForm.email,\n                name: leadForm.name,\n                org: leadForm.org || \"\",\n                role: leadForm.role || \"\",\n                filename: pdf.filename,\n                pdf_base64: base64,\n                scenario: am===\"CONSERVATIVE\"?\"Conservative\":am===\"STRETCH\"?\"Stretch\":\"Expected\",\n                annual: r.annual,\n                total3: r.total3,\n              }),\n            });\n          } catch (e) { console.warn(\"Email endpoint failed:\", e); }\n        };\n        reader.readAsDataURL(pdf.blob);\n      } catch (e) { console.warn(\"PDF encoding failed:\", e); }\n    }\n\n    setLeadSending(false);\n    setLeadSubmitted(true);\n  }, [generatePDF, leadForm, am, r, HUBSPOT_PORTAL_ID, HUBSPOT_FORM_GUID, EMAIL_ENDPOINT]);\n\n\n  \/* Styles loaded from styles.css *\/\n\n  \/* \u2500\u2500 INPUT PAGE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n\n  const inputPage = <div>\n    {thinking && (\n      <div style={{position:\"fixed\",inset:0,background:\"rgba(27,36,51,0.55)\",zIndex:100,display:\"flex\",alignItems:\"center\",justifyContent:\"center\",backdropFilter:\"blur(3px)\"}}>\n        <div style={{background:C.surface,borderRadius:16,padding:\"40px 48px\",textAlign:\"center\",maxWidth:380,boxShadow:\"0 12px 48px rgba(0,0,0,0.25)\"}}>\n          <div style={{display:\"flex\",gap:6,justifyContent:\"center\",marginBottom:20,height:32,alignItems:\"flex-end\"}}>\n            {[0,1,2,3,4].map(i => <div key={i} style={{width:6,borderRadius:3,background:C.accent,animation:`think-pulse 1.2s ease-in-out ${i*0.15}s infinite`,height:`${16+Math.sin(i*1.2)*12}px`}} \/>)}\n          <\/div>\n          <div style={{fontSize:16,fontWeight:700,color:C.text,marginBottom:6}}>Calibrating model<\/div>\n          <div style={{fontSize:13,color:C.textMuted,marginBottom:16,lineHeight:1.5}}>Mapping legacy estate and projecting savings...<\/div>\n          <div style={{height:4,background:\"#e0e4ea\",borderRadius:2,overflow:\"hidden\"}}>\n            <div style={{height:\"100%\",background:`linear-gradient(90deg,${C.accent},${C.teal})`,borderRadius:2,animation:\"think-fill 1.7s ease-out forwards\",width:0}} \/>\n          <\/div>\n        <\/div>\n      <\/div>\n    )}\n\n    {\/* STEP 0 \u2013 Journey stage *\/}\n    <Card>\n      <div style={{fontSize:12,fontWeight:600,color:C.textMuted,textTransform:\"uppercase\",letterSpacing:\".05em\",marginBottom:12,display:\"flex\",alignItems:\"center\",gap:8}}>\n        <span style={{width:24,height:24,borderRadius:\"50%\",background:C.accent,color:\"#fff\",display:\"flex\",alignItems:\"center\",justifyContent:\"center\",fontSize:12,fontWeight:700}}>1<\/span>\n        Where are you on your EPR journey?\n      <\/div>\n      <div style={{display:\"flex\",gap:12,flexWrap:\"wrap\"}}>\n        {[\n          {k:\"HAVE_EPR\",icon:\"\u2705\",title:\"We have an enterprise EPR\",desc:\"Already on Epic, Oracle\/Cerner or similar, and looking to archive and decommission legacy systems.\",focus:\"Archiving + decommission savings\"},\n          {k:\"EVALUATING\",icon:\"\ud83d\udd0d\",title:\"We\u2019re evaluating enterprise EPRs\",desc:\"Assessing migration to a single platform and need the full case for migration and archiving.\",focus:\"Migration safety + archiving savings\"},\n        ].map(j=><button type=\"button\" key={j.k} onClick={()=>{update(\"journey\",j.k);touch(0);setTimeout(()=>scrollTo(ref1),200);}} className=\"roi-btn\" style={{\n          flex:\"1 1 240px\",minWidth:0,padding:\"18px 20px\",textAlign:\"left\",cursor:\"pointer\",\n          border:inputs.journey===j.k?`2px solid ${C.accent}`:`1px solid ${C.border}`,borderRadius:12,\n          background:inputs.journey===j.k?C.accentPale:\"#fff\",transition:\"all .15s\"\n        }}>\n          <div style={{fontSize:24,marginBottom:8}}>{j.icon}<\/div>\n          <div style={{fontSize:15,fontWeight:700,color:inputs.journey===j.k?C.accent:C.text,marginBottom:4}}>{j.title}<\/div>\n          <div style={{fontSize:13,color:C.textMuted,lineHeight:1.5,marginBottom:8}}>{j.desc}<\/div>\n          <div style={{fontSize:12,fontWeight:600,color:inputs.journey===j.k?C.accent:C.textMid,padding:\"4px 10px\",background:inputs.journey===j.k?`${C.accent}15`:C.bg,borderRadius:6,display:\"inline-block\"}}>{j.focus}<\/div>\n        <\/button>)}\n      <\/div>\n    <\/Card>\n    <LockedConnector\/>\n\n    {\/* STEP 1 \u2013 Org scale + presets *\/}\n    <div ref={ref1} style={{animation:\"fade-in .4s ease\"}}><Card>\n      <div style={{fontSize:12,fontWeight:600,color:C.textMuted,textTransform:\"uppercase\",letterSpacing:\".05em\",marginBottom:12,display:\"flex\",alignItems:\"center\",gap:8}}>\n        <span style={{width:24,height:24,borderRadius:\"50%\",background:step>=0?C.accent:C.border,color:\"#fff\",display:\"flex\",alignItems:\"center\",justifyContent:\"center\",fontSize:12,fontWeight:700}}>2<\/span>\n        Organisation scale\n      <\/div>\n      <div style={{marginBottom:20,padding:\"16px 18px\",background:C.bg,borderRadius:10,border:`1px solid ${C.borderLight}`}}>\n        <div style={{fontSize:12,fontWeight:600,color:C.textMuted,textTransform:\"uppercase\",letterSpacing:\".05em\",marginBottom:8}}>Quick start {\"\\u2013\"} load a template<\/div>\n        <div style={{display:\"grid\",gridTemplateColumns:\"1fr 1fr\",gap:8}}>\n          {Object.entries(PRESETS).map(([k,p])=><button type=\"button\" key={k} onClick={()=>applyPreset(k)} disabled={thinking} className=\"roi-preset\" style={{width:\"100%\",padding:\"10px 12px\",textAlign:\"left\",border:activePreset===k?`2px solid ${C.accent}`:`1px solid ${C.border}`,borderRadius:14,background:activePreset===k?C.accentPale:\"#fff\",cursor:thinking?\"wait\":\"pointer\",opacity:thinking?0.6:1}}>\n            <div style={{fontSize:13,fontWeight:700,color:activePreset===k?C.accent:C.text}}>{p.label}<\/div>\n            <div style={{fontSize:11,color:C.textMuted,marginTop:1}}>{p.desc}<\/div>\n          <\/button>)}\n        <\/div>\n      <\/div>\n\n      <AnimatedSlider label={<span>Organisations in scope<InfoTip text=\"How many separate NHS organisations are included? For multi-org, you can break down beds and systems per organisation below.\"\/><\/span>} value={inputs.org_count} min={1} max={10} step={1} onChange={v=>handleOrgCountChange(v)}\/>\n\n      <div style={{marginTop:20}}>\n        {inputs.org_count > 1 && (\n          <div style={{display:\"flex\",alignItems:\"center\",justifyContent:\"space-between\",marginBottom:12}}>\n            <label style={{fontSize:13,fontWeight:600,color:C.textMid}}>Acute beds<\/label>\n            <TogglePill active={perOrgBeds?\"byorg\":\"combined\"} onToggle={v=>{if((v===\"byorg\")!==perOrgBeds)togglePerOrgBeds();}} labelA=\"combined\" labelB=\"byorg\" labelAText=\"Combined total\" labelBText=\"By organisation\"\/>\n          <\/div>\n        )}\n        {!perOrgBeds ? (\n          <div>\n            <AnimatedSlider label={inputs.org_count <= 1 ? <span>Total acute beds<InfoTip text=\"Total beds across all organisations in scope. Scales clinical staff (2.8 per bed), system costs, and clinical safety baseline. Set to 0 for community providers.\"\/><\/span> : null} value={inputs.bed_count} min={0} max={8000} step={10} onChange={v=>{update(\"bed_count\",v);touch(1);}}\/>\n          <\/div>\n        ) : (\n          <div>\n            <div style={{display:\"flex\",flexDirection:\"column\",gap:6}}>\n              {orgBedsList.map((beds, i) => (\n                <div key={i} className=\"roi-org\" style={{display:\"flex\",alignItems:\"center\",gap:12,padding:\"8px 14px\",background:C.bg,borderRadius:8,border:`1px solid ${C.borderLight}`}}>\n                  <span style={{fontSize:13,fontWeight:600,color:C.textMid,minWidth:48}}>Org {i+1}<\/span>\n                  <input type=\"range\" min={0} max={4000} step={10} value={beds}\n                    onChange={e => setOrgBeds(i, +e.target.value)}\n                    style={{flex:1,cursor:\"pointer\",accentColor:C.accent}} \/>\n                  <span style={{fontSize:16,fontWeight:800,color:C.accent,minWidth:55,textAlign:\"right\"}}>{fmtNum(beds)}<\/span>\n                <\/div>\n              ))}\n            <\/div>\n            <div style={{marginTop:10,padding:\"10px 14px\",background:C.accentPale,borderRadius:8,display:\"flex\",alignItems:\"baseline\",gap:8}}>\n              <span style={{fontSize:13,fontWeight:600,color:C.textMid}}>Total:<\/span>\n              <span style={{fontSize:22,fontWeight:800,color:C.accent}}>{fmtNum(inputs.bed_count)} beds<\/span>\n            <\/div>\n          <\/div>\n        )}\n      <\/div>\n      {inputs.bed_count===0&&<div style={{marginTop:10,padding:\"8px 14px\",background:C.accentPale,borderRadius:6,fontSize:13,color:C.accent}}>Community provider {\"\\u2013\"} model still works.<\/div>}\n    <\/Card><\/div>\n    {step<1?<ContinuePrompt onClick={()=>goTo(1,ref2)} label=\"Next: legacy systems\"\/>:<LockedConnector\/>}\n\n    {\/* STEP 2 \u2013 Tiered legacy systems *\/}\n    <div ref={ref2} style={{animation:step>=1?\"fade-in .4s ease\":\"none\"}}><Card dimmed={step<1}>\n      <div style={{fontSize:12,fontWeight:600,color:C.textMuted,textTransform:\"uppercase\",letterSpacing:\".05em\",marginBottom:4,display:\"flex\",alignItems:\"center\",gap:8}}>\n        <span style={{width:24,height:24,borderRadius:\"50%\",background:step>=1?C.accent:C.border,color:\"#fff\",display:\"flex\",alignItems:\"center\",justifyContent:\"center\",fontSize:12,fontWeight:700}}>3<\/span>\n        Legacy systems\n        <span style={{marginLeft:\"auto\",display:\"inline-flex\",alignItems:\"center\",justifyContent:\"center\",minWidth:36,height:24,borderRadius:12,padding:\"0 10px\",background:C.accent,color:\"#fff\",fontSize:14,fontWeight:800,lineHeight:1}}>{totalSystems + flagships.length}<\/span>\n      <\/div>\n      <p style={{fontSize:13,color:C.textMuted,margin:\"0 0 12px\"}}>How many legacy systems in each tier? Default costs are NHS benchmarks scaled to your bed count. <strong style={{color:C.textMid}}>Click any tier cost to override it<\/strong>, add named high-cost systems below, or switch to {\"\\u201c\"}I know my spend{\"\\u201d\"} if you have your total.<\/p>\n\n      {\/* Cost input mode *\/}\n      <div style={{marginBottom:14,display:\"flex\",alignItems:\"center\",gap:12,flexWrap:\"wrap\"}}>\n        <TogglePill active={costMode} onToggle={v=>setCostMode(v)}\n          labelA=\"estimate\" labelB=\"known\"\n          labelAText=\"Estimate costs\" labelBText=\"I know my spend\"\/>\n        {inputs.org_count > 1 && costMode === \"estimate\" && (\n          <TogglePill active={perOrgSystems?\"byorg\":\"combined\"} onToggle={v=>{if((v===\"byorg\")!==perOrgSystems)togglePerOrgSystems();}} labelA=\"combined\" labelB=\"byorg\" labelAText=\"Combined totals\" labelBText=\"By organisation\"\/>\n        )}\n      <\/div>\n\n      {\/* Known costs input *\/}\n      {costMode === \"known\" && (\n        <div style={{marginBottom:16,padding:\"18px 20px\",background:C.accentPale,borderRadius:10,border:`1px solid ${C.accent}33`}}>\n          <div style={{fontSize:13,fontWeight:700,color:C.text,marginBottom:8}}>Total annual legacy system spend<\/div>\n          <div style={{display:\"flex\",alignItems:\"center\",gap:8,flexWrap:\"wrap\"}}>\n            <span style={{fontSize:18,fontWeight:800,color:C.accent}}>{\"\\u00a3\"}<\/span>\n            <input type=\"number\" value={knownAnnualSpend||\"\"} placeholder=\"e.g. 5000000\"\n              onChange={e=>{const v=parseInt(e.target.value);setKnownAnnualSpend(isNaN(v)?null:v);setActivePreset(null);}}\n              style={{flex:\"1 1 200px\",maxWidth:260,padding:\"12px 14px\",border:`2px solid ${C.accent}`,borderRadius:999,fontSize:18,fontWeight:700,fontFamily:\"inherit\"}}\/>\n            <span style={{fontSize:13,color:C.textMid}}>\/yr across all legacy systems<\/span>\n          <\/div>\n          {knownAnnualSpend > 0 && (totalSystems + flagships.length) > 0 && (\n            <div style={{marginTop:12,fontSize:13,color:C.textMid,lineHeight:1.6}}>\n              {fmtK(knownAnnualSpend)}\/yr will be distributed across your {totalSystems + flagships.length} systems\n              {flagships.length > 0 ? ` (${fmtK(flagships.reduce((s,f)=>s+(f.cost||0),0))} in named systems, remainder by tier)` : \"\"}\n              . Costs are weighted 5:2:1 across tiers. Click any tier cost below to override.\n            <\/div>\n          )}\n        <\/div>\n      )}\n\n      {!perOrgSystems ? (\n        <div>\n          <div style={{display:\"flex\",flexDirection:\"column\",gap:16}}>\n            {[\n              {tier:\"enterprise\",label:\"Enterprise systems\",color:C.accent,bg:C.accentPale,br:C.border,hint:\"e.g. legacy EPR, PAS, large clinical suites\",max:10,tip:\"Large clinical platforms, e.g. legacy EPR\/PAS, major departmental suites. High per-system cost.\"},\n              {tier:\"departmental\",label:\"Departmental systems\",color:\"#1a7a7a\",bg:\"#eef9f7\",br:\"#c0e8e2\",hint:\"e.g. theatres, lab, ED, maternity, radiology\",max:30,tip:\"Mid-tier clinical systems serving one department or specialty. Moderate running costs.\"},\n              {tier:\"niche\",label:\"Standalone systems\",color:\"#3d8a8a\",bg:C.tealPale,br:`${C.teal}22`,hint:\"e.g. document stores, scanned notes, specialty databases\",max:100,tip:\"Small self-contained tools, e.g. single-function databases, document stores, scanned notes. Low individual cost but they add up.\"},\n            ].map(t=>{\n              const computedCost = tierCost(t.tier,inputs.bed_count,CX[inputs.complexity_level]);\n              const editKey = t.tier;\n              const knownKey = t.tier === \"enterprise\" ? \"entCost\" : t.tier === \"departmental\" ? \"depCost\" : \"nicCost\";\n              const knownDerived = costMode === \"known\" && knownAnnualSpend > 0 ? knownCostOv[knownKey] : null;\n              const displayCost = tierCostEdits[editKey] != null ? tierCostEdits[editKey] : (knownDerived || computedCost);\n              const isEdited = tierCostEdits[editKey] != null;\n              const isEditing = editingTierCost === editKey;\n              return <div key={t.tier} className=\"roi-tier\" style={{padding:\"16px 18px\",background:t.bg,borderRadius:10,border:`1px solid ${t.br}`}}>\n              <div style={{display:\"flex\",alignItems:\"center\",gap:8,marginBottom:4}}>\n                                <span style={{fontSize:14,fontWeight:700,color:C.text}}>{t.label}<\/span>\n                <InfoTip text={t.tip}\/>\n                {isEditing ? (\n                  <span style={{marginLeft:\"auto\",display:\"flex\",alignItems:\"center\",gap:4}}>\n                    <span style={{fontSize:13,color:C.textMuted}}>{\"\u00a3\"}<\/span>\n                    <input type=\"number\" autoFocus defaultValue={displayCost} min={0} max={1000000} step={1000}\n                      onBlur={e => { const v = parseInt(e.target.value); if (!isNaN(v) && v >= 0) { setTierCostEdits(p=>({...p,[editKey]:v})); } setEditingTierCost(null); }}\n                      onKeyDown={e => { if(e.key===\"Enter\") e.target.blur(); if(e.key===\"Escape\"){ setEditingTierCost(null); } }}\n                      style={{width:80,padding:\"4px 6px\",fontSize:14,fontWeight:700,border:`1px solid ${C.accent}`,borderRadius:4,fontFamily:\"inherit\",textAlign:\"right\"}}\/>\n                    <span style={{fontSize:11,color:C.textMuted}}>\/yr<\/span>\n                  <\/span>\n                ) : (\n                  <span onClick={()=>setEditingTierCost(editKey)} title=\"Click to override cost\" className=\"roi-cost\" style={{marginLeft:\"auto\",fontSize:15,fontWeight:800,color:t.color,cursor:\"pointer\",display:\"flex\",alignItems:\"center\",gap:4,borderBottom:`1px dashed ${t.color}40`}}>\n                    {fmtK(displayCost)}<span style={{fontSize:11,fontWeight:500,color:C.textMuted}}>\/sys\/yr<\/span>\n                    {isEdited && <span onClick={e=>{e.stopPropagation();setTierCostEdits(p=>({...p,[editKey]:null}));}} title=\"Reset to calculated\" style={{fontSize:12,color:C.accent,cursor:\"pointer\",marginLeft:2,padding:\"6px 8px\"}}>{\"\u2715\"}<\/span>}\n                    {costMode === \"known\" && knownAnnualSpend > 0 && !isEdited && <span style={{fontSize:10,color:C.textMid,marginLeft:4,fontWeight:500}}>from total<\/span>}\n                  <\/span>\n                )}\n              <\/div>\n              <AnimatedSlider value={inputs.tiers[t.tier]} min={0} max={t.max} step={1} onChange={v=>{updateTier(t.tier,v);touch(2);}} hideRange label={<span style={{fontSize:12,color:C.textMuted}}>{t.hint}<\/span>}\/>\n            <\/div>})}\n          <\/div>\n\n        <\/div>\n      ) : (\n        <div>\n          <div style={{display:\"flex\",flexDirection:\"column\",gap:12}}>\n            {orgTiersList.map((org, oi) => (\n              <div key={oi} className=\"roi-org\" style={{padding:\"12px 16px\",background:C.bg,borderRadius:10,border:`1px solid ${C.borderLight}`}}>\n                <div style={{fontSize:13,fontWeight:700,color:C.text,marginBottom:8}}>Org {oi+1}{orgBedsList[oi] != null ? ` (${fmtNum(orgBedsList[oi])} beds)` : \"\"}<\/div>\n                <div style={{display:\"flex\",gap:10,flexWrap:\"wrap\"}}>\n                  {[\n                    {tier:\"enterprise\",label:\"Enterprise\",color:C.accent,max:5},\n                    {tier:\"departmental\",label:\"Departmental\",color:\"#1a7a7a\",max:15},\n                    {tier:\"niche\",label:\"Standalone\",color:\"#3d8a8a\",max:50},\n                  ].map(t=>(\n                    <div key={t.tier} style={{flex:\"1 1 100px\",minWidth:90}}>\n                      <div style={{fontSize:11,fontWeight:600,color:C.textMuted,marginBottom:4,display:\"flex\",alignItems:\"center\",gap:4}}>\n                        {t.label}\n                      <\/div>\n                      <div style={{display:\"flex\",alignItems:\"center\",gap:6}}>\n                        <input type=\"range\" min={0} max={t.max} step={1} value={org[t.tier]}\n                          onChange={e => setOrgTier(oi, t.tier, +e.target.value)}\n                          style={{flex:1,cursor:\"pointer\",accentColor:t.color}} \/>\n                        <span style={{fontSize:15,fontWeight:800,color:t.color,minWidth:24,textAlign:\"right\"}}>{org[t.tier]}<\/span>\n                      <\/div>\n                    <\/div>\n                  ))}\n                <\/div>\n                <div style={{marginTop:6,fontSize:12,color:C.textMid}}>Subtotal: {org.enterprise + org.departmental + org.niche} systems<\/div>\n              <\/div>\n            ))}\n          <\/div>\n          <div style={{marginTop:12,padding:\"10px 14px\",background:C.accentPale,borderRadius:8,display:\"flex\",alignItems:\"baseline\",gap:8,flexWrap:\"wrap\"}}>\n            <span style={{fontSize:13,fontWeight:600,color:C.textMid}}>Totals:<\/span>\n            <span style={{fontSize:13,color:C.textMid}}>{inputs.tiers.enterprise} enterprise, {inputs.tiers.departmental} departmental, {inputs.tiers.niche} standalone<\/span>\n            <span style={{fontSize:20,fontWeight:800,color:C.accent,marginLeft:\"auto\"}}>{totalSystems + flagships.length}<\/span>\n          <\/div>\n        <\/div>\n      )}\n\n          {\/* Flagship systems *\/}\n          {flagships.length > 0 && (\n            <div style={{marginTop:16}}>\n              <div style={{fontSize:13,fontWeight:700,color:C.text,marginBottom:8}}>Named high-cost systems<\/div>\n              <div style={{display:\"flex\",flexDirection:\"column\",gap:8}}>\n                {flagships.map((f, i) => (\n                  <div key={i} className=\"roi-flag\" style={{display:\"flex\",alignItems:\"center\",gap:10,padding:\"12px 14px\",background:\"#fff\",borderRadius:8,border:`1px solid ${C.border}`,flexWrap:\"wrap\"}}>\n                    <input type=\"text\" placeholder=\"System name\" value={f.name} onChange={e => updateFlagship(i,\"name\",e.target.value)}\n                      style={{flex:\"1 1 100%\",minWidth:0,padding:\"8px 10px\",border:`1px solid ${C.borderLight}`,borderRadius:4,fontSize:13,fontFamily:\"inherit\"}}\/>\n                    <span style={{fontSize:12,color:C.textMuted,flexShrink:0}}>{\"\u00a3\"}<\/span>\n                    <input type=\"number\" value={f.cost} min={0} max={20000000} step={10000} onChange={e => updateFlagship(i,\"cost\",parseInt(e.target.value)||0)}\n                      style={{width:90,padding:\"6px 8px\",border:`1px solid ${C.borderLight}`,borderRadius:4,fontSize:13,fontFamily:\"inherit\",textAlign:\"right\"}}\/>\n                    <span style={{fontSize:11,color:C.textMuted,flexShrink:0}}>\/yr<\/span>\n                    <label style={{display:\"flex\",alignItems:\"center\",gap:4,fontSize:12,color:C.textMid,flexShrink:0,cursor:\"pointer\"}}>\n                      <input type=\"checkbox\" checked={f.retire} onChange={e => updateFlagship(i,\"retire\",e.target.checked)} style={{accentColor:C.accent}}\/>\n                      Retire\n                    <\/label>\n                    <button type=\"button\" onClick={() => removeFlagship(i)} style={{border:\"none\",background:\"none\",color:C.textMuted,fontSize:18,cursor:\"pointer\",padding:\"8px\",lineHeight:1,borderRadius:4}} title=\"Remove\">{\"\u2715\"}<\/button>\n                  <\/div>\n                ))}\n              <\/div>\n            <\/div>\n          )}\n          <div style={{marginTop:flagships.length > 0 ? 8 : 16,padding:\"14px 18px\",background:\"#fff\",borderRadius:10,border:`1px dashed ${C.accent}55`}}>\n            <button type=\"button\" onClick={()=>{addFlagship();touch(2);}} className=\"roi-link\" style={{border:\"none\",background:\"none\",color:C.accent,fontSize:14,fontWeight:700,cursor:\"pointer\",padding:0,display:\"flex\",alignItems:\"center\",gap:6}}>\n              + Add a named high-cost system\n            <\/button>\n            {flagships.length === 0 && <div style={{fontSize:12,color:C.textMuted,marginTop:4,lineHeight:1.5}}>Got a legacy PAS at {\"\u00a3\"}3m\/yr or an EPR at {\"\u00a3\"}5m? Add it here with its actual contract value for a more accurate result.<\/div>}\n          <\/div>\n          {costMode === \"estimate\" && totalSystems >= 8 && flagships.length === 0 && (\n            <div style={{marginTop:12,padding:\"10px 14px\",background:\"#fef9ec\",borderRadius:8,border:\"1px solid #f0dca0\",fontSize:12,color:\"#7a6520\",lineHeight:1.5}}>\n              <strong>Tip:<\/strong> Default costs are NHS benchmarks. For large or merged trusts, actual contract values can be 2{\"\u20135\"}x higher. Use flagships above for big-ticket systems, or switch to {\"\u201c\"}I know my spend{\"\u201d\"} for the best accuracy.\n            <\/div>\n          )}\n\n\n      {(totalSystems + flagships.length) > 0 && (() => {\n        const estEstate = costMode === \"known\" && knownAnnualSpend > 0 ? knownAnnualSpend :\n          inputs.tiers.enterprise * (tierCostEdits.enterprise != null ? tierCostEdits.enterprise : tierCost(\"enterprise\",inputs.bed_count,CX[inputs.complexity_level]))\n          + inputs.tiers.departmental * (tierCostEdits.departmental != null ? tierCostEdits.departmental : tierCost(\"departmental\",inputs.bed_count,CX[inputs.complexity_level]))\n          + inputs.tiers.niche * (tierCostEdits.niche != null ? tierCostEdits.niche : tierCost(\"niche\",inputs.bed_count,CX[inputs.complexity_level]))\n          + flagships.reduce((s,f) => s + (f.cost||0), 0);\n        const hasCustomCost = costMode === \"known\" || tierCostEdits.enterprise != null || tierCostEdits.departmental != null || tierCostEdits.niche != null || flagships.length > 0;\n        return <>\n          <div style={{marginTop:16,padding:\"14px 18px\",background:C.bg,borderRadius:8,border:`1px solid ${C.borderLight}`}}>\n            <div style={{display:\"flex\",alignItems:\"center\",gap:8}}>\n              <div style={{fontSize:34,fontWeight:800,color:C.accent,lineHeight:1}}>{totalSystems + flagships.length}<\/div>\n              <div style={{fontSize:14,fontWeight:700,color:C.text}}>legacy systems{flagships.length > 0 ? ` (incl. ${flagships.length} named)` : \"\"}<\/div>\n            <\/div>\n            <div style={{fontSize:12,color:C.textMuted,marginTop:4}}>\n              Estate: ~{fmtK(estEstate)}\/yr {hasCustomCost ? \"\" : \"(modelled estimate) \"}running costs\n            <\/div>\n          <\/div>\n          {!hasCustomCost && costMode === \"estimate\" && <div style={{marginTop:8,padding:\"12px 16px\",background:\"#fef9ec\",borderRadius:8,border:\"1px solid #f0dca0\",fontSize:13,color:\"#7a6520\",lineHeight:1.6}}>\n            <strong>Know your actual costs?<\/strong> Click any tier cost above to override, switch to {'\"'}I know my spend{'\"'}, or add named high-cost systems. Real contract values give the most accurate ROI.\n          <\/div>}\n        <\/>;\n      })()}\n    <\/Card><\/div>\n    {step<2?<ContinuePrompt onClick={()=>goTo(2,ref3)} label=\"Next: fine-tune\"\/>:<LockedConnector\/>}\n\n    {\/* STEP 3 \u2013 Fine-tune *\/}\n    <div ref={ref3} style={{animation:step>=2?\"fade-in .4s ease\":\"none\"}}><Card dimmed={step<2}>\n      <div style={{fontSize:12,fontWeight:600,color:C.textMuted,textTransform:\"uppercase\",letterSpacing:\".05em\",marginBottom:12,display:\"flex\",alignItems:\"center\",gap:8}}>\n        <span style={{width:24,height:24,borderRadius:\"50%\",background:step>=2?C.accent:C.border,color:\"#fff\",display:\"flex\",alignItems:\"center\",justifyContent:\"center\",fontSize:12,fontWeight:700}}>4<\/span>\n        Fine-tune\n      <\/div>\n      <div style={{display:\"flex\",gap:16,flexWrap:\"wrap\",marginBottom:16}}>\n        <div style={{flex:\"1 1 200px\"}}><label style={{fontSize:13,fontWeight:600,color:C.textMid,marginBottom:5,display:\"flex\",alignItems:\"center\"}}>Complexity<InfoTip text=\"How bespoke are your legacy systems? High = more expensive (bigger decom savings) but harder interfaces (more wasted time).\"\/><\/label>\n          <select value={inputs.complexity_level} onChange={e=>{update(\"complexity_level\",e.target.value);touch(2);}} style={{width:\"100%\",padding:\"10px 12px\",borderRadius:8,border:`1px solid ${C.border}`,fontSize:14,fontFamily:\"inherit\"}}><option value=\"LOW\">Low<\/option><option value=\"TYPICAL\">Typical<\/option><option value=\"HIGH\">High<\/option><\/select><\/div>\n        <div style={{flex:\"1 1 200px\"}}><label style={{fontSize:13,fontWeight:600,color:C.textMid,marginBottom:5,display:\"flex\",alignItems:\"center\"}}>Data quality<InfoTip text=\"How clean is your legacy data? Poor quality increases clinician time wasted, support tickets, and subject access request turnaround.\"\/><\/label>\n          <select value={inputs.data_quality_level} onChange={e=>{update(\"data_quality_level\",e.target.value);touch(2);}} style={{width:\"100%\",padding:\"10px 12px\",borderRadius:8,border:`1px solid ${C.border}`,fontSize:14,fontFamily:\"inherit\"}}><option value=\"CLEAN\">Clean<\/option><option value=\"MIXED\">Mixed<\/option><option value=\"POOR\">Poor<\/option><\/select><\/div>\n      <\/div>\n      <AnimatedSlider label={<span>Decommission target<InfoTip text=\"What % of legacy systems do you plan to retire? 60-80% is typical as most programmes retain a few for standalone or long-retention use.\"\/><\/span>} value={inputs.decom_retire_rate} min={0} max={1} step={0.05} display={`${Math.round(inputs.decom_retire_rate*100)}%`} onChange={v=>{update(\"decom_retire_rate\",v);touch(3);}} suffix=\"\"\/>\n    <\/Card><\/div>\n    <LockedConnector\/>\n\n    {\/* CTA *\/}\n    <div ref={refCta} style={{animation:step>=2?\"fade-in .4s ease\":\"none\",opacity:step<2?0.3:1,pointerEvents:step<2?\"none\":\"auto\",transition:\"opacity .4s\"}}>\n      <button type=\"button\" className=\"roi-cta\" onClick={() => {\n          setPage(\"calculating\"); setCalcStep(0); setScenarioMode(null);\n          scrollToTop();\n          setTimeout(() => setCalcStep(1), 400);\n          setTimeout(() => setCalcStep(2), 1100);\n          setTimeout(() => setCalcStep(3), 1800);\n          setTimeout(() => { setPage(\"results\"); scrollToTop(); }, 2400);\n        }} style={{\n          width: \"100%\", padding: \"18px\", background: C.accent, color: \"#fff\",\n          border: \"none\", borderRadius: 999, fontSize: 17, fontWeight: 700,\n          cursor: \"pointer\", boxShadow: \"0 2px 12px rgba(0,94,184,.3)\",\n        }}>\n          Calculate ROI {\"\\u2192\"}\n        <\/button>\n    <\/div>\n  <\/div>;\n\n  \/* \u2500\u2500 RESULTS PAGE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n\n  const dim=!sOk, an=sOk;\n\n  const resultsPage = <div>\n    {\/* Journey context banner *\/}\n    <div style={{marginBottom:16,padding:\"18px 22px\",borderRadius:12,background:isArchiveOnly?C.greenPale:C.rosePale,border:`1px solid ${isArchiveOnly?\"#c3e6cb\":C.rose+\"22\"}`}}>\n      <div style={{display:\"flex\",alignItems:\"center\",gap:10,marginBottom:8}}>\n        <span style={{fontSize:22}}>{isArchiveOnly?\"\ud83d\udcb0\":\"\ud83d\udee1\ufe0f\"}<\/span>\n        <div style={{fontSize:15,fontWeight:700,color:isArchiveOnly?C.green:C.rose}}>{isArchiveOnly?\"Archiving + decommission\":\"Migration + archiving\"}<\/div>\n      <\/div>\n      <div style={{fontSize:14,color:C.text,lineHeight:1.6}}>\n        <span style={{fontWeight:600}}>You said:<\/span> {isArchiveOnly\n          ? \"\\\"We have an enterprise EPR\\\" , so we've estimated the potential ROI for archiving and decommissioning your legacy systems.\"\n          : \"\\\"We're evaluating enterprise EPRs\\\" , so we've estimated the potential ROI for both migration and archiving.\"}\n      <\/div>\n      <div style={{fontSize:13,color:C.textMid,marginTop:6,lineHeight:1.5}}>{isArchiveOnly\n        ? \"Your enterprise EPR is in place. Retiring legacy systems eliminates ongoing licensing, hosting and support costs. A clinical archive ensures historical patient records remain accessible to clinicians at the point of care, supporting continuity of care and regulatory compliance.\"\n        : \"Migrating to a single EPR improves patient safety at care transitions. Archiving legacy systems eliminates ongoing costs, while ensuring historical information can be accessed, in context, by clinicians at the point of care.\"}<\/div>\n    <\/div>\n\n    {\/* Scenario chooser *\/}\n    <Card style={{marginBottom:dim?16:24,border:dim?`2px solid ${C.accent}`:`1px solid ${C.border}`,boxShadow:dim?`0 0 0 4px ${C.accent}22`:\"none\"}}>\n      <div style={{fontSize:14,fontWeight:700,color:C.accent,marginBottom:10,display:\"flex\",alignItems:\"center\",gap:8}}>\n        {dim&&<span style={{display:\"inline-flex\",alignItems:\"center\",justifyContent:\"center\",width:24,height:24,borderRadius:\"50%\",background:C.accent,color:\"#fff\",fontSize:12,fontWeight:800}}>1<\/span>}\n        {dim ? \"Choose scenario to unlock results\" : \"Scenario\"}\n      <\/div>\n      <div style={{display:\"flex\",gap:8}}>\n        {[\n            { k: \"CONSERVATIVE\", l: \"Conservative\", s: \"Lower-bound\", e: \"\\ud83d\\udee1\\ufe0f\" },\n            { k: \"EXPECTED\", l: \"Expected\", s: \"Best estimate\", e: \"\\ud83d\\udcca\" },\n            { k: \"STRETCH\", l: \"Stretch\", s: \"Upside\", e: \"\\ud83d\\ude80\" },\n          ].map(s => <button type=\"button\" key={s.k} onClick={()=>setScenarioMode(s.k)} className=\"roi-scenario\" style={{flex:\"1 1 0\",minWidth:90,padding:\"14px 10px\",textAlign:\"center\",border:scenarioMode===s.k?`2px solid ${C.accent}`:`1px solid ${C.border}`,borderRadius:10,cursor:\"pointer\",background:scenarioMode===s.k?C.accentPale:\"#fff\"}}>\n          <div style={{fontSize:20,marginBottom:4}}>{s.e}<\/div>\n          <div style={{fontSize:15,fontWeight:scenarioMode===s.k?700:500,color:scenarioMode===s.k?C.accent:C.textMid}}>{s.l}<\/div>\n          <div style={{fontSize:11,color:C.textMuted}}>{s.s}<\/div>\n        <\/button>)}\n      <\/div>\n    <\/Card>\n\n    {dim&&<div style={{textAlign:\"center\",padding:\"60px 20px\",color:C.textMuted}}><div style={{fontSize:48,marginBottom:12,opacity:.3}}>{\"\ud83d\udcca\"}<\/div><div style={{fontSize:16,fontWeight:600,color:C.textMid}}>Select a scenario above<\/div><\/div>}\n\n    {!dim && overrideCount > 0 && (\n      <div style={{\n        marginBottom: 16, padding: \"10px 16px\", background: C.overridePale,\n        borderRadius: 8, border: `1px solid ${C.override}33`,\n        display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\",\n        flexWrap: \"wrap\", gap: 8,\n      }}>\n        <div style={{ fontSize: 13, color: C.override }}><strong>{overrideCount}<\/strong> overridden<\/div>\n        <button type=\"button\" onClick={resetOv} style={{\n          border: \"none\", background: C.override, color: \"#fff\",\n          padding: \"8px 14px\", borderRadius: 6, fontSize: 12, fontWeight: 600, cursor: \"pointer\",\n        }}>Reset all<\/button>\n      <\/div>\n    )}\n\n    {\/* Headline cards *\/}\n    <div style={{display:\"flex\",gap:14,flexWrap:\"wrap\",marginBottom:20}}>\n      <ResultCard dimmed={dim} label=\"Total savings (3-year)\" value={an?<AnimatedK value={r.total3} active={an}\/>:\"\u2013\"} sub={an?\"Decom + clinician time x 3 years\":\"\"} bg={C.greenPale} border=\"#c3ddd3\" valueColor={C.green} \/>\n      <ResultCard dimmed={dim} label=\"Annual recurring\" value={an?<AnimatedK value={r.annual} active={an}\/>:\"\u2013\"} sub={an?`${fmtK(r.decomSave)} decom + ${fmtK(r.timeSave)} time`:\"\"} bg=\"#e8faf6\" border=\"#b0e8dc\" valueColor={C.accent} \/>\n      <ResultCard dimmed={dim} label=\"Clinician time (annual)\" value={an?<AnimatedK value={r.timeSave} active={an}\/>:\"\u2013\"} sub={an?<span>{fmtNum(r.hrsSaved)} hrs\/yr, {Math.round(r.realisation*100)}% realisation<\/span>:\"\"} bg={C.blue25} border={C.blue50} valueColor=\"#0f4146\" \/>\n    <\/div>\n\n    {\/* Decom \u2013 tiered breakdown *\/}\n    <Card dimmed={dim}><h3 style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} id=\"legacy-decommission-savings\">\n\t\t\t\tLegacy decommission savings<\/h3>\n      <p style={{fontSize:13,color:C.textMuted,margin:\"0 0 16px\"}}>Annual costs eliminated when systems are switched off.<\/p>\n      <div style={{overflowX:\"auto\",marginBottom:16}}>\n        <table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:14}}><thead><tr>\n          <th style={th_}>Tier<\/th><th style={{...th_,textAlign:\"right\"}}>Systems<\/th><th style={{...th_,textAlign:\"right\"}}>Cost\/sys\/yr<\/th><th style={{...th_,textAlign:\"right\"}}>Retired<\/th><th style={{...th_,textAlign:\"right\",color:C.green}}>Saving<\/th>\n        <\/tr><\/thead><tbody>\n          {[\n            {l:\"Enterprise\",n:r.ent,c:r.entCost,d:r.entDecom,s:r.entDecom*r.entCost},\n            {l:\"Departmental\",n:r.dep,c:r.depCost,d:r.depDecom,s:r.depDecom*r.depCost},\n            {l:\"Standalone\",n:r.nic,c:r.nicCost,d:r.nicDecom,s:r.nicDecom*r.nicCost},\n          ].filter(t=>t.n>0).map((t,i)=><tr key={i}><td style={{padding:\"10px 12px\",borderBottom:`1px solid ${C.borderLight}`}}>{t.l}<\/td><td style={tdR_}>{t.n}<\/td><td style={tdR_}>{fmtK(t.c)}<\/td><td style={tdR_}>{t.d}<\/td><td style={{...tdR_,fontWeight:700,color:C.green}}>{fmtK(t.s)}\/yr<\/td><\/tr>)}\n          {flagships.map((f,i) => <tr key={`f${i}`} style={{background:\"#fafcfb\"}}><td style={{padding:\"10px 12px\",borderBottom:`1px solid ${C.borderLight}`,fontStyle:\"italic\"}}>{f.name||`Named system ${i+1}`}<\/td><td style={tdR_}>1<\/td><td style={tdR_}>{fmtK(f.cost)}<\/td><td style={tdR_}>{f.retire?\"1\":\"0\"}<\/td><td style={{...tdR_,fontWeight:700,color:C.green}}>{f.retire?fmtK(f.cost)+\"\/yr\":\"\\u2013\"}<\/td><\/tr>)}\n          <tr style={{background:C.bg}}><td style={{padding:\"10px 12px\",fontWeight:700}}>Total<\/td><td style={{...tdR_,fontWeight:700}}>{r.legacy}<\/td><td style={{...tdR_,fontWeight:600,color:C.textMid}}>avg {fmtK(r.blendedCost)}<\/td><td style={{...tdR_,fontWeight:700}}>{r.decom}<\/td><td style={{...tdR_,fontWeight:800,color:C.green}}>{fmtK(r.decomSave)}\/yr<\/td><\/tr>\n        <\/tbody><\/table>\n      <\/div>\n      <Callout bg={C.greenPale} border=\"#c3ddd3\">\n        <strong style={{color:C.green}}>{r.decom} retired {\"\u2192\"} {fmtK(r.decomSave)}\/yr<\/strong>\n        <span style={{margin:\"0 8px\",color:C.textMuted}}>|<\/span>\n        Estate {fmtK(r.totalEstate)}\/yr {\"\u2192\"} {Math.round(r.decomSave\/Math.max(1,r.totalEstate)*100)}% reduction\n      <\/Callout>\n    <\/Card><LockedConnector\/>\n\n    {\/* Clinician time *\/}\n    <Card dimmed={dim}><h3 style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} id=\"isarchiveonlylegacy-lookup-time-freedclinical-workflow-time-freed\">\n\t\t\t\t{isArchiveOnly?\"Legacy lookup time freed\":\"Clinical workflow time freed\"}<\/h3>\n      <p style={{fontSize:13,color:C.textMuted,margin:\"0 0 16px\"}}>{isArchiveOnly?\"Time spent accessing legacy systems for historical records.\":\"System-switching time eliminated by consolidating to a single platform.\"}<\/p>\n      <div style={{display:\"flex\",gap:20,flexWrap:\"wrap\",marginBottom:16}}>\n        <OverridableStat label=\"Clinical staff\" computed={calc(inputs,am,{...mergedOv,clinicians:null},flagships).clinicians} override={overrides.clinicians} onOverride={v=>setOv(\"clinicians\",v)} info={`Clinical staff who regularly interact with legacy systems, including doctors, nurses, pharmacists and allied health professionals. Based on NHS workforce data: ${STAFF_PER_BED} clinical staff per acute bed, plus ${CORPORATE_STAFF_PER_ORG} corporate\/IG staff per organisation. Your estimate: ${fmtNum(Math.round(inputs.bed_count*STAFF_PER_BED+inputs.org_count*CORPORATE_STAFF_PER_ORG))} staff in scope.`}\/>\n        <OverridableStat label=\"Mins wasted \/ wk\" computed={calc(inputs,am,{...mergedOv,minsWasted:null},flagships).minsWasted} override={overrides.minsWasted} onOverride={v=>setOv(\"minsWasted\",v)} info={`Time clinicians lose each week navigating between legacy systems, searching for records, and context-switching. Starts at ${r.baseMin} min\/wk as a baseline${isArchiveOnly?\" (lower because your main workflow is already on your EPR)\":\"\"}, then increases with data quality issues (${inputs.data_quality_level} = ${DQ[inputs.data_quality_level]}x), system complexity (${inputs.complexity_level} = ${CX[inputs.complexity_level]}x), and a 4% overhead per additional legacy system. Based on Westbrook et al 2010 and Nuffield Trust 2020.`}\/>\n        <Stat label=\"Hours freed \/ yr\" value={an?<AnimatedNum value={r.hrsSaved} active={an}\/>:0} color={C.amber} info={`Total hours freed across all ${fmtNum(r.clinicians)} clinical staff when legacy system switching is eliminated. Accounts for 48 working weeks per year and subtracts ${isArchiveOnly?1:2} min\/wk residual (time still needed navigating the new system). Result: ${fmtNum(r.hrsSaved)} hours per year.`}\/>\n        <Stat label=\"Capacity value\" value={an?<AnimatedK value={r.timeSave} active={an}\/>:\"\u2013\"} sub={`${Math.round(r.realisation*100)}% realisation`} color={C.amber} info={`The value of freed clinician time, based on ${fmtNum(r.hrsSaved)} hours at the NHS blended rate of \u00a355\/hr (Agenda for Change mid-band including on-costs). Only ${Math.round(r.realisation*100)}% is counted as realisable capacity, since not all freed time converts to measurable output. This represents capacity redirected to patient care, not a direct cash saving.`}\/>\n      <\/div>\n      <Callout bg={C.greenPale} border=\"#c3ddd3\"><strong style={{color:C.green}}>{fmtNum(r.hrsSaved)} hrs\/yr<\/strong> {\"\u00d7\"} {\"\u00a3\"}55 {\"\u00d7\"} {Math.round(r.realisation*100)}% = <strong>{fmtK(r.timeSave)}\/yr<\/strong><\/Callout>\n    <\/Card><LockedConnector\/>\n\n    {\/* Clinical safety *\/}\n    {r.hasClinicalScope && <div>\n    <Card dimmed={dim} style={{border:\"1px solid #e0c4cf\",background:\"#faf5f7\"}}>\n      <div style={{display:\"flex\",alignItems:\"center\",gap:8,marginBottom:4}}>\n        <span style={{fontSize:20}}>{\"\ud83d\udee1\ufe0f\"}<\/span>\n        <h3 style={{fontSize:16,fontWeight:700,margin:0}} style={{fontSize:16,fontWeight:700,margin:0}} id=\"isarchiveonlypatient-safety-contextpatient-safety-case-for-migration\">\n\t\t\t\t{isArchiveOnly?\"Patient safety context\":\"Patient safety case for migration\"}<\/h3>\n        <InfoTip text={`Source: Camacho et al 2024 (BMJ Quality &#038; Safety). Per-bed rates from ~100K acute beds nationally, scaled by fragmentation (${(0.6+Math.min(r.legacy,20)\/20*0.6).toFixed(2)}x). NOT included in financial totals.`}\/>\n      <\/div>\n      <p style={{fontSize:13,color:C.textMid,margin:\"0 0 16px\"}}>{isArchiveOnly?\"Fewer fragmented systems means fewer information gaps during care transitions.\":\"Consolidating onto a single EPR directly reduces information gaps at care transitions, which is the primary patient safety justification for migration.\"}<\/p>\n      <div style={{display:\"flex\",gap:14,flexWrap:\"wrap\",marginBottom:16}}>\n        {[\n          {label:\"Medication errors\",bl:r.safetyMedErrorsBaseline,av:r.safetyMedErrorsAvoided,avl:\"potentially avoidable\",sub:\"at admission, transfer, or discharge\"},\n          {label:\"Patients harmed\",bl:Math.round(r.safetyPatientsProtected\/(SCENARIO[am].safety||0.25)),av:r.safetyPatientsProtected,avl:\"potentially protected\",sub:\"by medication errors during handoffs\"},\n          {label:\"Excess bed days\",bl:Math.round(r.safetyBedDaysAvoided\/(SCENARIO[am].safety||0.25)),av:r.safetyBedDaysAvoided,avl:\"potentially avoidable\",sub:\"from adverse medication events\"},\n        ].map((m,i)=><div key={i} style={{flex:\"1 1 150px\",minWidth:140,padding:\"16px 18px\",background:\"#fff\",borderRadius:10,textAlign:\"center\",border:`1px solid ${C.rose}18`}}>\n          <div style={{fontSize:11,fontWeight:600,color:C.textMuted,marginBottom:6}}>{m.label}<\/div>\n          <div style={{fontSize:26,fontWeight:800,color:C.rose}}>{an?fmtNum(m.bl):0}<span style={{fontSize:13,fontWeight:600}}>\/yr<\/span><\/div>\n          <div style={{fontSize:11,color:C.textMuted,marginTop:4}}>{m.sub}<\/div>\n          <div style={{marginTop:8,fontSize:12,color:C.rose,fontWeight:600}}>{\"\u2193\"} {an?fmtNum(m.av):0} {m.avl}<\/div>\n        <\/div>)}\n      <\/div>\n      {\/* Litigation context *\/}\n      <div style={{padding:\"12px 16px\",background:\"#fff\",borderRadius:8,border:`1px solid ${C.rose}15`,fontSize:12,lineHeight:1.6,color:C.textMid}}>\n        <strong style={{color:C.text}}>Evidence & litigation context<\/strong><br\/>\n        Camacho et al 2024 (BMJ Quality & Safety): 1.8M medication errors at NHS care transitions\/yr, 31,500 patients harmed, 36,500 excess bed days. NHS trusts collectively pay <strong>{\"\u00a3\"}3.6bn\/yr<\/strong> in clinical negligence settlements (NAO, October 2025). CNST contributions are directly influenced by claims history {\"\\u2013\"} fewer medication errors means fewer potential claims and lower premiums. Every patient harmed by an information gap is a potential negligence claim.\n      <\/div>\n    <\/Card><LockedConnector\/>\n    <\/div>}\n\n    {\/* KPIs *\/}\n    <Card dimmed={dim} style={{background:C.surface,border:`1px solid ${C.border}`}}>\n      <h3 style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} id=\"projected-kpi-improvements\">\n\t\t\t\tProjected KPI improvements<\/h3>\n      <div style={{display:\"flex\",gap:14,flexWrap:\"wrap\",marginBottom:16,marginTop:16}}>\n        <div style={{flex:\"1 1 180px\",minWidth:170,padding:\"18px 20px\",background:C.greenPale,borderRadius:10,textAlign:\"center\"}}>\n          <div style={{fontSize:12,fontWeight:600,color:C.textMuted}}>Decommissioned<\/div>\n          <div style={{fontSize:42,fontWeight:800,color:C.green,margin:\"6px 0\"}}>{an?<AnimatedNum value={r.decom} active={an}\/>:0}<\/div>\n          <div style={{fontSize:13,color:C.textMid}}>of {r.legacy}<\/div>\n          <div style={{marginTop:8,display:\"inline-block\",padding:\"4px 12px\",borderRadius:20,background:C.green,color:\"#fff\",fontSize:13,fontWeight:700}}>{Math.round(r.decom\/Math.max(1,r.legacy)*100)}%<\/div>\n        <\/div>\n        <div style={{flex:\"1 1 200px\",minWidth:240,padding:\"18px 20px\",background:\"#e8faf6\",borderRadius:10}}>\n          <div style={{display:\"flex\",alignItems:\"center\",gap:8,marginBottom:10}}><span style={{fontSize:14,fontWeight:700}}>Support tickets (monthly)<\/span><\/div>\n          <div style={{display:\"flex\",justifyContent:\"space-between\",alignItems:\"center\",fontSize:12,color:C.textMuted,marginBottom:3}}><span>Before<\/span><OverridableStat inline label=\"\" computed={calc(inputs,am,{...mergedOv,ticketsBaseline:null},flagships).ticketsBaselineMonthly} override={overrides.ticketsBaseline} onOverride={v=>setOv(\"ticketsBaseline\",v)} suffix=\"\/mo\" info={`Estimated monthly IT support tickets across your ${r.legacy} legacy systems. Based on typical NHS service desk volumes of 2.5 tickets per system per month (password resets, access issues, data queries, interface failures), adjusted for data quality. Your baseline: ${r.ticketsBaselineMonthly} tickets\/month.`}\/><\/div>\n          <div style={{height:8,background:\"#e0e4ea\",borderRadius:4,marginBottom:6}}><div style={{height:\"100%\",width:\"100%\",background:\"#b0b8c4\",borderRadius:4}}\/><\/div>\n          <div style={{display:\"flex\",justifyContent:\"space-between\",fontSize:12,color:C.textMuted,marginBottom:3}}><span>After<\/span><span style={{fontWeight:600,color:C.teal}}>{r.ticketsAfter}\/mo<\/span><\/div>\n          <div style={{height:8,background:\"#e0e4ea\",borderRadius:4,marginBottom:8}}><div style={{height:\"100%\",width:`${Math.max(5,Math.round(r.ticketsAfter\/Math.max(1,r.ticketsBaselineMonthly)*100))}%`,background:C.teal,borderRadius:4,transition:\"width .8s\"}}\/><\/div>\n          <div style={{fontSize:20,fontWeight:800,color:C.teal}}>{\"\u2193\"} {an?<AnimatedNum value={r.ticketsReductionPct} active={an}\/>:0}%<\/div>\n        <\/div>\n        <div style={{flex:\"1 1 200px\",minWidth:240,padding:\"18px 20px\",background:C.blue25,borderRadius:10}}>\n          <div style={{display:\"flex\",alignItems:\"center\",gap:8,marginBottom:10}}><span style={{fontSize:14,fontWeight:700}}>SAR turnaround<\/span><\/div>\n          <div style={{display:\"flex\",justifyContent:\"space-between\",alignItems:\"center\",fontSize:12,color:C.textMuted,marginBottom:3}}><span>Before<\/span><OverridableStat inline label=\"\" computed={calc(inputs,am,{...mergedOv,sarDaysBefore:null},flagships).sarDaysBefore} override={overrides.sarDaysBefore} onOverride={v=>setOv(\"sarDaysBefore\",v)} suffix=\" days\" step={.5} info={`Working days to fulfil a subject access request. Each legacy system adds search time, login overhead, and potentially manual extraction. With ${r.legacy} systems, estimated turnaround is ${r.sarDaysBefore} working days. The ICO requires organisations to search all systems where patient data may be held.`}\/><\/div>\n          <div style={{height:8,background:\"#e0e4ea\",borderRadius:4,marginBottom:6}}><div style={{height:\"100%\",width:\"100%\",background:\"#b0b8c4\",borderRadius:4}}\/><\/div>\n          <div style={{display:\"flex\",justifyContent:\"space-between\",fontSize:12,color:C.textMuted,marginBottom:3}}><span>After<\/span><span style={{fontWeight:600,color:C.accent}}>{r.sarDaysAfter} days<\/span><\/div>\n          <div style={{height:8,background:\"#e0e4ea\",borderRadius:4,marginBottom:8}}><div style={{height:\"100%\",width:`${Math.max(5,Math.round(r.sarDaysAfter\/Math.max(1,r.sarDaysBefore)*100))}%`,background:C.accent,borderRadius:4,transition:\"width .8s\"}}\/><\/div>\n          <div style={{fontSize:20,fontWeight:800,color:C.accent}}>{\"\u2193\"} {an?<AnimatedNum value={r.sarReductionPct} active={an}\/>:0}%<\/div>\n        <\/div>\n      <\/div>\n    <\/Card><LockedConnector\/>\n\n    {\/* Year-by-year *\/}\n    <Card dimmed={dim}>\n      <h3 style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} style={{fontSize:16,fontWeight:700,margin:\"0 0 4px\"}} id=\"year-by-year-breakdown\">\n\t\t\t\tYear-by-year breakdown<\/h3>\n      <p style={{fontSize:13,color:C.textMuted,margin:\"0 0 16px\"}}>({fmtK(r.decomSave)} + {fmtK(r.timeSave)}) {\"\u00d7\"} 3 = <strong style={{color:C.green}}>{fmtK(r.total3)}<\/strong><\/p>\n      <div style={{overflowX:\"auto\"}}>\n        <table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:14}}><thead><tr>\n          <th style={th_}>Benefit<\/th><th style={{...th_,textAlign:\"right\"}}>Y1<\/th><th style={{...th_,textAlign:\"right\"}}>Y2<\/th><th style={{...th_,textAlign:\"right\"}}>Y3<\/th><th style={{...th_,textAlign:\"right\",color:C.accent}}>Total<\/th>\n        <\/tr><\/thead><tbody>\n          {[\n            {l:\"Decom savings\",y:r.decomSave},\n            {l:\"Clinician time\",y:r.timeSave},\n          ].map((row,i)=><tr key={i}><td style={{padding:\"10px 12px\",borderBottom:`1px solid ${C.borderLight}`}}>{row.l} <span style={{fontSize:10,fontWeight:600,padding:\"2px 7px\",borderRadius:4,background:C.greenPale,color:C.green,textTransform:\"uppercase\"}}>recurring<\/span><\/td><td style={tdR_}>{fmt(row.y)}<\/td><td style={tdR_}>{fmt(row.y)}<\/td><td style={tdR_}>{fmt(row.y)}<\/td><td style={{...tdR_,fontWeight:600,color:C.accent}}>{fmt(row.y*3)}<\/td><\/tr>)}\n          <tr style={{background:C.bg}}><td style={{padding:\"12px\",fontWeight:700}}>Total<\/td><td style={{...tdR_,fontWeight:700}}>{fmt(r.annual)}<\/td><td style={{...tdR_,fontWeight:700}}>{fmt(r.annual)}<\/td><td style={{...tdR_,fontWeight:700}}>{fmt(r.annual)}<\/td><td style={{...tdR_,fontWeight:800,color:C.green,fontSize:15}}>{fmt(r.total3)}<\/td><\/tr>\n          <tr style={{background:C.bg}}><td style={{padding:\"12px\",fontWeight:600,color:C.textMid}}>Cumulative<\/td><td style={{...tdR_,fontWeight:600,color:C.textMid}}>{fmt(r.annual)}<\/td><td style={{...tdR_,fontWeight:600,color:C.textMid}}>{fmt(r.annual*2)}<\/td><td style={{...tdR_,fontWeight:600,color:C.textMid}}>{fmt(r.total3)}<\/td><td><\/td><\/tr>\n        <\/tbody><\/table>\n      <\/div>\n      <div style={{marginTop:14,padding:\"10px 14px\",background:C.bg,borderRadius:6,fontSize:12,color:C.textMuted,display:\"flex\",gap:16,flexWrap:\"wrap\"}}>\n        <span>Showing: <strong style={{color:C.accent}}>{am===\"CONSERVATIVE\"?\"Conservative\":am===\"STRETCH\"?\"Stretch\":\"Expected\"}<\/strong><\/span>\n        <span>Range: <strong>{fmtK(rCon.total3)}<\/strong> {\"\u2013\"} <strong>{fmtK(rStr.total3)}<\/strong><\/span>\n      <\/div>\n    <\/Card>\n\n    {\/* Lead gen *\/}\n    {!dim && !leadSubmitted && (\n      <Card style={{marginTop:20,background:`linear-gradient(135deg,${C.navy} 0%,${C.navyMid} 100%)`,border:\"none\",color:\"#fff\"}}>\n        <div style={{display:\"flex\",alignItems:\"center\",gap:10,marginBottom:4}}>\n          <span style={{fontSize:24}}>{\"\ud83d\udce9\"}<\/span>\n          <h3 style={{fontSize:18,fontWeight:800,margin:0}} style={{fontSize:18,fontWeight:800,margin:0}} id=\"download-your-report\">\n\t\t\t\tDownload your report<\/h3>\n        <\/div>\n        <p style={{fontSize:13,opacity:.7,marginBottom:20}}>Download a formatted summary ready to share with your team or board.<\/p>\n        <div style={{display:\"flex\",gap:12,flexWrap:\"wrap\",marginBottom:12}}>\n          <input type=\"text\" className=\"roi-lead-input\" placeholder=\"Your name *\" value={leadForm.name} onChange={e=>setLeadForm(p=>({...p,name:e.target.value}))} style={{flex:\"1 1 200px\",minWidth:160,padding:\"11px 14px\",borderRadius:999,border:\"1px solid rgba(255,255,255,0.2)\",background:\"rgba(255,255,255,0.1)\",color:\"#fff\",fontSize:14,outline:\"none\",fontFamily:\"inherit\"}} \/>\n          <input type=\"email\" className=\"roi-lead-input\" placeholder=\"Work email *\" value={leadForm.email} onChange={e=>setLeadForm(p=>({...p,email:e.target.value}))} style={{flex:\"1 1 200px\",minWidth:160,padding:\"11px 14px\",borderRadius:999,border:\"1px solid rgba(255,255,255,0.2)\",background:\"rgba(255,255,255,0.1)\",color:\"#fff\",fontSize:14,outline:\"none\",fontFamily:\"inherit\"}} \/>\n        <\/div>\n        <div style={{display:\"flex\",gap:12,flexWrap:\"wrap\",marginBottom:20}}>\n          <input type=\"text\" className=\"roi-lead-input\" placeholder=\"Organisation\" value={leadForm.org} onChange={e=>setLeadForm(p=>({...p,org:e.target.value}))} style={{flex:\"1 1 200px\",minWidth:160,padding:\"11px 14px\",borderRadius:999,border:\"1px solid rgba(255,255,255,0.2)\",background:\"rgba(255,255,255,0.1)\",color:\"#fff\",fontSize:14,outline:\"none\",fontFamily:\"inherit\"}} \/>\n          <select value={leadForm.role} onChange={e=>setLeadForm(p=>({...p,role:e.target.value}))} style={{flex:\"1 1 200px\",minWidth:160,padding:\"11px 14px\",borderRadius:999,border:\"1px solid rgba(255,255,255,0.2)\",background:\"rgba(255,255,255,0.15)\",color:leadForm.role?\"#fff\":\"rgba(255,255,255,0.5)\",fontSize:14,outline:\"none\",fontFamily:\"inherit\"}}>\n            <option value=\"\" style={{color:\"#1b2433\"}}>Your role<\/option>\n            <option value=\"CIO\/CCIO\" style={{color:\"#1b2433\"}}>CIO \/ CCIO<\/option>\n            <option value=\"CTO\" style={{color:\"#1b2433\"}}>CTO \/ Technical Director<\/option>\n            <option value=\"Programme Lead\" style={{color:\"#1b2433\"}}>Programme \/ Project Lead<\/option>\n            <option value=\"Digital Lead\" style={{color:\"#1b2433\"}}>Digital Transformation Lead<\/option>\n            <option value=\"IG\/DPO\" style={{color:\"#1b2433\"}}>IG Lead \/ DPO<\/option>\n            <option value=\"Finance\" style={{color:\"#1b2433\"}}>Finance \/ Business Case<\/option>\n            <option value=\"Other\" style={{color:\"#1b2433\"}}>Other<\/option>\n          <\/select>\n        <\/div>\n        <button type=\"button\" onClick={submitLead} disabled={!leadForm.name||!leadForm.email||leadSending} style={{\n          padding:\"14px 32px\",background:(!leadForm.name||!leadForm.email)?\"rgba(255,255,255,0.15)\":\"#fff\",\n          color:(!leadForm.name||!leadForm.email)?\"rgba(255,255,255,0.4)\":C.accent,\n          border:\"none\",borderRadius:999,fontSize:15,fontWeight:700,cursor:(!leadForm.name||!leadForm.email)?\"not-allowed\":\"pointer\",display:\"flex\",alignItems:\"center\",gap:8\n        }} className=\"roi-cta\">{leadSending ? \"Generating report...\" : \"Download your report\"}<\/button>\n        <p style={{fontSize:11,opacity:.4,marginTop:12}}>{EMAIL_ENDPOINT ? \"Your report will also be emailed to you. \" : \"\"}We may reach out to see if we can help with your programme.<\/p>\n      <\/Card>\n    )}\n\n    {!dim && leadSubmitted && (\n      <Card style={{marginTop:20,background:C.greenPale,border:\"1px solid #c3ddd3\"}}>\n        <div style={{textAlign:\"center\",padding:\"12px 0\"}}>\n          <h3 style={{fontSize:18,fontWeight:800,color:C.green,margin:\"0 0 6px\"}} style={{fontSize:18,fontWeight:800,color:C.green,margin:\"0 0 6px\"}} id=\"%e2%9c%93-report-downloaded\">\n\t\t\t\t{\"\u2713\"} Report downloaded<\/h3>\n          <p style={{fontSize:14,color:C.textMid}}>{EMAIL_ENDPOINT ? \"A copy is also being sent to \" + leadForm.email + \".\" : \"Your PDF has been saved.\"}<\/p>\n          <button type=\"button\" onClick={async ()=>{await generatePDF();}} className=\"roi-btn\" style={{marginTop:12,padding:\"10px 24px\",background:C.accent,color:\"#fff\",border:\"none\",borderRadius:999,fontSize:14,fontWeight:600,cursor:\"pointer\"}}>Download again<\/button>\n        <\/div>\n      <\/Card>\n    )}\n\n\n    {\/* \u2500\u2500 Full Methodology \u2500\u2500 *\/}\n    {!dim && (() => {\n      const S = ({title, children, defaultOpen}) => {\n        const [open, setOpen] = useState(defaultOpen || false);\n        return <div style={{borderBottom:`1px solid ${C.borderLight}`}}>\n          <button type=\"button\" onClick={()=>setOpen(!open)} style={{display:\"flex\",alignItems:\"center\",justifyContent:\"space-between\",width:\"100%\",padding:\"13px 0\",background:\"none\",border:\"none\",cursor:\"pointer\",fontSize:14,fontWeight:700,color:C.text,textAlign:\"left\"}}>\n            <span>{title}<\/span>\n            <span style={{fontSize:16,color:C.textMuted,transform:open?\"rotate(180deg)\":\"none\",transition:\"transform .2s\",flexShrink:0,marginLeft:8}}>{\"\\u25be\"}<\/span>\n          <\/button>\n          {open && <div style={{paddingBottom:16,fontSize:13,lineHeight:1.7,color:C.textMid}}>{children}<\/div>}\n        <\/div>;\n      };\n      const Tag = ({label, color, source}) => {\n        const [hover, setHover] = useState(false);\n        return <span style={{position:\"relative\",display:\"inline-block\",marginLeft:6,verticalAlign:\"middle\"}} onMouseEnter={()=>setHover(true)} onMouseLeave={()=>setHover(false)}>\n          <span style={{display:\"inline-block\",padding:\"2px 8px\",borderRadius:4,fontSize:10,fontWeight:700,background:color||C.accentPale,color:color?\"#fff\":C.accent,cursor:source?\"help\":\"default\"}}>{label}<\/span>\n          {hover && source && <span style={{position:\"absolute\",bottom:\"calc(100% + 6px)\",left:\"50%\",transform:\"translateX(-50%)\",background:C.navy,color:\"#fff\",fontSize:11,lineHeight:1.5,padding:\"8px 12px\",borderRadius:6,width:260,maxWidth:\"80vw\",boxShadow:\"0 4px 16px rgba(0,0,0,.22)\",zIndex:20,pointerEvents:\"none\",fontWeight:400,whiteSpace:\"normal\"}}>{source}<\/span>}\n        <\/span>;\n      };\n      const Ev = ({s}) => <Tag label=\"Evidence-based\" color={C.green} source={s}\/>;\n      const Bm = ({s}) => <Tag label=\"Industry benchmark\" color=\"#1a7a7a\" source={s}\/>;\n      const Bn = ({s}) => <Tag label=\"Benchmarked\" color=\"#8a6d2b\" source={s}\/>;\n      const Md = ({s}) => <Tag label=\"Modelled estimate\" color={C.textMuted} source={s}\/>;\n      const F = ({children}) => <div style={{padding:\"12px 16px\",background:C.bg,borderRadius:8,margin:\"10px 0\",fontFamily:\"'FK Grotesk Neue','FK Grotesk',monospace\",fontSize:12,color:C.text,lineHeight:1.6}}>{children}<\/div>;\n\n      return <Card style={{marginTop:20}}>\n        <div style={{display:\"flex\",alignItems:\"center\",gap:8,marginBottom:4}}>\n          <span style={{fontSize:20}}>{\"\\ud83d\\udcd0\"}<\/span>\n          <h3 style={{fontSize:17,fontWeight:800,margin:0,color:C.text}} style={{fontSize:17,fontWeight:800,margin:0,color:C.text}} id=\"how-we-calculated-this\">\n\t\t\t\tHow we calculated this<\/h3>\n        <\/div>\n        <p style={{fontSize:13,color:C.textMuted,margin:\"4px 0 12px\"}}>Full methodology, formulae and evidence base. Every figure marked {\"\\u270e\"} in the calculator is overridable.<\/p>\n\n        <S title=\"1. Scenario multipliers\" defaultOpen={true}>\n          <p style={{margin:\"0 0 8px\"}}>Three scenarios scale all outputs to reflect confidence levels.<\/p>\n          <div style={{overflowX:\"auto\"}}><table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:12,margin:\"8px 0\"}}>\n            <thead><tr style={{background:C.accent,color:\"#fff\"}}><th style={{padding:\"6px 10px\",textAlign:\"left\"}}>Parameter<\/th><th style={{padding:\"6px 10px\"}}>Conservative<\/th><th style={{padding:\"6px 10px\"}}>Expected<\/th><th style={{padding:\"6px 10px\"}}>Stretch<\/th><\/tr><\/thead>\n            <tbody>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\",fontWeight:600}}>Decommission scaling<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>0.85x<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.00x<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.10x<\/td><\/tr>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\",fontWeight:600}}>Time realisation rate<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>20%<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>30%<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>40%<\/td><\/tr>\n              <tr><td style={{padding:\"6px 10px\",fontWeight:600}}>Safety reduction<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>15%<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>25%<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>35%<\/td><\/tr>\n            <\/tbody>\n          <\/table><\/div>\n          <p style={{margin:\"8px 0 4px\"}}><strong>Decom scaling<\/strong> adjusts how many of your planned retirements actually happen {\" - \"} Conservative assumes 85% (programme delays, contractual holdups), Stretch assumes 10% overachievement. <Md s=\"No published benchmark. User sets their own target and this scales it.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Time realisation<\/strong> {\" - \"} the fraction of freed clinician time that translates into measurable capacity. NHS Productive Ward reported 20-40%. <Bm s=\"NHS Productive Ward programme; Lord Carter review of operational productivity.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Safety reduction<\/strong> {\" - \"} avoidable medication errors from system consolidation. Camacho et al 2024 (BMJ Quality & Safety), Gate et al 2023 found 25-50% reductions. <Ev s=\"Camacho et al 2024 (BMJ Quality &#038; Safety); Gate et al 2023.\"\/><\/p>\n        <\/S>\n\n        <S title=\"2. System cost estimation\">\n          <p style={{margin:\"0 0 8px\"}}>Each legacy system is classified into one of three tiers. Annual cost scales with bed count and complexity.<\/p>\n          <F>tierCost = round((base + beds {\"\\u00d7\"} perBed) {\"\\u00d7\"} complexity \/ 1000) {\"\\u00d7\"} 1000<\/F>\n          <div style={{overflowX:\"auto\"}}><table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:12,margin:\"8px 0\"}}>\n            <thead><tr style={{background:C.accent,color:\"#fff\"}}><th style={{padding:\"6px 10px\",textAlign:\"left\"}}>Tier<\/th><th style={{padding:\"6px 10px\"}}>Base<\/th><th style={{padding:\"6px 10px\"}}>Per bed<\/th><th style={{padding:\"6px 10px\"}}>800 beds (Typical)<\/th><\/tr><\/thead>\n            <tbody>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\",fontWeight:600}}>Enterprise<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}300k<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}700\/bed<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700}}>{\"\\u00a3\"}860k\/yr<\/td><\/tr>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\",fontWeight:600}}>Departmental<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}75k<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}160\/bed<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700}}>{\"\\u00a3\"}203k\/yr<\/td><\/tr>\n              <tr><td style={{padding:\"6px 10px\",fontWeight:600}}>Standalone<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}14k<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}20\/bed<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700}}>{\"\\u00a3\"}30k\/yr<\/td><\/tr>\n            <\/tbody>\n          <\/table><\/div>\n          <p style={{margin:\"8px 0 4px\"}}>These formulas were calibrated against real contract data from a large acute NHS Trust (~1,385 beds, 42 legacy systems). Default estimates cover ~80% of actual estate value. <Bn s=\"Validated against contract register from a large acute NHS Trust (~1,385 beds, 42 systems, 2024\/25 data).\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}>The complexity multiplier adjusts costs: Low 0.70, Typical 1.00, High 1.45. <Md s=\"Calibrated to produce approximately +\/-30% variation. Benchmarked Trust classified as HIGH (1.45).\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}>For trusts with known contract values, click any tier cost to override it, add named flagship systems with actual costs, or use {\"\\u201c\"}I know my spend{\"\\u201d\"} mode to enter your total directly.<\/p>\n        <\/S>\n\n        <S title=\"3. Decommission savings\">\n          <p style={{margin:\"0 0 8px\"}}>The primary financial benefit: retiring legacy systems eliminates their annual running costs.<\/p>\n          <F>1. Set decom target (default 75%)<br\/>2. rawDecom = totalSystems {\"\\u00d7\"} decomTarget {\"\\u00d7\"} scenarioMultiplier<br\/>3. Systems retired pro-rata across tiers<br\/>4. decomSave = (entRetired {\"\\u00d7\"} entCost) + (depRetired {\"\\u00d7\"} depCost) + (nicRetired {\"\\u00d7\"} nicCost) + flagshipSave<\/F>\n          <p style={{margin:\"8px 0 4px\"}}><strong>75% default decom target<\/strong> {\" - \"} most NHS EPR programmes plan to retire 60-80% of legacy systems. Some are retained for long-retention data or functions not covered by the new EPR. Validated against a real Trust where ~32 of 42 systems were candidates for retirement. <Bm s=\"NHS EPR programme consensus; validated against benchmarked Trust application footprint.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Named flagship systems<\/strong> flagged as {\"\\u201c\"}Retire{\"\\u201d\"} are decommissioned at their full stated contract value, scaled by the scenario multiplier.<\/p>\n        <\/S>\n\n        <S title=\"4. Clinician time savings\">\n          <p style={{margin:\"0 0 8px\"}}>Time freed when clinicians no longer switch between multiple legacy systems.<\/p>\n          <F>clinicians = beds {\"\\u00d7\"} 2.8 + orgs {\"\\u00d7\"} 80<br\/>minsWasted = baseMin {\"\\u00d7\"} DQ {\"\\u00d7\"} CX {\"\\u00d7\"} (1 + switchPenalty)<br\/>switchPenalty = max(0, systems - 1) {\"\\u00d7\"} 0.04<br\/>hrsSaved = clinicians {\"\\u00d7\"} (minsWasted - residual) {\"\\u00d7\"} 48wks \/ 60<br\/>timeSave = hrsSaved {\"\\u00d7\"} {\"\\u00a3\"}55\/hr {\"\\u00d7\"} realisationRate<\/F>\n          <p style={{margin:\"8px 0 4px\"}}><strong>2.8 staff per bed<\/strong> {\" - \"} NHS workforce stats 2023\/24 show 2.6-3.0 clinical full-time equivalents (FTEs) per acute bed (doctors, nurses, allied health professionals (AHPs), pharmacists). <Ev s=\"NHS Digital workforce statistics 2023\/24; Lord Carter operational productivity review.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>12 min\/wk base<\/strong> (migration path) {\" - \"} Westbrook et al 2010 (JAMIA) found nurses interrupted 3-6 times\/hour by system-switching. Nuffield Trust 2020 estimated 15-20 min\/day lost to system navigation. Our 12 min\/week is conservative (reducible portion only). Archive-only uses 5 min\/wk. <Ev s=\"Westbrook et al 2010 (JAMIA); Nuffield Trust 2020 system navigation study.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>+4% per system<\/strong> {\" - \"} each additional legacy system adds cognitive overhead. At 42 systems (as in the benchmarked Trust), this produces ~10 min\/day wasted, consistent with published studies. <Md s=\"First-principles switching cost model. Validated against benchmarked Trust (42 systems).\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>{\"\\u00a3\"}55\/hr blended rate<\/strong> {\" - \"} NHS Agenda for Change (AfC) mid-band including on-costs. Band 5 nurse ~{\"\\u00a3\"}40-45\/hr, consultants ~{\"\\u00a3\"}80-120\/hr. Weighted towards nursing (largest user group). <Ev s=\"NHS Employers Agenda for Change pay scales 2024\/25.\"\/><\/p>\n        <\/S>\n\n        <S title=\"5. Support tickets &#038; subject access request (SAR) turnaround\">\n          <p style={{margin:\"0 0 8px\"}}><strong>Tickets:<\/strong> 2.5 per system\/month baseline (IT Infrastructure Library \/ ITIL service desk metrics). Surviving systems generate 40% fewer tickets due to consolidated support. <Bm s=\"ITIL service desk reporting in NHS trusts; 0.6 factor is a modelled estimate.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Subject access request (SAR) turnaround:<\/strong> 1.5 day base + 0.4 days per system (before), 0.15 days per surviving system (after). The Information Commissioner's Office (ICO) requires searching all systems where patient data may be held. At 42 systems, this produces ~18 working days per SAR {\" - \"} consistent with reports from trusts with fragmented estates. <Md s=\"Consistent with NHS IG team reports. Validated against benchmarked Trust.\"\/><\/p>\n        <\/S>\n\n        <S title=\"6. Patient safety metrics\">\n          <p style={{margin:\"0 0 8px\"}}>These are <strong>not included<\/strong> in the financial ROI total. They provide qualitative context for the patient safety case.<\/p>\n          <F>medErrors = beds {\"\\u00d7\"} 18 {\"\\u00d7\"} fragmentation {\"\\u00d7\"} DQ<br\/>patientsHarmed = beds {\"\\u00d7\"} 0.315 {\"\\u00d7\"} fragmentation {\"\\u00d7\"} DQ<br\/>excessBedDays = beds {\"\\u00d7\"} 0.365 {\"\\u00d7\"} fragmentation {\"\\u00d7\"} DQ<\/F>\n          <p style={{margin:\"8px 0 4px\"}}><strong>18 medication errors per bed\/yr<\/strong> {\" - \"} Camacho et al 2024 reported ~1.8M medication errors at care transitions across NHS England (~100k acute beds). <Ev s=\"Camacho et al 2024 (BMJ Quality &#038; Safety), 1.8M errors \/ ~100K beds.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>0.315 patients harmed per bed\/yr<\/strong> and <strong>0.365 excess bed days per bed\/yr<\/strong> {\" - \"} both from Camacho et al 2024. <Ev s=\"Camacho et al 2024: 31,500 patients harmed and 36,500 excess bed days annually.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Fragmentation index<\/strong> (0.6-1.2) scales risk by number of systems. Caps at 20 systems (diminishing marginal fragmentation). <Md s=\"No published fragmentation index exists. Designed to produce sensible scaling without overstating.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Litigation context:<\/strong> NHS trusts collectively pay {\"\\u00a3\"}3.6bn\/yr in clinical negligence settlements (National Audit Office, October 2025). {\"\\u00a3\"}60bn total provision (NHS Resolution 2024\/25). Clinical Negligence Scheme for Trusts (CNST) premiums are directly influenced by claims history. No per-trust figure is fabricated {\" - \"} the mechanism is real but not directly quantifiable at trust level. <Ev s=\"National Audit Office Oct 2025; NHS Resolution Annual Report 2024\/25.\"\/><\/p>\n        <\/S>\n\n        <S title=\"7. Real-world validation\">\n          <p style={{margin:\"0 0 8px\"}}>All tier cost formulas were validated against the complete application footprint and contract register of a large acute NHS Trust (~1,385 beds, dual-site merged estate).<\/p>\n          <div style={{overflowX:\"auto\"}}><table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:12,margin:\"8px 0\"}}>\n            <thead><tr style={{background:C.accent,color:\"#fff\"}}><th style={{padding:\"6px 10px\",textAlign:\"left\"}}>Method<\/th><th style={{padding:\"6px 10px\"}}>Expected result<\/th><th style={{padding:\"6px 10px\"}}>vs {\"\\u00a3\"}16m target<\/th><\/tr><\/thead>\n            <tbody>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\"}}>Defaults only<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}11.9m\/yr<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u221226%\"}<\/td><\/tr>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\"}}>Tier cost overrides<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700}}>{\"\\u00a3\"}16.4m\/yr<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700,color:C.green}}>{\"+2.5%\"}<\/td><\/tr>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\"}}>Flagships + overrides<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"\\u00a3\"}19.0m\/yr<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>{\"+19%\"}<\/td><\/tr>\n              <tr><td style={{padding:\"6px 10px\"}}>{\"\\u201c\"}I know my spend{\"\\u201d\"} mode<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700}}>{\"\\u00a3\"}16.4m\/yr<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\",fontWeight:700,color:C.green}}>{\"+2.5%\"}<\/td><\/tr>\n            <\/tbody>\n          <\/table><\/div>\n          <p style={{margin:\"8px 0 4px\"}}>The Trust had 42 unique systems (3 enterprise, 15 departmental, 24 standalone) with {\"\\u00a3\"}16.75m\/yr total contract value. Four systems accounted for 78% of spend {\" - \"} exactly the pattern the flagship feature captures. The stated {\"\\u00a3\"}16m\/yr saving estimate falls between Conservative and Expected, confirming the scenario corridor brackets real-world figures.<\/p>\n        <\/S>\n\n        <S title=\"8. Complexity and data quality adjustments\">\n          <p style={{margin:\"0 0 8px\"}}>Two multipliers adjust the model for your specific environment.<\/p>\n          <div style={{overflowX:\"auto\"}}><table style={{width:\"100%\",borderCollapse:\"collapse\",fontSize:12,margin:\"8px 0\"}}>\n            <thead><tr style={{background:C.accent,color:\"#fff\"}}><th style={{padding:\"6px 10px\",textAlign:\"left\"}}>Setting<\/th><th style={{padding:\"6px 10px\"}}>Low \/ Clean<\/th><th style={{padding:\"6px 10px\"}}>Typical \/ Mixed<\/th><th style={{padding:\"6px 10px\"}}>High \/ Poor<\/th><th style={{padding:\"6px 10px\"}}>What it affects<\/th><\/tr><\/thead>\n            <tbody>\n              <tr style={{borderBottom:`1px solid ${C.borderLight}`}}><td style={{padding:\"6px 10px\",fontWeight:600}}>Complexity<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>0.70<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.00<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.45<\/td><td style={{padding:\"6px 10px\"}}>System costs, minutes wasted<\/td><\/tr>\n              <tr><td style={{padding:\"6px 10px\",fontWeight:600}}>Data quality<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>0.75<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.00<\/td><td style={{padding:\"6px 10px\",textAlign:\"center\"}}>1.40<\/td><td style={{padding:\"6px 10px\"}}>Minutes wasted, tickets, SAR time<\/td><\/tr>\n            <\/tbody>\n          <\/table><\/div>\n          <p style={{margin:\"8px 0 4px\"}}><strong>Complexity<\/strong> reflects how bespoke and integrated your legacy systems are. Low means off-the-shelf with simple interfaces. High means heavily customised systems with deep integrations and complex data models. Higher complexity increases both per-system costs (more expensive to run) and clinician time wasted (harder to navigate). <Md s=\"Designed to produce approximately +\/-30% variation around baseline, consistent with observed range across NHS trusts.\"\/><\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Data quality<\/strong> affects how long clinicians spend searching for information. Clean data means well-structured records with consistent coding. Poor data means duplicate records, missing fields, and inconsistent formats. Poor quality roughly doubles the effort of finding information compared to clean data. <Md s=\"Modelled estimate. Poor data approximately doubles effort vs clean data.\"\/><\/p>\n        <\/S>\n\n        <S title=\"9. How the three input modes work\">\n          <p style={{margin:\"0 0 8px\"}}>You can provide cost data at three levels of detail, depending on what you know about your legacy estate.<\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Estimate (default):<\/strong> The calculator uses the bed-scaled tier cost formulas to estimate your annual legacy spend. Best when you do not have contract data available. In benchmarking, this covered approximately 80% of a real Trust's actual estate value.<\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Named flagship systems:<\/strong> Add your highest-cost systems individually with their actual contract values (for example, a legacy PAS at {\"\\u00a3\"}3m\/yr). The remaining systems are still costed through the tier formulas. This captures outlier contracts that generic formulas underestimate.<\/p>\n          <p style={{margin:\"4px 0\"}}><strong>\"I know my spend\" mode:<\/strong> Enter your total annual legacy system spend. The calculator distributes it across tiers using a 5:2:1 weighting ratio (enterprise : departmental : standalone). You can then click any derived tier cost to fine-tune. In benchmarking, this mode produced results within 2.5% of the stated estimate.<\/p>\n          <p style={{margin:\"4px 0\"}}>In all modes, you can click any individual tier cost to override it with your actual figure.<\/p>\n        <\/S>\n\n        <S title=\"10. What is included and excluded\">\n          <p style={{margin:\"0 0 8px\"}}><strong>Included:<\/strong> Decommission savings (licence, hosting, support costs eliminated) and clinician time capacity value (hours freed, valued at blended rate with realisation discount).<\/p>\n          <p style={{margin:\"4px 0\"}}><strong>Excluded:<\/strong> Implementation costs (covered in main business case), parallel-run costs (too variable to model), data migration costs (one-off programme budget), clinical safety in financial terms (kept qualitative), Clinical Negligence Scheme for Trusts (CNST) premium reductions (mechanism real but not quantifiable per trust).<\/p>\n          <p style={{margin:\"4px 0\"}}>The 3-year total assumes flat annual savings. In practice, savings may increase in years 2-3 as more systems are retired, or decrease as easy wins are captured first.<\/p>\n        <\/S>\n\n        <div style={{marginTop:12,padding:\"10px 14px\",background:C.bg,borderRadius:8,fontSize:11,color:C.textMuted,lineHeight:1.6}}>\n          Tier cost formulas benchmarked against real-world NHS contract data from a large acute NHS Trust. All figures are user-overridable. For the complete audit trail with full citations, see the methodology report.\n        <\/div>\n      <\/Card>;\n    })()}\n\n    <button type=\"button\" onClick={()=>{setPage(\"inputs\");scrollToTop();}} className=\"roi-btn\" style={{marginTop:20,padding:\"12px 28px\",background:\"#fff\",color:C.textMid,border:`1px solid ${C.border}`,borderRadius:999,fontSize:14,fontWeight:600,cursor:\"pointer\"}}>{\"\u2190\"} Edit inputs<\/button>\n  <\/div>;\n\n  \/* \u2500\u2500 CALCULATING PAGE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n\n  const calcSteps = [\n    {label:\"Analysing your legacy estate\"},\n    {label:\"Modelling decommission savings\"},\n    {label:\"Estimating clinician time impact\"},\n  ];\n\n  const calculatingPage = <div style={{display:\"flex\",alignItems:\"center\",justifyContent:\"center\",minHeight:\"55vh\"}}>\n    <div style={{width:\"100%\",maxWidth:400,textAlign:\"center\"}}>\n      <div style={{width:44,height:44,margin:\"0 auto 20px\",border:`3px solid ${C.borderLight}`,borderTopColor:C.accent,borderRadius:\"50%\",animation:\"calc-spin .8s linear infinite\"}}\/>\n      <h2 style={{fontSize:20,fontWeight:700,color:C.text,marginBottom:4}} style={{fontSize:20,fontWeight:700,color:C.text,marginBottom:4}} id=\"calculating-your-roi\">\n\t\t\t\tCalculating your ROI<\/h2>\n      <p style={{fontSize:14,color:C.textMuted,marginBottom:28}}>Building a bespoke model from your inputs...<\/p>\n      <div style={{textAlign:\"left\",display:\"flex\",flexDirection:\"column\",gap:12}}>\n        {calcSteps.map((s,i)=>{\n          const done = calcStep > i, active = calcStep === i;\n          return <div key={i} style={{display:\"flex\",alignItems:\"center\",gap:12,opacity:done?1:active?0.8:0.2,transition:\"opacity .4s\",animation:active?\"calc-fade .35s ease\":\"none\"}}>\n            <div style={{width:30,height:30,borderRadius:\"50%\",background:done?C.green:active?C.accent:C.borderLight,display:\"flex\",alignItems:\"center\",justifyContent:\"center\",flexShrink:0}}>\n              {done?<span style={{color:\"#fff\",fontWeight:800,fontSize:12}}>{\"\u2713\"}<\/span>:<span style={{fontSize:12,fontWeight:700,color:\"#fff\"}}>{i+1}<\/span>}\n            <\/div>\n            <span style={{fontSize:14,fontWeight:done||active?600:400,color:done?C.green:active?C.text:C.textMuted}}>{s.label}<\/span>\n          <\/div>;\n        })}\n      <\/div>\n      <div style={{marginTop:24,height:3,background:C.borderLight,borderRadius:2,overflow:\"hidden\"}}>\n        <div style={{height:\"100%\",background:`linear-gradient(90deg,${C.accent},${C.green})`,borderRadius:2,width:`${Math.min(calcStep\/3*100,100)}%`,transition:\"width .5s ease-out\"}}\/>\n      <\/div>\n    <\/div>\n  <\/div>;\n\n  \/* \u2500\u2500 SHELL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 *\/\n\n  const fontHead = \"'FK Grotesk Neue','FK Grotesk','Inter','Helvetica Neue',Helvetica,sans-serif\";\n  const fontBody = \"'FK Grotesk Neue','FK Grotesk','Inter','Helvetica Neue',Helvetica,sans-serif\";\n\n  return <div style={{fontFamily:fontBody,background:C.bg,minHeight:\"100vh\",color:C.text,lineHeight:1.55,fontWeight:400,border:\"1px solid #f7faf9\",borderRadius:20,overflow:\"hidden\"}}>\n    <div style={{maxWidth:900,margin:\"0 auto\",padding:\"16px 24px 0\"}}><div style={{display:\"flex\",gap:4,marginBottom:20}}>\n      {[\n        { k: \"inputs\", l: \"Your programme\", n: \"1\" },\n        { k: \"results\", l: \"ROI estimate\", n: \"2\" },\n      ].map(p => {\n        const isCurrent = page === p.k || (page === \"calculating\" && p.k === \"results\");\n        return (\n          <button type=\"button\" key={p.k} className=\"roi-tab\"\n            onClick={() => page !== \"calculating\" && setPage(p.k)}\n            style={{\n              flex: 1, padding: \"11px 8px\", border: \"none\",\n              borderBottom: isCurrent ? `3px solid ${C.accent}` : \"3px solid transparent\",\n              background: isCurrent ? C.surface : \"transparent\",\n              cursor: page === \"calculating\" ? \"default\" : \"pointer\",\n              fontFamily: fontBody, fontSize: 14, fontWeight: isCurrent ? 700 : 500,\n              color: isCurrent ? C.accent : C.textMuted, borderRadius: \"8px 8px 0 0\",\n            }}>\n            <span style={{\n              display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\",\n              width: 22, height: 22, borderRadius: \"50%\",\n              background: isCurrent ? C.accent : C.border, color: \"#fff\",\n              fontSize: 11, fontWeight: 700, marginRight: 6,\n            }}>{p.n}<\/span>\n            {p.l}\n          <\/button>\n        );\n      })}\n    <\/div><\/div>\n    <div style={{maxWidth:900,margin:\"0 auto\",padding:\"0 16px 80px\"}}>{page===\"inputs\"?inputPage:page===\"calculating\"?calculatingPage:resultsPage}<\/div>\n  <\/div>;\n}\n\nReactDOM.createRoot(document.getElementById(\"root\")).render(<ROICalculator \/>);\n  <\/script>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":40,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-full-width.php","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"class_list":["post-10284","page","type-page","status-publish","hentry"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v25.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>ROI Calculator | RLDatix<\/title>\n<meta name=\"description\" content=\"ROI Calculator. Discover health &amp; care software solutions designed to support patient safety, compliance and operational efficiency.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"ROI Calculator | RLDatix\" \/>\n<meta property=\"og:description\" content=\"ROI Calculator. Discover health &amp; care software solutions designed to support patient safety, compliance and operational efficiency.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/\" \/>\n<meta property=\"og:site_name\" content=\"UK &amp; Ireland\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-07T13:52:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2025\/08\/OpenGraph-Image_UK-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@rldatixuki\" \/>\n<meta name=\"twitter:label1\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/\",\"url\":\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/\",\"name\":\"ROI Calculator | RLDatix\",\"isPartOf\":{\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#website\"},\"datePublished\":\"2026-03-19T15:26:43+00:00\",\"dateModified\":\"2026-04-07T13:52:14+00:00\",\"description\":\"ROI Calculator. Discover health & care software solutions designed to support patient safety, compliance and operational efficiency.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/\"]}],\"about\":{\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#organization\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"RLDatix \u2013 UK &amp; Ireland\",\"item\":\"https:\/\/www.rldatix.com\/en-uki\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"ROI Calculator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#website\",\"url\":\"https:\/\/www.rldatix.com\/en-uki\/\",\"name\":\"RLDatix \u2013 UK & Ireland\",\"description\":\"RLDatix, the real life decisions software solutions driving health in healthcare, helping organise, reduce harm, improve safety and strengthen patient and staff outcomes through systemwide decision making\",\"publisher\":{\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.rldatix.com\/en-uki\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-GB\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#organization\",\"name\":\"RLDatix \u2013 UK & Ireland\",\"url\":\"https:\/\/www.rldatix.com\/en-uki\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2024\/08\/logo-dark.svg\",\"contentUrl\":\"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2024\/08\/logo-dark.svg\",\"caption\":\"RLDatix \u2013 UK & Ireland\"},\"image\":{\"@id\":\"https:\/\/www.rldatix.com\/en-uki\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/rldatixuki\",\"https:\/\/www.linkedin.com\/company\/rldatix-uki\/\",\"https:\/\/www.youtube.com\/@TheConnectionRLDatix\"],\"areaServed\":\"Worldwide\",\"parentOrganization\":{\"@id\":\"https:\/\/www.rldatix.com\/#organization\"}},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.rldatix.com\/#organization\",\"name\":\"RLDatix\",\"url\":\"https:\/\/www.rldatix.com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"ROI Calculator | RLDatix","description":"ROI Calculator. Discover health & care software solutions designed to support patient safety, compliance and operational efficiency.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/","og_locale":"en_GB","og_type":"article","og_title":"ROI Calculator | RLDatix","og_description":"ROI Calculator. Discover health & care software solutions designed to support patient safety, compliance and operational efficiency.","og_url":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/","og_site_name":"UK &amp; Ireland","article_modified_time":"2026-04-07T13:52:14+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2025\/08\/OpenGraph-Image_UK-1.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_site":"@rldatixuki","twitter_misc":{"Estimated reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/","url":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/","name":"ROI Calculator | RLDatix","isPartOf":{"@id":"https:\/\/www.rldatix.com\/en-uki\/#website"},"datePublished":"2026-03-19T15:26:43+00:00","dateModified":"2026-04-07T13:52:14+00:00","description":"ROI Calculator. Discover health & care software solutions designed to support patient safety, compliance and operational efficiency.","breadcrumb":{"@id":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/"]}],"about":{"@id":"https:\/\/www.rldatix.com\/en-uki\/#organization"}},{"@type":"BreadcrumbList","@id":"https:\/\/www.rldatix.com\/en-uki\/legacy-data-roi-calculator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"RLDatix \u2013 UK &amp; Ireland","item":"https:\/\/www.rldatix.com\/en-uki\/"},{"@type":"ListItem","position":2,"name":"ROI Calculator"}]},{"@type":"WebSite","@id":"https:\/\/www.rldatix.com\/en-uki\/#website","url":"https:\/\/www.rldatix.com\/en-uki\/","name":"RLDatix \u2013 UK & Ireland","description":"RLDatix, the real life decisions software solutions driving health in healthcare, helping organise, reduce harm, improve safety and strengthen patient and staff outcomes through systemwide decision making","publisher":{"@id":"https:\/\/www.rldatix.com\/en-uki\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.rldatix.com\/en-uki\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-GB"},{"@type":"Organization","@id":"https:\/\/www.rldatix.com\/en-uki\/#organization","name":"RLDatix \u2013 UK & Ireland","url":"https:\/\/www.rldatix.com\/en-uki\/","logo":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/www.rldatix.com\/en-uki\/#\/schema\/logo\/image\/","url":"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2024\/08\/logo-dark.svg","contentUrl":"https:\/\/www.rldatix.com\/en-uki\/wp-content\/uploads\/sites\/6\/2024\/08\/logo-dark.svg","caption":"RLDatix \u2013 UK & Ireland"},"image":{"@id":"https:\/\/www.rldatix.com\/en-uki\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/rldatixuki","https:\/\/www.linkedin.com\/company\/rldatix-uki\/","https:\/\/www.youtube.com\/@TheConnectionRLDatix"],"areaServed":"Worldwide","parentOrganization":{"@id":"https:\/\/www.rldatix.com\/#organization"}},{"@type":"Organization","@id":"https:\/\/www.rldatix.com\/#organization","name":"RLDatix","url":"https:\/\/www.rldatix.com\/"}]}},"_links":{"self":[{"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/pages\/10284","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/users\/40"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/comments?post=10284"}],"version-history":[{"count":0,"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/pages\/10284\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.rldatix.com\/en-uki\/wp-json\/wp\/v2\/media?parent=10284"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}