From cbbc9ba839bc551eadf062894a5a5bd1c46f176e Mon Sep 17 00:00:00 2001 From: Robert Helewka Date: Mon, 13 Jul 2026 16:32:18 -0400 Subject: [PATCH] New notebooks: Scenario without current spend, VA scenario --- .../ctm_business_case_corrected.ipynb | 3607 +++-- .../ctm_business_case_no_current_state.ipynb | 11350 ++++++++++++++++ .../ctm_business_case_virtual_agents.ipynb | 3768 +++++ .../notebooks/ctm_migration_wfm.ipynb | 2708 ++-- .../notebooks/ctm_token_calculator.ipynb | 1556 +-- .../tests/test_appendix4.py | 19 +- .../tests/test_migration_wfm.py | 11 +- .../tokencalc/appendix4.py | 23 +- 8 files changed, 19078 insertions(+), 3964 deletions(-) create mode 100644 studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_no_current_state.ipynb create mode 100644 studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_virtual_agents.ipynb diff --git a/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_corrected.ipynb b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_corrected.ipynb index bcd7ea7..0372055 100644 --- a/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_corrected.ipynb +++ b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_corrected.ipynb @@ -36,10 +36,10 @@ "id": "26632e8d", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.208264Z", - "iopub.status.busy": "2026-07-08T15:15:51.207990Z", - "iopub.status.idle": "2026-07-08T15:15:51.611865Z", - "shell.execute_reply": "2026-07-08T15:15:51.611045Z" + "iopub.execute_input": "2026-07-13T19:23:46.109765Z", + "iopub.status.busy": "2026-07-13T19:23:46.109438Z", + "iopub.status.idle": "2026-07-13T19:23:46.715209Z", + "shell.execute_reply": "2026-07-13T19:23:46.713630Z" } }, "outputs": [ @@ -164,22 +164,22 @@ "id": "7b33a037", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.614204Z", - "iopub.status.busy": "2026-07-08T15:15:51.613921Z", - "iopub.status.idle": "2026-07-08T15:15:51.627002Z", - "shell.execute_reply": "2026-07-08T15:15:51.626023Z" + "iopub.execute_input": "2026-07-13T19:23:46.718132Z", + "iopub.status.busy": "2026-07-13T19:23:46.717597Z", + "iopub.status.idle": "2026-07-13T19:23:46.739041Z", + "shell.execute_reply": "2026-07-13T19:23:46.737212Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "4470b352a7e348919049702c575225c8", + "model_id": "f9605eccf55f4358908dcc4ce6fdf2a6", "position": "sidebar", "widget": "MarkdownWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "4470b352a7e348919049702c575225c8", + "model_id": "f9605eccf55f4358908dcc4ce6fdf2a6", "version_major": 2, "version_minor": 0 }, @@ -259,27 +259,27 @@ "id": "7e4e12ce", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.628930Z", - "iopub.status.busy": "2026-07-08T15:15:51.628743Z", - "iopub.status.idle": "2026-07-08T15:15:51.663654Z", - "shell.execute_reply": "2026-07-08T15:15:51.662978Z" + "iopub.execute_input": "2026-07-13T19:23:46.743283Z", + "iopub.status.busy": "2026-07-13T19:23:46.742963Z", + "iopub.status.idle": "2026-07-13T19:23:46.807784Z", + "shell.execute_reply": "2026-07-13T19:23:46.806961Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "91e44777e10b4dc982d4f35fb80fb2a1", + "model_id": "d69203bdb8584fc9975e475199fcc235", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "91e44777e10b4dc982d4f35fb80fb2a1", + "model_id": "d69203bdb8584fc9975e475199fcc235", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -288,17 +288,17 @@ { "data": { "application/mercury+json": { - "model_id": "c337c93de81943939d6e1b0e1c392359", + "model_id": "64a4f40d3f8a4633adc0cded5e2fb6c5", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "c337c93de81943939d6e1b0e1c392359", + "model_id": "64a4f40d3f8a4633adc0cded5e2fb6c5", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -307,17 +307,17 @@ { "data": { "application/mercury+json": { - "model_id": "6e62963d5c0c47a5b8e7d41daedbe004", + "model_id": "43454ebce3e44a238ef3a9b82836bdde", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "6e62963d5c0c47a5b8e7d41daedbe004", + "model_id": "43454ebce3e44a238ef3a9b82836bdde", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -326,17 +326,17 @@ { "data": { "application/mercury+json": { - "model_id": "b9727084f82b49089dfcacd62b9d72d5", + "model_id": "79ec042073404a6da4ec639b6b44a57c", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "b9727084f82b49089dfcacd62b9d72d5", + "model_id": "79ec042073404a6da4ec639b6b44a57c", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -345,17 +345,17 @@ { "data": { "application/mercury+json": { - "model_id": "feb40a4b31fd415aa44db460a40e1037", + "model_id": "72a50fd89db8497b97b625d05c3ed7f0", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "feb40a4b31fd415aa44db460a40e1037", + "model_id": "72a50fd89db8497b97b625d05c3ed7f0", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -364,17 +364,17 @@ { "data": { "application/mercury+json": { - "model_id": "5c32c561ba9c462286aa30e7efe3dd0c", + "model_id": "95256d5116704a59b90582870595a766", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "5c32c561ba9c462286aa30e7efe3dd0c", + "model_id": "95256d5116704a59b90582870595a766", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -383,17 +383,17 @@ { "data": { "application/mercury+json": { - "model_id": "8a4664e2944b48dd8c16ce8ea30481f4", + "model_id": "d55f5a00e77747b5a9142f96092253d2", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "8a4664e2944b48dd8c16ce8ea30481f4", + "model_id": "d55f5a00e77747b5a9142f96092253d2", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -402,17 +402,17 @@ { "data": { "application/mercury+json": { - "model_id": "7dc643d5b4e145b7b1eba08ff5ac668c", + "model_id": "408cdd33dace46aca1a41d1d0e75edc1", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "7dc643d5b4e145b7b1eba08ff5ac668c", + "model_id": "408cdd33dace46aca1a41d1d0e75edc1", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -421,17 +421,17 @@ { "data": { "application/mercury+json": { - "model_id": "50b0fca7bacc457f927fe77456336164", + "model_id": "5ccddaf9e94a4222afc3a08567ed9339", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "50b0fca7bacc457f927fe77456336164", + "model_id": "5ccddaf9e94a4222afc3a08567ed9339", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -440,17 +440,17 @@ { "data": { "application/mercury+json": { - "model_id": "e0626f2d55c14d7b802fb701e2074005", + "model_id": "08825d38562f490eace54ea96badd044", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "e0626f2d55c14d7b802fb701e2074005", + "model_id": "08825d38562f490eace54ea96badd044", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -501,10 +501,10 @@ "id": "52d1e89f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.665454Z", - "iopub.status.busy": "2026-07-08T15:15:51.665292Z", - "iopub.status.idle": "2026-07-08T15:15:51.682457Z", - "shell.execute_reply": "2026-07-08T15:15:51.681696Z" + "iopub.execute_input": "2026-07-13T19:23:46.810399Z", + "iopub.status.busy": "2026-07-13T19:23:46.810093Z", + "iopub.status.idle": "2026-07-13T19:23:46.841969Z", + "shell.execute_reply": "2026-07-13T19:23:46.841018Z" } }, "outputs": [ @@ -782,10 +782,10 @@ "id": "c7e86e80", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.684531Z", - "iopub.status.busy": "2026-07-08T15:15:51.684366Z", - "iopub.status.idle": "2026-07-08T15:15:51.714441Z", - "shell.execute_reply": "2026-07-08T15:15:51.713713Z" + "iopub.execute_input": "2026-07-13T19:23:46.845264Z", + "iopub.status.busy": "2026-07-13T19:23:46.845008Z", + "iopub.status.idle": "2026-07-13T19:23:46.895352Z", + "shell.execute_reply": "2026-07-13T19:23:46.894408Z" } }, "outputs": [ @@ -938,27 +938,27 @@ "id": "5ace8c50", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.716432Z", - "iopub.status.busy": "2026-07-08T15:15:51.716282Z", - "iopub.status.idle": "2026-07-08T15:15:51.721198Z", - "shell.execute_reply": "2026-07-08T15:15:51.720378Z" + "iopub.execute_input": "2026-07-13T19:23:46.899174Z", + "iopub.status.busy": "2026-07-13T19:23:46.898793Z", + "iopub.status.idle": "2026-07-13T19:23:46.906488Z", + "shell.execute_reply": "2026-07-13T19:23:46.905719Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "6b4df6310b224d668a46ff3767622396", + "model_id": "c2c1b1b71a354d27804630f9e5135ca2", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "6b4df6310b224d668a46ff3767622396", + "model_id": "c2c1b1b71a354d27804630f9e5135ca2", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -978,10 +978,10 @@ "id": "fe3572d2", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.722767Z", - "iopub.status.busy": "2026-07-08T15:15:51.722615Z", - "iopub.status.idle": "2026-07-08T15:15:51.734450Z", - "shell.execute_reply": "2026-07-08T15:15:51.733874Z" + "iopub.execute_input": "2026-07-13T19:23:46.909796Z", + "iopub.status.busy": "2026-07-13T19:23:46.909478Z", + "iopub.status.idle": "2026-07-13T19:23:46.931738Z", + "shell.execute_reply": "2026-07-13T19:23:46.929668Z" } }, "outputs": [ @@ -989,7 +989,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "NA Email exception: implemented Jan 2027, realizes Apr 2027\n" + "NA Email exception: implemented Oct 2026, realizes Jan 2027\n" ] }, { @@ -1037,6 +1037,17 @@ " \n", " \n", " NA\n", + " Oct 2026 (m10)\n", + " Jan 2027 (m13)\n", + " 3\n", + " 12\n", + " 12\n", + " 0\n", + " 12\n", + " 12\n", + " \n", + " \n", + " ANZ\n", " Jun 2027 (m18)\n", " Sep 2027 (m21)\n", " 0\n", @@ -1047,37 +1058,26 @@ " 12\n", " \n", " \n", - " ANZ\n", - " Sep 2027 (m21)\n", - " Dec 2027 (m24)\n", - " 0\n", - " 4\n", - " 12\n", - " 0\n", - " 1\n", - " 12\n", - " \n", - " \n", " EMEA\n", - " Dec 2027 (m24)\n", - " Mar 2028 (m27)\n", + " Feb 2027 (m14)\n", + " May 2027 (m17)\n", " 0\n", - " 1\n", + " 11\n", " 12\n", " 0\n", - " 0\n", - " 10\n", + " 8\n", + " 12\n", " \n", " \n", " ASIA\n", - " Mar 2028 (m27)\n", - " Jun 2028 (m30)\n", + " Oct 2027 (m22)\n", + " Jan 2028 (m25)\n", + " 0\n", + " 3\n", + " 12\n", " 0\n", " 0\n", - " 10\n", - " 0\n", - " 0\n", - " 7\n", + " 12\n", " \n", " \n", "\n", @@ -1086,24 +1086,24 @@ "text/plain": [ " implemented benefits realize token months 2026 token months 2027 \\\n", "region \n", - "NA Jun 2027 (m18) Sep 2027 (m21) 0 7 \n", - "ANZ Sep 2027 (m21) Dec 2027 (m24) 0 4 \n", - "EMEA Dec 2027 (m24) Mar 2028 (m27) 0 1 \n", - "ASIA Mar 2028 (m27) Jun 2028 (m30) 0 0 \n", + "NA Oct 2026 (m10) Jan 2027 (m13) 3 12 \n", + "ANZ Jun 2027 (m18) Sep 2027 (m21) 0 7 \n", + "EMEA Feb 2027 (m14) May 2027 (m17) 0 11 \n", + "ASIA Oct 2027 (m22) Jan 2028 (m25) 0 3 \n", "\n", " token months 2028 benefit months 2026 benefit months 2027 \\\n", "region \n", - "NA 12 0 4 \n", - "ANZ 12 0 1 \n", - "EMEA 12 0 0 \n", - "ASIA 10 0 0 \n", + "NA 12 0 12 \n", + "ANZ 12 0 4 \n", + "EMEA 12 0 8 \n", + "ASIA 12 0 0 \n", "\n", " benefit months 2028 \n", "region \n", "NA 12 \n", "ANZ 12 \n", - "EMEA 10 \n", - "ASIA 7 " + "EMEA 12 \n", + "ASIA 12 " ] }, "metadata": {}, @@ -1144,10 +1144,10 @@ "id": "a06e90b9", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.736296Z", - "iopub.status.busy": "2026-07-08T15:15:51.736137Z", - "iopub.status.idle": "2026-07-08T15:15:51.795636Z", - "shell.execute_reply": "2026-07-08T15:15:51.794556Z" + "iopub.execute_input": "2026-07-13T19:23:46.934297Z", + "iopub.status.busy": "2026-07-13T19:23:46.934021Z", + "iopub.status.idle": "2026-07-13T19:23:47.038038Z", + "shell.execute_reply": "2026-07-13T19:23:47.037206Z" } }, "outputs": [ @@ -1155,7 +1155,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "benefits by year: {2026: '$0K', 2027: '$2.6M', 2028: '$12.4M'}\n", + "benefits by year: {2026: '$0K', 2027: '$5.4M', 2028: '$9.7M'}\n", "⚠ 2026 benefits are $0 — the honest consequence of the deck's own deployment schedule.\n" ] }, @@ -1197,50 +1197,50 @@ " \n", " Agent Copilot\n", " 0\n", - " 1,150,000\n", - " 6,214,000\n", + " 2,700,600\n", + " 4,663,400\n", " 7,364,000\n", " \n", " \n", " WFM\n", " 0\n", - " 107,692\n", - " 2,893,308\n", + " 624,800\n", + " 2,376,200\n", " 3,001,000\n", " \n", " \n", " Email\n", " 0\n", - " 1,082,429\n", - " 1,888,571\n", + " 1,379,750\n", + " 1,591,250\n", " 2,971,000\n", " \n", " \n", " STA\n", " 0\n", - " 134,577\n", - " 679,423\n", + " 331,650\n", + " 482,350\n", " 814,000\n", " \n", " \n", " Predictive Routing\n", " 0\n", - " 64,981\n", - " 461,019\n", + " 161,400\n", + " 364,600\n", " 526,000\n", " \n", " \n", " Supervisor Copilot\n", " 0\n", - " 74,827\n", - " 292,173\n", + " 171,850\n", + " 195,150\n", " 367,000\n", " \n", " \n", " TOTAL\n", " 0\n", - " 2,614,505\n", - " 12,428,495\n", + " 5,370,050\n", + " 9,672,950\n", " 15,043,000\n", " \n", " \n", @@ -1248,15 +1248,15 @@ "" ], "text/plain": [ - "year 2026 2027 2028 TOTAL\n", - "capability \n", - "Agent Copilot 0 1,150,000 6,214,000 7,364,000\n", - "WFM 0 107,692 2,893,308 3,001,000\n", - "Email 0 1,082,429 1,888,571 2,971,000\n", - "STA 0 134,577 679,423 814,000\n", - "Predictive Routing 0 64,981 461,019 526,000\n", - "Supervisor Copilot 0 74,827 292,173 367,000\n", - "TOTAL 0 2,614,505 12,428,495 15,043,000" + "year 2026 2027 2028 TOTAL\n", + "capability \n", + "Agent Copilot 0 2,700,600 4,663,400 7,364,000\n", + "WFM 0 624,800 2,376,200 3,001,000\n", + "Email 0 1,379,750 1,591,250 2,971,000\n", + "STA 0 331,650 482,350 814,000\n", + "Predictive Routing 0 161,400 364,600 526,000\n", + "Supervisor Copilot 0 171,850 195,150 367,000\n", + "TOTAL 0 5,370,050 9,672,950 15,043,000" ] }, "metadata": {}, @@ -1313,27 +1313,27 @@ "id": "977418e1", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.798201Z", - "iopub.status.busy": "2026-07-08T15:15:51.798054Z", - "iopub.status.idle": "2026-07-08T15:15:51.812657Z", - "shell.execute_reply": "2026-07-08T15:15:51.811957Z" + "iopub.execute_input": "2026-07-13T19:23:47.042202Z", + "iopub.status.busy": "2026-07-13T19:23:47.041840Z", + "iopub.status.idle": "2026-07-13T19:23:47.067063Z", + "shell.execute_reply": "2026-07-13T19:23:47.066160Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "6347323104f645beac02bd1592fb60d4", + "model_id": "f6ffc6bbb4a04db8b59c069718cbf704", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "6347323104f645beac02bd1592fb60d4", + "model_id": "f6ffc6bbb4a04db8b59c069718cbf704", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1342,17 +1342,17 @@ { "data": { "application/mercury+json": { - "model_id": "e0699dfd28c84c71a9e7f8a1ae48add5", + "model_id": "5907e927e40b4fd7b59484d0c541091c", "position": "sidebar", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "e0699dfd28c84c71a9e7f8a1ae48add5", + "model_id": "5907e927e40b4fd7b59484d0c541091c", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1361,17 +1361,17 @@ { "data": { "application/mercury+json": { - "model_id": "d9877763b4a94831b94c3d083f818d58", + "model_id": "fdde38396a7d447eb57f94bc198f8e2f", "position": "sidebar", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "d9877763b4a94831b94c3d083f818d58", + "model_id": "fdde38396a7d447eb57f94bc198f8e2f", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1380,17 +1380,17 @@ { "data": { "application/mercury+json": { - "model_id": "0f0c0863eade46b2a23d4f7efe58c173", + "model_id": "8c0f6f8f207c4f7b90aa037b58790d4c", "position": "sidebar", "widget": "SliderWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "0f0c0863eade46b2a23d4f7efe58c173", + "model_id": "8c0f6f8f207c4f7b90aa037b58790d4c", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1399,17 +1399,17 @@ { "data": { "application/mercury+json": { - "model_id": "555759d10b104fbf9ac24b8e9963baa8", + "model_id": "d7d4d74246ea490da7610c277c802b99", "position": "sidebar", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "555759d10b104fbf9ac24b8e9963baa8", + "model_id": "d7d4d74246ea490da7610c277c802b99", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -1447,10 +1447,10 @@ "id": "9767177f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.814858Z", - "iopub.status.busy": "2026-07-08T15:15:51.814724Z", - "iopub.status.idle": "2026-07-08T15:15:51.825579Z", - "shell.execute_reply": "2026-07-08T15:15:51.824675Z" + "iopub.execute_input": "2026-07-13T19:23:47.069635Z", + "iopub.status.busy": "2026-07-13T19:23:47.069338Z", + "iopub.status.idle": "2026-07-13T19:23:47.087605Z", + "shell.execute_reply": "2026-07-13T19:23:47.086414Z" } }, "outputs": [ @@ -1600,10 +1600,10 @@ "id": "3f3102f4", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.829201Z", - "iopub.status.busy": "2026-07-08T15:15:51.828968Z", - "iopub.status.idle": "2026-07-08T15:15:51.865340Z", - "shell.execute_reply": "2026-07-08T15:15:51.864704Z" + "iopub.execute_input": "2026-07-13T19:23:47.090705Z", + "iopub.status.busy": "2026-07-13T19:23:47.090415Z", + "iopub.status.idle": "2026-07-13T19:23:47.142308Z", + "shell.execute_reply": "2026-07-13T19:23:47.141252Z" } }, "outputs": [ @@ -1611,7 +1611,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "AI token consumption (missing cost #1): {2026: '$0K', 2027: '$1.2M', 2028: '$3.3M'} → 3-yr $4.5M\n" + "AI token consumption (missing cost #1): {2026: '$424K', 2027: '$2.7M', 2028: '$3.5M'} → 3-yr $6.6M\n" ] }, { @@ -1654,42 +1654,42 @@ " \n", " Predictive Routing\n", " 🟢\n", - " 0\n", - " 583,561\n", - " 1,764,870\n", - " 2,348,431\n", + " 214,299\n", + " 1,362,323\n", + " 1,851,816\n", + " 3,428,438\n", " \n", " \n", " Agent Copilot [named]\n", " 🟢\n", - " 0\n", - " 311,000\n", + " 114,000\n", + " 662,400\n", " 715,200\n", - " 1,026,200\n", + " 1,491,600\n", " \n", " \n", " Speech & Text Analytics [named]\n", " 🟢\n", - " 0\n", - " 233,250\n", - " 715,800\n", - " 949,050\n", + " 85,500\n", + " 550,620\n", + " 751,680\n", + " 1,387,800\n", " \n", " \n", " Email AI (Auto-Respond)\n", " 🟡\n", - " 0\n", - " 46,272\n", - " 87,136\n", - " 133,408\n", + " 10,551\n", + " 67,080\n", + " 91,500\n", + " 169,131\n", " \n", " \n", " AI Translate\n", " 🟢\n", " 0\n", - " 2,100\n", - " 62,150\n", - " 64,250\n", + " 34,185\n", + " 69,540\n", + " 103,725\n", " \n", " \n", " AI Summary & Insights\n", @@ -1710,37 +1710,37 @@ " \n", " TOTAL\n", " \n", - " 0\n", - " 1,176,183\n", - " 3,345,156\n", - " 4,521,339\n", + " 424,350\n", + " 2,676,608\n", + " 3,479,736\n", + " 6,580,694\n", " \n", " \n", "\n", "" ], "text/plain": [ - "year conf 2026 2027 2028 \\\n", - "cost_line \n", - "Predictive Routing 🟢 0 583,561 1,764,870 \n", - "Agent Copilot [named] 🟢 0 311,000 715,200 \n", - "Speech & Text Analytics [named] 🟢 0 233,250 715,800 \n", - "Email AI (Auto-Respond) 🟡 0 46,272 87,136 \n", - "AI Translate 🟢 0 2,100 62,150 \n", - "AI Summary & Insights 🟢 0 0 0 \n", - "WFM (no token meter — licence-included) 🟢 0 0 0 \n", - "TOTAL 0 1,176,183 3,345,156 \n", + "year conf 2026 2027 2028 \\\n", + "cost_line \n", + "Predictive Routing 🟢 214,299 1,362,323 1,851,816 \n", + "Agent Copilot [named] 🟢 114,000 662,400 715,200 \n", + "Speech & Text Analytics [named] 🟢 85,500 550,620 751,680 \n", + "Email AI (Auto-Respond) 🟡 10,551 67,080 91,500 \n", + "AI Translate 🟢 0 34,185 69,540 \n", + "AI Summary & Insights 🟢 0 0 0 \n", + "WFM (no token meter — licence-included) 🟢 0 0 0 \n", + "TOTAL 424,350 2,676,608 3,479,736 \n", "\n", "year 3-yr \n", "cost_line \n", - "Predictive Routing 2,348,431 \n", - "Agent Copilot [named] 1,026,200 \n", - "Speech & Text Analytics [named] 949,050 \n", - "Email AI (Auto-Respond) 133,408 \n", - "AI Translate 64,250 \n", + "Predictive Routing 3,428,438 \n", + "Agent Copilot [named] 1,491,600 \n", + "Speech & Text Analytics [named] 1,387,800 \n", + "Email AI (Auto-Respond) 169,131 \n", + "AI Translate 103,725 \n", "AI Summary & Insights 0 \n", "WFM (no token meter — licence-included) 0 \n", - "TOTAL 4,521,339 " + "TOTAL 6,580,694 " ] }, "metadata": {}, @@ -1775,10 +1775,10 @@ "id": "e62ecfa7", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:51.869407Z", - "iopub.status.busy": "2026-07-08T15:15:51.869265Z", - "iopub.status.idle": "2026-07-08T15:15:52.949655Z", - "shell.execute_reply": "2026-07-08T15:15:52.949011Z" + "iopub.execute_input": "2026-07-13T19:23:47.145095Z", + "iopub.status.busy": "2026-07-13T19:23:47.144761Z", + "iopub.status.idle": "2026-07-13T19:23:48.427222Z", + "shell.execute_reply": "2026-07-13T19:23:48.425202Z" } }, "outputs": [ @@ -1806,8 +1806,8 @@ "2028" ], "y": [ - 0.0, - 311000.0, + 114000.0, + 662400.0, 715200.0 ] }, @@ -1828,9 +1828,9 @@ "2028" ], "y": [ - 0.0, - 233250.0, - 715800.0 + 85500.0, + 550620.0, + 751680.0 ] }, { @@ -1850,9 +1850,9 @@ "2028" ], "y": [ - 0.0, - 583561.0, - 1764870.0 + 214299.0, + 1362323.0, + 1851816.0 ] }, { @@ -1872,9 +1872,9 @@ "2028" ], "y": [ - 0.0, - 46272.0, - 87136.0 + 10551.0, + 67080.0, + 91500.0 ] }, { @@ -1895,8 +1895,8 @@ ], "y": [ 0.0, - 2100.0, - 62150.0 + 34185.0, + 69540.0 ] } ], @@ -1908,9 +1908,9 @@ "size": 12 }, "showarrow": false, - "text": "$0K", + "text": "$424K", "x": 0, - "y": 0.0, + "y": 424350.0, "yshift": 12 }, { @@ -1919,9 +1919,9 @@ "size": 12 }, "showarrow": false, - "text": "$1.2M", + "text": "$2.7M", "x": 1, - "y": 1176183.0, + "y": 2676608.0, "yshift": 12 }, { @@ -1930,9 +1930,9 @@ "size": 12 }, "showarrow": false, - "text": "$3.3M", + "text": "$3.5M", "x": 2, - "y": 3345156.0, + "y": 3479736.0, "yshift": 12 } ], @@ -2828,27 +2828,27 @@ "id": "3b395fbd", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:52.953299Z", - "iopub.status.busy": "2026-07-08T15:15:52.953136Z", - "iopub.status.idle": "2026-07-08T15:15:52.963220Z", - "shell.execute_reply": "2026-07-08T15:15:52.962612Z" + "iopub.execute_input": "2026-07-13T19:23:48.432232Z", + "iopub.status.busy": "2026-07-13T19:23:48.431510Z", + "iopub.status.idle": "2026-07-13T19:23:48.449517Z", + "shell.execute_reply": "2026-07-13T19:23:48.448763Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "3414451f3bea4ecd86b616edc1bcf92f", + "model_id": "db128adf84c844139dcfffac697fd6ed", "position": "sidebar", "widget": "SelectWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "3414451f3bea4ecd86b616edc1bcf92f", + "model_id": "db128adf84c844139dcfffac697fd6ed", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -2857,17 +2857,17 @@ { "data": { "application/mercury+json": { - "model_id": "d57babc80cd04cf3bbb6b5f9749db410", + "model_id": "3f822353156447048ac80bbeab4fc036", "position": "sidebar", "widget": "SelectWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "d57babc80cd04cf3bbb6b5f9749db410", + "model_id": "3f822353156447048ac80bbeab4fc036", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -2876,17 +2876,17 @@ { "data": { "application/mercury+json": { - "model_id": "6c099f4dfe1c419984d542e66c67150f", + "model_id": "a55328df761a4c889bf2f82b4e986153", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "6c099f4dfe1c419984d542e66c67150f", + "model_id": "a55328df761a4c889bf2f82b4e986153", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -2913,10 +2913,10 @@ "id": "23756537", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:52.964968Z", - "iopub.status.busy": "2026-07-08T15:15:52.964759Z", - "iopub.status.idle": "2026-07-08T15:15:52.990788Z", - "shell.execute_reply": "2026-07-08T15:15:52.990167Z" + "iopub.execute_input": "2026-07-13T19:23:48.451745Z", + "iopub.status.busy": "2026-07-13T19:23:48.451550Z", + "iopub.status.idle": "2026-07-13T19:23:48.474679Z", + "shell.execute_reply": "2026-07-13T19:23:48.474057Z" } }, "outputs": [ @@ -3081,56 +3081,56 @@ " Agent Copilot\n", " 1,500\n", " 337,500\n", - " 207,888\n", - " 129,612\n", + " 311,832\n", + " 25,668\n", " 0\n", " \n", " \n", " Cross-cutting (PM, governance, testing, integration)\n", " 1,400\n", " 315,000\n", - " 178,711\n", - " 126,366\n", - " 9,923\n", + " 257,242\n", + " 57,758\n", + " 0\n", " \n", " \n", " Email Auto-Respond\n", " 1,100\n", " 247,500\n", - " 169,530\n", - " 70,174\n", - " 7,796\n", + " 202,119\n", + " 45,381\n", + " 0\n", " \n", " \n", " KB readiness (prerequisite)\n", " 1,000\n", " 225,000\n", - " 127,651\n", - " 90,261\n", - " 7,088\n", + " 183,744\n", + " 41,256\n", + " 0\n", " \n", " \n", " Predictive Routing\n", " 550\n", " 123,750\n", - " 70,208\n", - " 49,644\n", - " 3,898\n", + " 101,059\n", + " 22,691\n", + " 0\n", " \n", " \n", " STA\n", " 1,000\n", " 225,000\n", - " 127,651\n", - " 90,261\n", - " 7,088\n", + " 183,744\n", + " 41,256\n", + " 0\n", " \n", " \n", " Supervisor Copilot\n", " 300\n", " 67,500\n", - " 41,578\n", - " 25,922\n", + " 62,366\n", + " 5,134\n", " 0\n", " \n", " \n", @@ -3145,38 +3145,38 @@ " TOTAL\n", " 8,250\n", " 1,856,250\n", - " 923,217\n", - " 739,741\n", - " 193,293\n", + " 1,302,108\n", + " 396,642\n", + " 157,500\n", " \n", " \n", "\n", "" ], "text/plain": [ - " hours cost 2026 \\\n", - "workstream \n", - "Agent Copilot 1,500 337,500 207,888 \n", - "Cross-cutting (PM, governance, testing, integra... 1,400 315,000 178,711 \n", - "Email Auto-Respond 1,100 247,500 169,530 \n", - "KB readiness (prerequisite) 1,000 225,000 127,651 \n", - "Predictive Routing 550 123,750 70,208 \n", - "STA 1,000 225,000 127,651 \n", - "Supervisor Copilot 300 67,500 41,578 \n", - "Steady-state tuning (2027-28) 1,400 315,000 0 \n", - "TOTAL 8,250 1,856,250 923,217 \n", + " hours cost 2026 \\\n", + "workstream \n", + "Agent Copilot 1,500 337,500 311,832 \n", + "Cross-cutting (PM, governance, testing, integra... 1,400 315,000 257,242 \n", + "Email Auto-Respond 1,100 247,500 202,119 \n", + "KB readiness (prerequisite) 1,000 225,000 183,744 \n", + "Predictive Routing 550 123,750 101,059 \n", + "STA 1,000 225,000 183,744 \n", + "Supervisor Copilot 300 67,500 62,366 \n", + "Steady-state tuning (2027-28) 1,400 315,000 0 \n", + "TOTAL 8,250 1,856,250 1,302,108 \n", "\n", " 2027 2028 \n", "workstream \n", - "Agent Copilot 129,612 0 \n", - "Cross-cutting (PM, governance, testing, integra... 126,366 9,923 \n", - "Email Auto-Respond 70,174 7,796 \n", - "KB readiness (prerequisite) 90,261 7,088 \n", - "Predictive Routing 49,644 3,898 \n", - "STA 90,261 7,088 \n", - "Supervisor Copilot 25,922 0 \n", + "Agent Copilot 25,668 0 \n", + "Cross-cutting (PM, governance, testing, integra... 57,758 0 \n", + "Email Auto-Respond 45,381 0 \n", + "KB readiness (prerequisite) 41,256 0 \n", + "Predictive Routing 22,691 0 \n", + "STA 41,256 0 \n", + "Supervisor Copilot 5,134 0 \n", "Steady-state tuning (2027-28) 157,500 157,500 \n", - "TOTAL 739,741 193,293 " + "TOTAL 396,642 157,500 " ] }, "metadata": {}, @@ -3220,10 +3220,10 @@ "id": "1ffaa74a", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:52.992940Z", - "iopub.status.busy": "2026-07-08T15:15:52.992787Z", - "iopub.status.idle": "2026-07-08T15:15:52.996336Z", - "shell.execute_reply": "2026-07-08T15:15:52.995761Z" + "iopub.execute_input": "2026-07-13T19:23:48.476746Z", + "iopub.status.busy": "2026-07-13T19:23:48.476573Z", + "iopub.status.idle": "2026-07-13T19:23:48.480630Z", + "shell.execute_reply": "2026-07-13T19:23:48.479902Z" } }, "outputs": [ @@ -3276,10 +3276,10 @@ "id": "ad880bda", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:52.998103Z", - "iopub.status.busy": "2026-07-08T15:15:52.997970Z", - "iopub.status.idle": "2026-07-08T15:15:53.151333Z", - "shell.execute_reply": "2026-07-08T15:15:53.150190Z" + "iopub.execute_input": "2026-07-13T19:23:48.482624Z", + "iopub.status.busy": "2026-07-13T19:23:48.482431Z", + "iopub.status.idle": "2026-07-13T19:23:48.707467Z", + "shell.execute_reply": "2026-07-13T19:23:48.706201Z" } }, "outputs": [ @@ -3287,7 +3287,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Corrected programme cost: {2026: '$11.1M', 2027: '$13.8M', 2028: '$7.1M'} → 3-yr $32.1M\n", + "Corrected programme cost: {2026: '$11.9M', 2027: '$15.0M', 2028: '$7.2M'} → 3-yr $34.1M\n", "As pitched (deck): {2026: '$6.9M', 2027: '$4.3M', 2028: '$4.3M'} → 3-yr $15.5M (deck says $15.4M — rounding)\n" ] }, @@ -3335,10 +3335,10 @@ " \n", " \n", " NTT managed services (from MCX go-live)\n", - " 102,730\n", + " 68,486\n", " 410,918\n", " 410,918\n", - " 924,566\n", + " 890,323\n", " \n", " \n", " Existing platform (term-contract run-off)\n", @@ -3349,16 +3349,16 @@ " \n", " \n", " AI token consumption\n", - " 0\n", - " 1,176,183\n", - " 3,345,156\n", - " 4,521,339\n", + " 424,350\n", + " 2,676,608\n", + " 3,479,736\n", + " 6,580,694\n", " \n", " \n", " AI implementation + KB readiness\n", - " 923,217\n", - " 582,241\n", - " 35,793\n", + " 1,302,108\n", + " 239,142\n", + " 0\n", " 1,541,250\n", " \n", " \n", @@ -3370,10 +3370,10 @@ " \n", " \n", " TOTAL — corrected\n", - " 11,105,671\n", - " 13,839,566\n", - " 7,149,367\n", - " 32,094,604\n", + " 11,874,669\n", + " 14,996,893\n", + " 7,248,154\n", + " 34,119,716\n", " \n", " \n", "\n", @@ -3383,22 +3383,22 @@ " 2026 2027 \\\n", "CCaaS platform licences (contracted, ramp-adjus... 1,600,000 3,200,000 \n", "NTT PS + training (SOW billing milestones) 1,179,723 1,012,723 \n", - "NTT managed services (from MCX go-live) 102,730 410,918 \n", + "NTT managed services (from MCX go-live) 68,486 410,918 \n", "Existing platform (term-contract run-off) 7,300,001 7,300,001 \n", - "AI token consumption 0 1,176,183 \n", - "AI implementation + KB readiness 923,217 582,241 \n", + "AI token consumption 424,350 2,676,608 \n", + "AI implementation + KB readiness 1,302,108 239,142 \n", "AI steady-state tuning 0 157,500 \n", - "TOTAL — corrected 11,105,671 13,839,566 \n", + "TOTAL — corrected 11,874,669 14,996,893 \n", "\n", " 2028 3-yr \n", "CCaaS platform licences (contracted, ramp-adjus... 3,200,000 8,000,000 \n", "NTT PS + training (SOW billing milestones) 0 2,192,446 \n", - "NTT managed services (from MCX go-live) 410,918 924,566 \n", + "NTT managed services (from MCX go-live) 410,918 890,323 \n", "Existing platform (term-contract run-off) 0 14,600,002 \n", - "AI token consumption 3,345,156 4,521,339 \n", - "AI implementation + KB readiness 35,793 1,541,250 \n", + "AI token consumption 3,479,736 6,580,694 \n", + "AI implementation + KB readiness 0 1,541,250 \n", "AI steady-state tuning 157,500 315,000 \n", - "TOTAL — corrected 7,149,367 32,094,604 " + "TOTAL — corrected 7,248,154 34,119,716 " ] }, "metadata": {}, @@ -3512,20 +3512,20 @@ " $607,633.94\n", " \n", " \n", - " Start of client UAT (first region)\n", - " 2026-09-30\n", + " Start of client UAT (first region, NA)\n", + " 2026-10-16\n", " 20%\n", " $405,089.30\n", " \n", " \n", - " Start of client UAT (last region)\n", - " 2027-06-30\n", + " Start of client UAT (last region, APAC)\n", + " 2027-10-01\n", " 30%\n", " $607,633.94\n", " \n", " \n", " Completion of last go-live migration\n", - " 2027-09-30\n", + " 2027-11-01\n", " 20%\n", " $405,089.30\n", " \n", @@ -3534,12 +3534,12 @@ "" ], "text/plain": [ - " date share amount\n", - "SOW billing milestone \n", - "SOW Effective Date 2026-03-15 30% $607,633.94\n", - "Start of client UAT (first region) 2026-09-30 20% $405,089.30\n", - "Start of client UAT (last region) 2027-06-30 30% $607,633.94\n", - "Completion of last go-live migration 2027-09-30 20% $405,089.30" + " date share amount\n", + "SOW billing milestone \n", + "SOW Effective Date 2026-03-15 30% $607,633.94\n", + "Start of client UAT (first region, NA) 2026-10-16 20% $405,089.30\n", + "Start of client UAT (last region, APAC) 2027-10-01 30% $607,633.94\n", + "Completion of last go-live migration 2027-11-01 20% $405,089.30" ] }, "metadata": {}, @@ -3604,10 +3604,10 @@ "id": "2e2b207c", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.153864Z", - "iopub.status.busy": "2026-07-08T15:15:53.153616Z", - "iopub.status.idle": "2026-07-08T15:15:53.229459Z", - "shell.execute_reply": "2026-07-08T15:15:53.228727Z" + "iopub.execute_input": "2026-07-13T19:23:48.710188Z", + "iopub.status.busy": "2026-07-13T19:23:48.709787Z", + "iopub.status.idle": "2026-07-13T19:23:48.818084Z", + "shell.execute_reply": "2026-07-13T19:23:48.817047Z" } }, "outputs": [ @@ -3636,8 +3636,8 @@ ], "y": [ 0.0, - 1150000.0, - 6214000.0 + 2700600.0, + 4663400.0 ] }, { @@ -3658,8 +3658,8 @@ ], "y": [ 0.0, - 107692.30769230769, - 2893307.692307692 + 624800.0, + 2376200.0 ] }, { @@ -3680,8 +3680,8 @@ ], "y": [ 0.0, - 1082428.5714285714, - 1888571.4285714286 + 1379750.0, + 1591250.0 ] }, { @@ -3702,8 +3702,8 @@ ], "y": [ 0.0, - 134576.92307692306, - 679423.076923077 + 331650.0, + 482350.0 ] }, { @@ -3724,8 +3724,8 @@ ], "y": [ 0.0, - 64980.769230769234, - 461019.23076923075 + 161400.0, + 364600.0 ] }, { @@ -3746,8 +3746,8 @@ ], "y": [ 0.0, - 74826.92307692308, - 292173.07692307694 + 171850.0, + 195150.0 ] }, { @@ -3772,7 +3772,7 @@ "2028" ], "y": { - "bdata": "AAAAAAAAAAC/9Eu/dPJDQQAAAAA3sWxB", + "bdata": "AAAAAAAAAAAAAACAMHxUQQAAAAA3sWxB", "dtype": "f8" } } @@ -3796,9 +3796,9 @@ "size": 12 }, "showarrow": false, - "text": "$2.6M", + "text": "$5.4M", "x": 1, - "y": 2614505.4945054944, + "y": 5370050.0, "yshift": 12 }, { @@ -3807,9 +3807,9 @@ "size": 12 }, "showarrow": false, - "text": "$12.4M", + "text": "$9.7M", "x": 2, - "y": 12428494.505494505, + "y": 9672950.0, "yshift": 12 }, { @@ -4703,10 +4703,10 @@ "id": "c2c9418f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.232459Z", - "iopub.status.busy": "2026-07-08T15:15:53.232280Z", - "iopub.status.idle": "2026-07-08T15:15:53.262550Z", - "shell.execute_reply": "2026-07-08T15:15:53.261738Z" + "iopub.execute_input": "2026-07-13T19:23:48.821801Z", + "iopub.status.busy": "2026-07-13T19:23:48.821433Z", + "iopub.status.idle": "2026-07-13T19:23:48.854848Z", + "shell.execute_reply": "2026-07-13T19:23:48.853928Z" } }, "outputs": [ @@ -4778,7 +4778,7 @@ "2028" ], "y": [ - 102729.60000000002, + 68486.40000000001, 410918.4000000001, 410918.4000000001 ] @@ -4822,9 +4822,9 @@ "2028" ], "y": [ - 0.0, - 1176183.0, - 3345156.0 + 424350.0, + 2676608.0, + 3479736.0 ] }, { @@ -4844,9 +4844,9 @@ "2028" ], "y": [ - 923216.703288553, - 582240.770938251, - 35792.52577319588 + 1302107.96532141, + 239142.03467859002, + 0.0 ] }, { @@ -4893,7 +4893,7 @@ "2028" ], "y": { - "bdata": "rZ5i0bAuZUFUg0RPJcp3QeJ6FL6Ym35B", + "bdata": "Asteky2mZkFI4XqUcKB5QdejcB0BRYBB", "dtype": "f8" } }, @@ -4933,9 +4933,9 @@ "size": 12 }, "showarrow": false, - "text": "$11.1M", + "text": "$11.9M", "x": 0, - "y": 11105670.543288553, + "y": 11874668.605321411, "yshift": 12 }, { @@ -4944,9 +4944,9 @@ "size": 12 }, "showarrow": false, - "text": "$13.8M", + "text": "$15.0M", "x": 1, - "y": 13839566.410938252, + "y": 14996892.67467859, "yshift": 12 }, { @@ -4955,9 +4955,9 @@ "size": 12 }, "showarrow": false, - "text": "$7.1M", + "text": "$7.2M", "x": 2, - "y": 7149366.925773196, + "y": 7248154.4, "yshift": 12 }, { @@ -4966,10 +4966,10 @@ "size": 12 }, "showarrow": false, - "text": "3-yr $32.1M — $16.6M above the pitch", + "text": "3-yr $34.1M — $18.7M above the pitch", "x": 2, "xshift": -70, - "y": 32094603.880000003, + "y": 34119715.68, "yshift": 18 } ], @@ -5840,10 +5840,10 @@ "id": "82335f50", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.264913Z", - "iopub.status.busy": "2026-07-08T15:15:53.264653Z", - "iopub.status.idle": "2026-07-08T15:15:53.301134Z", - "shell.execute_reply": "2026-07-08T15:15:53.300332Z" + "iopub.execute_input": "2026-07-13T19:23:48.857922Z", + "iopub.status.busy": "2026-07-13T19:23:48.857709Z", + "iopub.status.idle": "2026-07-13T19:23:48.883942Z", + "shell.execute_reply": "2026-07-13T19:23:48.882828Z" } }, "outputs": [ @@ -5893,9 +5893,9 @@ "2028" ], "y": [ - 11105670.543288553, - 13839566.410938252, - 7149366.925773196 + 11874668.605321411, + 14996892.67467859, + 7248154.4 ] } ], @@ -5907,10 +5907,10 @@ "size": 12 }, "showarrow": false, - "text": "+$4.2M", + "text": "+$5.0M", "x": 0, "xshift": 16, - "y": 11105670.543288553, + "y": 11874668.605321411, "yshift": 12 }, { @@ -5919,10 +5919,10 @@ "size": 12 }, "showarrow": false, - "text": "+$9.5M", + "text": "+$10.7M", "x": 1, "xshift": 16, - "y": 13839566.410938252, + "y": 14996892.67467859, "yshift": 12 }, { @@ -5931,10 +5931,10 @@ "size": 12 }, "showarrow": false, - "text": "+$2.8M", + "text": "+$2.9M", "x": 2, "xshift": 16, - "y": 7149366.925773196, + "y": 7248154.4, "yshift": 12 } ], @@ -6814,27 +6814,27 @@ "id": "de7679d8", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.303488Z", - "iopub.status.busy": "2026-07-08T15:15:53.303261Z", - "iopub.status.idle": "2026-07-08T15:15:53.309409Z", - "shell.execute_reply": "2026-07-08T15:15:53.308801Z" + "iopub.execute_input": "2026-07-13T19:23:48.886969Z", + "iopub.status.busy": "2026-07-13T19:23:48.886658Z", + "iopub.status.idle": "2026-07-13T19:23:48.893646Z", + "shell.execute_reply": "2026-07-13T19:23:48.893004Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "9c731e39482e40308709c89e44302200", + "model_id": "b83db5f7d9154255ae5e8db2dc7f112a", "position": "sidebar", "widget": "SelectWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "9c731e39482e40308709c89e44302200", + "model_id": "b83db5f7d9154255ae5e8db2dc7f112a", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -6855,10 +6855,10 @@ "id": "08433116", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.311159Z", - "iopub.status.busy": "2026-07-08T15:15:53.310953Z", - "iopub.status.idle": "2026-07-08T15:15:53.316419Z", - "shell.execute_reply": "2026-07-08T15:15:53.315807Z" + "iopub.execute_input": "2026-07-13T19:23:48.900993Z", + "iopub.status.busy": "2026-07-13T19:23:48.900649Z", + "iopub.status.idle": "2026-07-13T19:23:48.906965Z", + "shell.execute_reply": "2026-07-13T19:23:48.905996Z" } }, "outputs": [ @@ -6866,8 +6866,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "corrected: net by year {2026: '-$3.8M', 2027: '-$3.9M', 2028: '$12.6M'}\n", - "pitched: net by year {2026: '$433K', 2027: '$5.6M', 2028: '$15.4M'}\n" + "corrected: net by year {2026: '-$4.6M', 2027: '-$2.3M', 2028: '$9.7M'}\n", + "pitched: net by year {2026: '$433K', 2027: '$8.4M', 2028: '$12.7M'}\n" ] } ], @@ -6896,10 +6896,10 @@ "id": "04c43658", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.318410Z", - "iopub.status.busy": "2026-07-08T15:15:53.318221Z", - "iopub.status.idle": "2026-07-08T15:15:53.342497Z", - "shell.execute_reply": "2026-07-08T15:15:53.341878Z" + "iopub.execute_input": "2026-07-13T19:23:48.909311Z", + "iopub.status.busy": "2026-07-13T19:23:48.909017Z", + "iopub.status.idle": "2026-07-13T19:23:48.946877Z", + "shell.execute_reply": "2026-07-13T19:23:48.945881Z" } }, "outputs": [ @@ -6928,8 +6928,8 @@ ], "y": [ 0.0, - 2614505.4945054944, - 12428494.505494505 + 5370050.0, + 9672950.0 ] }, { @@ -6949,9 +6949,9 @@ "2028" ], "y": [ - -3805670.543288553, - -6539566.410938252, - 150633.07422680408 + -4574668.605321411, + -7696892.67467859, + 51845.59999999963 ] }, { @@ -6976,7 +6976,7 @@ "2028" ], "y": { - "bdata": "tHqKRfMITcHuEmzdin1dwXoUrgfDflJB", + "bdata": "BJa9JnNzUcEghevRwVNawYzC9Sg6ikVB", "dtype": "f8" } } @@ -6989,9 +6989,9 @@ "size": 12 }, "showarrow": false, - "text": "-$3.8M", + "text": "-$4.6M", "x": 0, - "y": -3805670.543288553, + "y": -4574668.605321411, "yshift": -14 }, { @@ -7000,9 +7000,9 @@ "size": 12 }, "showarrow": false, - "text": "-$7.7M", + "text": "-$6.9M", "x": 1, - "y": -7730731.45972131, + "y": -6901511.280000001, "yshift": -14 }, { @@ -7011,9 +7011,9 @@ "size": 12 }, "showarrow": false, - "text": "$4.8M", + "text": "$2.8M", "x": 2, - "y": 4848396.119999999, + "y": 2823284.3199999984, "yshift": 14 }, { @@ -7026,7 +7026,7 @@ "size": 12 }, "showarrow": false, - "text": "3-yr net $4.8M · ROI 48%
NPV@13.5% $2.2M · payback 32 months (~Aug 2028)", + "text": "3-yr net $2.8M · ROI 23%
NPV@13.5% $814K · payback 33 months (~Sep 2028)", "x": 0.01, "xref": "paper", "y": 0.98, @@ -7900,10 +7900,10 @@ "id": "a8c9584e", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.344575Z", - "iopub.status.busy": "2026-07-08T15:15:53.344407Z", - "iopub.status.idle": "2026-07-08T15:15:53.353487Z", - "shell.execute_reply": "2026-07-08T15:15:53.352923Z" + "iopub.execute_input": "2026-07-13T19:23:48.949950Z", + "iopub.status.busy": "2026-07-13T19:23:48.949609Z", + "iopub.status.idle": "2026-07-13T19:23:48.962323Z", + "shell.execute_reply": "2026-07-13T19:23:48.961575Z" } }, "outputs": [ @@ -7911,7 +7911,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "The missed costs move the 3-yr case by $16.6M ($21.5M pitched → $4.8M corrected).\n" + "The missed costs move the 3-yr case by $18.7M ($21.5M pitched → $2.8M corrected).\n" ] }, { @@ -7948,32 +7948,32 @@ " \n", " 3-yr incremental cost\n", " -$6.4M\n", - " $10.2M\n", + " $12.2M\n", " \n", " \n", " 3-yr net\n", " $21.5M\n", - " $4.8M\n", + " $2.8M\n", " \n", " \n", " ROI (net ÷ incremental cost)\n", " n/a — net cost saving\n", - " 48%\n", + " 23%\n", " \n", " \n", " NPV @ 13.5% (deck rate)\n", - " $15.3M\n", - " $2.2M\n", + " $15.5M\n", + " $814K\n", " \n", " \n", " NPV @ 8.0% (CTM treasury)\n", - " $17.5M\n", - " $3.1M\n", + " $17.6M\n", + " $1.5M\n", " \n", " \n", " Payback\n", " immediate\n", - " 32 months (~Aug 2028)\n", + " 33 months (~Sep 2028)\n", " \n", " \n", "\n", @@ -7982,12 +7982,12 @@ "text/plain": [ " As pitched (deck) Corrected\n", "3-yr benefits $15.0M $15.0M\n", - "3-yr incremental cost -$6.4M $10.2M\n", - "3-yr net $21.5M $4.8M\n", - "ROI (net ÷ incremental cost) n/a — net cost saving 48%\n", - "NPV @ 13.5% (deck rate) $15.3M $2.2M\n", - "NPV @ 8.0% (CTM treasury) $17.5M $3.1M\n", - "Payback immediate 32 months (~Aug 2028)" + "3-yr incremental cost -$6.4M $12.2M\n", + "3-yr net $21.5M $2.8M\n", + "ROI (net ÷ incremental cost) n/a — net cost saving 23%\n", + "NPV @ 13.5% (deck rate) $15.5M $814K\n", + "NPV @ 8.0% (CTM treasury) $17.6M $1.5M\n", + "Payback immediate 33 months (~Sep 2028)" ] }, "metadata": {}, @@ -8035,10 +8035,10 @@ "id": "6671014d", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.355336Z", - "iopub.status.busy": "2026-07-08T15:15:53.355180Z", - "iopub.status.idle": "2026-07-08T15:15:53.416805Z", - "shell.execute_reply": "2026-07-08T15:15:53.415898Z" + "iopub.execute_input": "2026-07-13T19:23:48.964954Z", + "iopub.status.busy": "2026-07-13T19:23:48.964672Z", + "iopub.status.idle": "2026-07-13T19:23:49.044601Z", + "shell.execute_reply": "2026-07-13T19:23:49.043299Z" } }, "outputs": [ @@ -8121,19 +8121,19 @@ "ygap": 2, "z": [ [ - 20930.0, - 52301.0, - 104602.0 + 26532.0, + 66303.0, + 132606.0 ], [ - 53389.0, - 133408.0, - 266749.0 + 67688.0, + 169131.0, + 338167.0 ], [ - 104602.0, - 261505.0, - 523010.0 + 132606.0, + 331515.0, + 663030.0 ] ] } @@ -8146,7 +8146,7 @@ "size": 12 }, "showarrow": false, - "text": "$21K", + "text": "$27K", "x": 0, "y": 0 }, @@ -8156,7 +8156,7 @@ "size": 12 }, "showarrow": false, - "text": "$52K", + "text": "$66K", "x": 1, "y": 0 }, @@ -8166,7 +8166,7 @@ "size": 12 }, "showarrow": false, - "text": "$105K", + "text": "$133K", "x": 2, "y": 0 }, @@ -8176,10 +8176,30 @@ "size": 12 }, "showarrow": false, - "text": "$53K", + "text": "$68K", "x": 0, "y": 1 }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$169K", + "x": 1, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$338K", + "x": 2, + "y": 1 + }, { "font": { "color": "#0b0b0b", @@ -8187,26 +8207,6 @@ }, "showarrow": false, "text": "$133K", - "x": 1, - "y": 1 - }, - { - "font": { - "color": "#0b0b0b", - "size": 12 - }, - "showarrow": false, - "text": "$267K", - "x": 2, - "y": 1 - }, - { - "font": { - "color": "#0b0b0b", - "size": 12 - }, - "showarrow": false, - "text": "$105K", "x": 0, "y": 2 }, @@ -8216,7 +8216,7 @@ "size": 12 }, "showarrow": false, - "text": "$262K", + "text": "$332K", "x": 1, "y": 2 }, @@ -8226,7 +8226,7 @@ "size": 12 }, "showarrow": false, - "text": "$523K", + "text": "$663K", "x": 2, "y": 2 } @@ -9109,10 +9109,10 @@ "id": "faa1e95f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.419794Z", - "iopub.status.busy": "2026-07-08T15:15:53.419516Z", - "iopub.status.idle": "2026-07-08T15:15:53.479629Z", - "shell.execute_reply": "2026-07-08T15:15:53.478659Z" + "iopub.execute_input": "2026-07-13T19:23:49.047075Z", + "iopub.status.busy": "2026-07-13T19:23:49.046773Z", + "iopub.status.idle": "2026-07-13T19:23:49.082168Z", + "shell.execute_reply": "2026-07-13T19:23:49.081039Z" } }, "outputs": [ @@ -9152,21 +9152,21 @@ " \n", " \n", " low hours\n", - " 5,672,146\n", - " 5,377,146\n", - " 5,082,146\n", + " 3,647,034\n", + " 3,352,034\n", + " 3,057,034\n", " \n", " \n", " mid hours\n", - " 5,260,896\n", - " 4,848,396\n", - " 4,435,896\n", + " 3,235,784\n", + " 2,823,284\n", + " 2,410,784\n", " \n", " \n", " high hours\n", - " 4,849,646\n", - " 4,319,646\n", - " 3,789,646\n", + " 2,824,534\n", + " 2,294,534\n", + " 1,764,534\n", " \n", " \n", "\n", @@ -9174,9 +9174,9 @@ ], "text/plain": [ " $175/h $225/h $275/h\n", - "low hours 5,672,146 5,377,146 5,082,146\n", - "mid hours 5,260,896 4,848,396 4,435,896\n", - "high hours 4,849,646 4,319,646 3,789,646" + "low hours 3,647,034 3,352,034 3,057,034\n", + "mid hours 3,235,784 2,823,284 2,410,784\n", + "high hours 2,824,534 2,294,534 1,764,534" ] }, "metadata": {}, @@ -9234,10 +9234,10 @@ "id": "6516ea96", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.482270Z", - "iopub.status.busy": "2026-07-08T15:15:53.481974Z", - "iopub.status.idle": "2026-07-08T15:15:53.521837Z", - "shell.execute_reply": "2026-07-08T15:15:53.521096Z" + "iopub.execute_input": "2026-07-13T19:23:49.084508Z", + "iopub.status.busy": "2026-07-13T19:23:49.084227Z", + "iopub.status.idle": "2026-07-13T19:23:49.121635Z", + "shell.execute_reply": "2026-07-13T19:23:49.120622Z" } }, "outputs": [ @@ -9246,7 +9246,7 @@ "output_type": "stream", "text": [ "All assertions passed.\n", - " benefits 3-yr $15.0M · tokens 3-yr $4.5M · corrected net 3-yr $4.8M\n" + " benefits 3-yr $15.0M · tokens 3-yr $6.6M · corrected net 3-yr $2.8M\n" ] } ], @@ -9271,7 +9271,7 @@ "_approx(_ps_c[2026], 607_633.94 + 405_089.30 + 167_000, tol=0.01) # 30% + 20% + training\n", "_approx(_ps_c[2027], 607_633.94 + 405_089.30, tol=0.01) # 30% + 20%\n", "_approx(managed_services_by_year()[2026],\n", - " MANAGED_SERVICES_ANNUAL * 3 / 12, tol=0.01) # Oct–Dec after go-live\n", + " MANAGED_SERVICES_ANNUAL * 2 / 12, tol=0.01) # Nov–Dec after go-live\n", "_approx(tco(\"ccaas_annual\"), 3_200_000) # signed contract overlay\n", "_approx(licence_costs_by_year(12, tco(\"ccaas_annual\"))[2027], 3_200_000)\n", "_contracts_at_default = (\n", @@ -9297,13 +9297,14 @@ "_sta_2028 = sum(_named[n] * 30 * TOKEN_ROLLOUT.live_months_in_year(n, 3) for n in ALL_SITES)\n", "_approx(sta.query(\"year == 2028\")[\"annual_cost\"].sum(), _sta_2028)\n", "if not COPILOT_INCLUDES_ASIA:\n", - " _approx(_sta_2028, 715_800) # NAM/AUZ/EMEA ×12mo + ASIA ×10mo, by hand\n", + " _approx(_sta_2028, 751_680) # PM timeline: every site live all 12 months in 2028\n", " cp = tokens_long.query(\"cost_line == 'Agent Copilot [named]' and year == 2028\")\n", " _approx(cp[\"annual_cost\"].sum(), 1_490 * 40 * 12) # = $715,200\n", "assert (tokens_long.query(\"cost_line == 'AI Summary & Insights'\")[\"annual_cost\"] == 0).all(), \\\n", " \"Rule 1: Copilot must cover AI Summary at Copilot sites\"\n", "assert math.ceil(1_214_358 * DEFAULT_METERS[\"Predictive Routing\"].tokens_per_unit) == 71_433\n", - "_approx(tokens_long.query(\"year == 2026\")[\"annual_cost\"].sum(), 0) # nothing live in 2026\n", + "# PM timeline: NA live Oct 2026 → tokens DO accrue in 2026 (NA-only).\n", + "_approx(tokens_long.query(\"year == 2026\")[\"annual_cost\"].sum(), 424_350)\n", "\n", "# Section 4 — impl model reconciles with the V2 doc (explicit default args)\n", "_, _impl_p, _kb_p, _steady_p = build_impl_costs(sites, \"mid\", 225, True, False, True)\n", @@ -9394,10 +9395,10 @@ "id": "c4c025b5", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:53.524766Z", - "iopub.status.busy": "2026-07-08T15:15:53.524542Z", - "iopub.status.idle": "2026-07-08T15:15:53.567349Z", - "shell.execute_reply": "2026-07-08T15:15:53.566263Z" + "iopub.execute_input": "2026-07-13T19:23:49.125035Z", + "iopub.status.busy": "2026-07-13T19:23:49.124736Z", + "iopub.status.idle": "2026-07-13T19:23:49.161250Z", + "shell.execute_reply": "2026-07-13T19:23:49.160380Z" } }, "outputs": [ @@ -9424,51 +9425,45 @@ "| Email | 143000 | 93000 | 235000 | 2500000 |\n", "| STA | 105000 | 72000 | 131000 | 506000 |\n", "| Predictive Routing | 302000 | 51000 | 6000 | 167000 |\n", - "| Supervisor Copilot | 27000 | 0 | 49000 | 291000 |\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "| Supervisor Copilot | 27000 | 0 | 49000 | 291000 |\n", "\n", "#### Benefits by capability × year (schedule-phased, $)\n", "\n", "| capability | 2026 | 2027 | 2028 |\n", "|:-------------------|-------:|----------:|----------:|\n", - "| Agent Copilot | 0 | 1,150,000 | 6,214,000 |\n", - "| WFM | 0 | 107,692 | 2,893,308 |\n", - "| Email | 0 | 1,082,429 | 1,888,571 |\n", - "| STA | 0 | 134,577 | 679,423 |\n", - "| Predictive Routing | 0 | 64,981 | 461,019 |\n", - "| Supervisor Copilot | 0 | 74,827 | 292,173 |\n", + "| Agent Copilot | 0 | 2,700,600 | 4,663,400 |\n", + "| WFM | 0 | 624,800 | 2,376,200 |\n", + "| Email | 0 | 1,379,750 | 1,591,250 |\n", + "| STA | 0 | 331,650 | 482,350 |\n", + "| Predictive Routing | 0 | 161,400 | 364,600 |\n", + "| Supervisor Copilot | 0 | 171,850 | 195,150 |\n", "\n", "#### AI token cost by meter × year ($)\n", "\n", - "| cost_line | 2026 | 2027 | 2028 | 3-yr |\n", - "|:----------------------------------------|-------:|----------:|----------:|----------:|\n", - "| Predictive Routing | 0 | 583,561 | 1,764,870 | 2,348,431 |\n", - "| Agent Copilot [named] | 0 | 311,000 | 715,200 | 1,026,200 |\n", - "| Speech & Text Analytics [named] | 0 | 233,250 | 715,800 | 949,050 |\n", - "| Email AI (Auto-Respond) | 0 | 46,272 | 87,136 | 133,408 |\n", - "| AI Translate | 0 | 2,100 | 62,150 | 64,250 |\n", - "| AI Summary & Insights | 0 | 0 | 0 | 0 |\n", - "| WFM (no token meter — licence-included) | 0 | 0 | 0 | 0 |\n", - "| TOTAL | 0 | 1,176,183 | 3,345,156 | 4,521,339 |\n", + "| cost_line | 2026 | 2027 | 2028 | 3-yr |\n", + "|:----------------------------------------|--------:|----------:|----------:|----------:|\n", + "| Predictive Routing | 214,299 | 1,362,323 | 1,851,816 | 3,428,438 |\n", + "| Agent Copilot [named] | 114,000 | 662,400 | 715,200 | 1,491,600 |\n", + "| Speech & Text Analytics [named] | 85,500 | 550,620 | 751,680 | 1,387,800 |\n", + "| Email AI (Auto-Respond) | 10,551 | 67,080 | 91,500 | 169,131 |\n", + "| AI Translate | 0 | 34,185 | 69,540 | 103,725 |\n", + "| AI Summary & Insights | 0 | 0 | 0 | 0 |\n", + "| WFM (no token meter — licence-included) | 0 | 0 | 0 | 0 |\n", + "| TOTAL | 424,350 | 2,676,608 | 3,479,736 | 6,580,694 |\n", "\n", "#### AI implementation by workstream ($, phased)\n", "\n", - "| workstream | hours | cost | 2026 | 2027 | 2028 |\n", - "|:-----------------------------------------------------|--------:|----------:|--------:|--------:|--------:|\n", - "| Agent Copilot | 1,500 | 337,500 | 207,888 | 129,612 | 0 |\n", - "| Cross-cutting (PM, governance, testing, integration) | 1,400 | 315,000 | 178,711 | 126,366 | 9,923 |\n", - "| Email Auto-Respond | 1,100 | 247,500 | 169,530 | 70,174 | 7,796 |\n", - "| KB readiness (prerequisite) | 1,000 | 225,000 | 127,651 | 90,261 | 7,088 |\n", - "| Predictive Routing | 550 | 123,750 | 70,208 | 49,644 | 3,898 |\n", - "| STA | 1,000 | 225,000 | 127,651 | 90,261 | 7,088 |\n", - "| Supervisor Copilot | 300 | 67,500 | 41,578 | 25,922 | 0 |\n", - "| Steady-state tuning (2027-28) | 1,400 | 315,000 | 0 | 157,500 | 157,500 |\n", - "| TOTAL | 8,250 | 1,856,250 | 923,217 | 739,741 | 193,293 |\n", + "| workstream | hours | cost | 2026 | 2027 | 2028 |\n", + "|:-----------------------------------------------------|--------:|----------:|----------:|--------:|--------:|\n", + "| Agent Copilot | 1,500 | 337,500 | 311,832 | 25,668 | 0 |\n", + "| Cross-cutting (PM, governance, testing, integration) | 1,400 | 315,000 | 257,242 | 57,758 | 0 |\n", + "| Email Auto-Respond | 1,100 | 247,500 | 202,119 | 45,381 | 0 |\n", + "| KB readiness (prerequisite) | 1,000 | 225,000 | 183,744 | 41,256 | 0 |\n", + "| Predictive Routing | 550 | 123,750 | 101,059 | 22,691 | 0 |\n", + "| STA | 1,000 | 225,000 | 183,744 | 41,256 | 0 |\n", + "| Supervisor Copilot | 300 | 67,500 | 62,366 | 5,134 | 0 |\n", + "| Steady-state tuning (2027-28) | 1,400 | 315,000 | 0 | 157,500 | 157,500 |\n", + "| TOTAL | 8,250 | 1,856,250 | 1,302,108 | 396,642 | 157,500 |\n", "\n", "#### Corrected programme cost stack ($)\n", "\n", @@ -9476,10 +9471,10 @@ "|:----------------------------------------------------|----------:|----------:|----------:|-----------:|\n", "| CCaaS platform licences (contracted, ramp-adjusted) | 1,600,000 | 3,200,000 | 3,200,000 | 8,000,000 |\n", "| NTT PS + training (SOW billing milestones) | 1,179,723 | 1,012,723 | 0 | 2,192,446 |\n", - "| NTT managed services (from MCX go-live) | 102,730 | 410,918 | 410,918 | 924,566 |\n", + "| NTT managed services (from MCX go-live) | 68,486 | 410,918 | 410,918 | 890,323 |\n", "| Existing platform (term-contract run-off) | 7,300,001 | 7,300,001 | 0 | 14,600,002 |\n", - "| AI token consumption | 0 | 1,176,183 | 3,345,156 | 4,521,339 |\n", - "| AI implementation + KB readiness | 923,217 | 582,241 | 35,793 | 1,541,250 |\n", + "| AI token consumption | 424,350 | 2,676,608 | 3,479,736 | 6,580,694 |\n", + "| AI implementation + KB readiness | 1,302,108 | 239,142 | 0 | 1,541,250 |\n", "| AI steady-state tuning | 0 | 157,500 | 157,500 | 315,000 |\n", "\n", "#### As-pitched cost stack ($)\n", @@ -9494,12 +9489,12 @@ "| | As pitched (deck) | Corrected |\n", "|:-----------------------------|:----------------------|:----------------------|\n", "| 3-yr benefits | $15.0M | $15.0M |\n", - "| 3-yr incremental cost | -$6.4M | $10.2M |\n", - "| 3-yr net | $21.5M | $4.8M |\n", - "| ROI (net ÷ incremental cost) | n/a — net cost saving | 48% |\n", - "| NPV @ 13.5% (deck rate) | $15.3M | $2.2M |\n", - "| NPV @ 8.0% (CTM treasury) | $17.5M | $3.1M |\n", - "| Payback | immediate | 32 months (~Aug 2028) |\n", + "| 3-yr incremental cost | -$6.4M | $12.2M |\n", + "| 3-yr net | $21.5M | $2.8M |\n", + "| ROI (net ÷ incremental cost) | n/a — net cost saving | 23% |\n", + "| NPV @ 13.5% (deck rate) | $15.5M | $814K |\n", + "| NPV @ 8.0% (CTM treasury) | $17.6M | $1.5M |\n", + "| Payback | immediate | 33 months (~Sep 2028) |\n", "\n", "#### Model state (JSON)\n", "\n", @@ -9507,13 +9502,13 @@ "{\n", " \"benefits_by_year\": {\n", " \"2026\": 0,\n", - " \"2027\": 2614505,\n", - " \"2028\": 12428495\n", + " \"2027\": 5370050,\n", + " \"2028\": 9672950\n", " },\n", " \"corrected_cost_by_year\": {\n", - " \"2026\": 11105671,\n", - " \"2027\": 13839566,\n", - " \"2028\": 7149367\n", + " \"2026\": 11874669,\n", + " \"2027\": 14996893,\n", + " \"2028\": 7248154\n", " },\n", " \"pitched_cost_by_year\": {\n", " \"2026\": 6867000,\n", @@ -9521,25 +9516,25 @@ " \"2028\": 4300000\n", " },\n", " \"net_by_year_corrected\": {\n", - " \"2026\": -3805671,\n", - " \"2027\": -3925061,\n", - " \"2028\": 12579128\n", + " \"2026\": -4574669,\n", + " \"2027\": -2326843,\n", + " \"2028\": 9724796\n", " },\n", " \"kpis_corrected\": {\n", " \"benefits_3yr\": 15043000,\n", - " \"incremental_cost_3yr\": 10194604,\n", - " \"net_3yr\": 4848396,\n", - " \"roi\": 0.4756,\n", - " \"npv\": 2203369,\n", + " \"incremental_cost_3yr\": 12219716,\n", + " \"net_3yr\": 2823284,\n", + " \"roi\": 0.231,\n", + " \"npv\": 814307,\n", " \"discount_rate\": 0.135,\n", - " \"payback\": \"32 months (~Aug 2028)\"\n", + " \"payback\": \"33 months (~Sep 2028)\"\n", " },\n", " \"kpis_pitched\": {\n", " \"benefits_3yr\": 15043000,\n", " \"incremental_cost_3yr\": -6433000,\n", " \"net_3yr\": 21476000,\n", " \"roi\": null,\n", - " \"npv\": 15291853,\n", + " \"npv\": 15546275,\n", " \"discount_rate\": 0.135,\n", " \"payback\": \"immediate\"\n", " },\n", @@ -9555,23 +9550,23 @@ " \"amount\": 607633.94\n", " },\n", " {\n", - " \"name\": \"Start of client UAT (first region)\",\n", - " \"date\": \"2026-09-30\",\n", + " \"name\": \"Start of client UAT (first region, NA)\",\n", + " \"date\": \"2026-10-16\",\n", " \"amount\": 405089.3\n", " },\n", " {\n", - " \"name\": \"Start of client UAT (last region)\",\n", - " \"date\": \"2027-06-30\",\n", + " \"name\": \"Start of client UAT (last region, APAC)\",\n", + " \"date\": \"2027-10-01\",\n", " \"amount\": 607633.94\n", " },\n", " {\n", " \"name\": \"Completion of last go-live migration\",\n", - " \"date\": \"2027-09-30\",\n", + " \"date\": \"2027-11-01\",\n", " \"amount\": 405089.3\n", " }\n", " ],\n", " \"managed_services_annual\": 410918.4,\n", - " \"mcx_go_live\": \"2026-09-30\",\n", + " \"mcx_go_live\": \"2026-10-16\",\n", " \"hours_mode\": \"mid\",\n", " \"blended_rate\": 225,\n", " \"include_kb_readiness\": true,\n", @@ -9698,755 +9693,7 @@ "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { - "0805812abf564bbf9816351103b02bea": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0de1ab4bb6ce41bc84cb0510923592b7": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0f0c0863eade46b2a23d4f7efe58c173": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.slider.SliderWidget", - "_css": "\n .mljar-slider-container {\n --mljar-slider-thumb-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0;\n width: 100%;\n max-width: 100%;\n min-width: 120px;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n font-weight: normal;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-slider-stage {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding-top: 20px;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-floating-value {\n position: absolute;\n top: 0;\n left: 8px;\n transform: translateX(-50%);\n color: #007bff;\n font-weight: 700;\n font-size: 0.95em;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n pointer-events: none;\n z-index: 1;\n }\n\n .mljar-slider-top-label {\n margin-bottom: 6px;\n font-weight: 600;\n line-height: 1.2;\n }\n\n .mljar-slider-row {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-minmax-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n margin-top: 6px;\n color: #616673;\n font-size: 0.9em;\n line-height: 1.2;\n box-sizing: border-box;\n }\n\n .mljar-slider-min-label,\n .mljar-slider-max-label {\n color: inherit;\n white-space: nowrap;\n }\n\n .mljar-slider-input {\n display: block;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n background: transparent;\n -webkit-appearance: none;\n appearance: none;\n border: none;\n height: 24px;\n padding: 0;\n margin: 0;\n cursor: pointer;\n box-sizing: border-box;\n }\n\n .mljar-slider-input:focus {\n outline: none;\n }\n\n .mljar-slider-input:focus-visible::-webkit-slider-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n .mljar-slider-input:focus-visible::-moz-range-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n\n /* Track */\n .mljar-slider-input::-webkit-slider-runnable-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n margin: auto;\n }\n .mljar-slider-input::-moz-range-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n }\n\n /* Thumb */\n .mljar-slider-input::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n margin-top: -5px;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n .mljar-slider-input::-moz-range-thumb {\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n\n .mljar-slider-input:disabled {\n cursor: not-allowed;\n opacity: 0.7;\n }\n\n .mljar-slider-input:disabled::-webkit-slider-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:disabled::-moz-range-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:active::-webkit-slider-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n .mljar-slider-input:active::-moz-range-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-slider-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-slider-top-label\");\n\n const sliderStage = document.createElement(\"div\");\n sliderStage.classList.add(\"mljar-slider-stage\");\n\n const floatingValueLabel = document.createElement(\"div\");\n floatingValueLabel.classList.add(\"mljar-slider-floating-value\");\n\n const sliderRow = document.createElement(\"div\");\n sliderRow.classList.add(\"mljar-slider-row\");\n\n const slider = document.createElement(\"input\");\n slider.type = \"range\";\n slider.classList.add(\"mljar-slider-input\");\n\n const minMaxRow = document.createElement(\"div\");\n minMaxRow.classList.add(\"mljar-slider-minmax-row\");\n\n const minLabel = document.createElement(\"span\");\n minLabel.classList.add(\"mljar-slider-min-label\");\n\n const maxLabel = document.createElement(\"span\");\n maxLabel.classList.add(\"mljar-slider-max-label\");\n\n function positionFloatingValue() {\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const value = Number(model.get(\"value\"));\n const range = max - min;\n const ratio = range <= 0 ? 0 : (value - min) / range;\n const clampedRatio = Math.max(0, Math.min(1, ratio));\n const computed = getComputedStyle(container);\n const thumbSize =\n parseFloat(computed.getPropertyValue(\"--mljar-slider-thumb-size\")) || 16;\n const inputWidth = slider.clientWidth || 0;\n const stageWidth = sliderStage.clientWidth || inputWidth || 0;\n const labelWidth = floatingValueLabel.offsetWidth || 0;\n\n if (inputWidth <= 0 || stageWidth <= 0) {\n return;\n }\n\n const sliderOffsetLeft = slider.offsetLeft || 0;\n const usableWidth = Math.max(0, inputWidth - thumbSize);\n const idealCenter =\n sliderOffsetLeft + thumbSize / 2 + clampedRatio * usableWidth;\n const halfLabel = labelWidth / 2;\n const minCenter = halfLabel;\n const maxCenter = Math.max(halfLabel, stageWidth - halfLabel);\n const finalCenter = Math.min(Math.max(idealCenter, minCenter), maxCenter);\n\n floatingValueLabel.style.left = `${finalCenter}px`;\n }\n\n function syncFromModel() {\n slider.min = model.get(\"min\");\n slider.max = model.get(\"max\");\n slider.value = model.get(\"value\");\n floatingValueLabel.textContent = String(model.get(\"value\"));\n minLabel.textContent = String(model.get(\"min\"));\n maxLabel.textContent = String(model.get(\"max\"));\n\n slider.disabled = !!model.get(\"disabled\");\n topLabel.textContent = model.get(\"label\") || \"Select number\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n positionFloatingValue();\n }\n\n let debounceTimer = null;\n slider.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", Number(slider.value));\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n model.save_changes();\n }, 100);\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n sliderRow.appendChild(slider);\n minMaxRow.appendChild(minLabel);\n minMaxRow.appendChild(maxLabel);\n\n container.appendChild(topLabel);\n sliderStage.appendChild(floatingValueLabel);\n sliderStage.appendChild(sliderRow);\n sliderStage.appendChild(minMaxRow);\n container.appendChild(sliderStage);\n el.appendChild(container);\n\n syncFromModel();\n\n const resizeObserver = new ResizeObserver(() => {\n positionFloatingValue();\n });\n resizeObserver.observe(sliderStage);\n\n return () => {\n resizeObserver.disconnect();\n };\n\n // ---- read cell id (no DOM modifications) ----\n /*const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Predictive Routing eligibility (%)", - "layout": "IPY_MODEL_e909e59b90a74e71b282acd76feecdae", - "layout_path": null, - "max": 100, - "min": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 100 - } - }, - "11bd0914b83a4f2d97be8bb927d287c1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "18bd7e25aac44935859433462a96e263": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1a6c73c71082451c8a56e12a30d34377": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2a71711de67c43aba0ab438e2e9ea50e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2f75de1a11684c259e244f088906f63f": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3414451f3bea4ecd86b616edc1bcf92f": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.select.SelectWidget", - "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "choices": [ - "low", - "mid", - "high" - ], - "disabled": false, - "hidden": false, - "label": "Impl hours (V2 range)", - "layout": "IPY_MODEL_88aaa7c9e2204aa18a6be394fffe5606", - "layout_path": null, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "mid" - } - }, - "37a36aac015e434390aa69448ae25871": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "380860d8b37c433d9e7758763f6bb94c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "405a08c2329849a9858cb8f08e9ed881": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4470b352a7e348919049702c575225c8": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "cell_id": "", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_405a08c2329849a9858cb8f08e9ed881", - "layout_path": null, - "placeholder": "​", - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "style": "IPY_MODEL_d590bfa08c5d4fdb853f875332c2d1c1", - "tabbable": null, - "tooltip": null, - "value": "" - } - }, - "463af11b0ee9471389b8300965d6cf69": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "50b0fca7bacc457f927fe77456336164": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "ASIA — current platform cost ($/yr)", - "layout": "IPY_MODEL_69a14a51c82640d2a59420ee51215637", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 2069588.0 - } - }, - "555759d10b104fbf9ac24b8e9963baa8": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "AI Translate eligibility 🟡", - "layout": "IPY_MODEL_f90949338fe246019f73c66d10e21727", - "layout_path": null, - "max": 1.0, - "min": 0.0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "step": 0.01, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 0.01 - } - }, - "5c32c561ba9c462286aa30e7efe3dd0c": { + "08825d38562f490eace54ea96badd044": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -10465,8 +9712,8 @@ "cell_id": "", "disabled": false, "hidden": false, - "label": "ANZ — contract termination", - "layout": "IPY_MODEL_0de1ab4bb6ce41bc84cb0510923592b7", + "label": "ASIA — contract termination", + "layout": "IPY_MODEL_16824d73d4dd42799fda8449c73ae6e9", "layout_path": null, "max": "", "min": "", @@ -10479,7 +9726,7 @@ "value": "2027-12-31" } }, - "5de6c388add14a5ea90c4a2972efb4d5": { + "0e3be284c70e45b7ad9fe12c3b76163f": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -10532,41 +9779,7 @@ "width": null } }, - "6347323104f645beac02bd1592fb60d4": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Copilot includes ASIA sites", - "last_changed_at": "", - "layout": "IPY_MODEL_2f75de1a11684c259e244f088906f63f", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": false - } - }, - "69a14a51c82640d2a59420ee51215637": { + "16824d73d4dd42799fda8449c73ae6e9": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -10619,109 +9832,7 @@ "width": null } }, - "6b4df6310b224d668a46ff3767622396": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "NA Email implements early (Jan 2027)", - "last_changed_at": "", - "layout": "IPY_MODEL_18bd7e25aac44935859433462a96e263", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": true - } - }, - "6c099f4dfe1c419984d542e66c67150f": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Include KB readiness (500–1,500 h)", - "last_changed_at": "", - "layout": "IPY_MODEL_11bd0914b83a4f2d97be8bb927d287c1", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": true - } - }, - "6e62963d5c0c47a5b8e7d41daedbe004": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "NA — current platform cost ($/yr)", - "layout": "IPY_MODEL_0805812abf564bbf9816351103b02bea", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 3348969.0 - } - }, - "7baadd5d4fe24328901f7aebf36ad51b": { + "1bf396a231374a2097e03bf45fc874ff": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -10774,40 +9885,7 @@ "width": null } }, - "7dc643d5b4e145b7b1eba08ff5ac668c": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.date.DateInputWidget", - "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "EMEA — contract termination", - "layout": "IPY_MODEL_ea2c2a2e8127410a90b15116308d2976", - "layout_path": null, - "max": "", - "min": "", - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "2027-12-31" - } - }, - "88aaa7c9e2204aa18a6be394fffe5606": { + "2beb1f0f71ec465fb08e6e2f5caecf2b": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -10860,143 +9938,7 @@ "width": null } }, - "8a4664e2944b48dd8c16ce8ea30481f4": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "EMEA — current platform cost ($/yr)", - "layout": "IPY_MODEL_ee4f2d492a8a427986098361f18ff667", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 1204124.0 - } - }, - "91e44777e10b4dc982d4f35fb80fb2a1": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Genesys ramp — licence-free months", - "layout": "IPY_MODEL_37a36aac015e434390aa69448ae25871", - "layout_path": null, - "max": 24.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 1.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 6.0 - } - }, - "9c731e39482e40308709c89e44302200": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.select.SelectWidget", - "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "choices": [ - "13.5% (deck)", - "8.0% (CTM treasury)" - ], - "disabled": false, - "hidden": false, - "label": "NPV discount rate", - "layout": "IPY_MODEL_1a6c73c71082451c8a56e12a30d34377", - "layout_path": null, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "13.5% (deck)" - } - }, - "b9727084f82b49089dfcacd62b9d72d5": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.date.DateInputWidget", - "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "NA — contract termination", - "layout": "IPY_MODEL_2a71711de67c43aba0ab438e2e9ea50e", - "layout_path": null, - "max": "", - "min": "", - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "2027-12-31" - } - }, - "ba2c47f86417464896faa72a9e2f0b8e": { + "30f9c2d5b1044cd2ad4bb3d697c6a89e": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -11049,41 +9991,60 @@ "width": null } }, - "c337c93de81943939d6e1b0e1c392359": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", + "3c767a6b472d42c89e6b7baf705fcbbc": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "CCaaS licence run-rate ($/yr) — contracted", - "layout": "IPY_MODEL_7baadd5d4fe24328901f7aebf36ad51b", - "layout_path": null, - "max": 10000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 100000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 3200000.0 + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null } }, - "d57babc80cd04cf3bbb6b5f9749db410": { + "3f822353156447048ac80bbeab4fc036": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -11108,7 +10069,7 @@ "disabled": false, "hidden": false, "label": "Blended rate ($/h)", - "layout": "IPY_MODEL_463af11b0ee9471389b8300965d6cf69", + "layout": "IPY_MODEL_5401d51d679e472ca62123b0019023ca", "layout_path": null, "position": "sidebar", "render_slot_id": null, @@ -11119,59 +10080,7 @@ "value": "225" } }, - "d590bfa08c5d4fdb853f875332c2d1c1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } - }, - "d9877763b4a94831b94c3d083f818d58": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Auto-respond tokens per message 🟡", - "layout": "IPY_MODEL_ba2c47f86417464896faa72a9e2f0b8e", - "layout_path": null, - "max": 0.5, - "min": 0.0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "step": 0.005, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 0.05 - } - }, - "e0626f2d55c14d7b802fb701e2074005": { + "408cdd33dace46aca1a41d1d0e75edc1": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -11190,8 +10099,8 @@ "cell_id": "", "disabled": false, "hidden": false, - "label": "ASIA — contract termination", - "layout": "IPY_MODEL_380860d8b37c433d9e7758763f6bb94c", + "label": "EMEA — contract termination", + "layout": "IPY_MODEL_87836824483549ff82f67ec4b169b098", "layout_path": null, "max": "", "min": "", @@ -11204,7 +10113,253 @@ "value": "2027-12-31" } }, - "e0699dfd28c84c71a9e7f8a1ae48add5": { + "43454ebce3e44a238ef3a9b82836bdde": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA — current platform cost ($/yr)", + "layout": "IPY_MODEL_6295cf92344b415598307483934485f3", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3348969.0 + } + }, + "53869a7ea78b44f4a134983d354228f0": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5401d51d679e472ca62123b0019023ca": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "54f1095af078476ba27715f8826aa967": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "587042f29ec04fc09f76e2c21701a1fd": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5907e927e40b4fd7b59484d0c541091c": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -11224,7 +10379,7 @@ "disabled": false, "hidden": false, "label": "Email auto-respond rate", - "layout": "IPY_MODEL_ecee11d4c3594ee9845e059ebf23e69a", + "layout": "IPY_MODEL_0e3be284c70e45b7ad9fe12c3b76163f", "layout_path": null, "max": 0.6, "min": 0.0, @@ -11238,7 +10393,41 @@ "value": 0.255 } }, - "e909e59b90a74e71b282acd76feecdae": { + "5ccddaf9e94a4222afc3a08567ed9339": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ASIA — current platform cost ($/yr)", + "layout": "IPY_MODEL_61085bdb080e48e186ec10b1abee291a", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 2069588.0 + } + }, + "61085bdb080e48e186ec10b1abee291a": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -11291,7 +10480,7 @@ "width": null } }, - "ea2c2a2e8127410a90b15116308d2976": { + "6295cf92344b415598307483934485f3": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -11344,7 +10533,41 @@ "width": null } }, - "ecee11d4c3594ee9845e059ebf23e69a": { + "64a4f40d3f8a4633adc0cded5e2fb6c5": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "CCaaS licence run-rate ($/yr) — contracted", + "layout": "IPY_MODEL_3c767a6b472d42c89e6b7baf705fcbbc", + "layout_path": null, + "max": 10000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 100000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3200000.0 + } + }, + "6ce50fa7157042a4be99b19f24be3ce7": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -11397,113 +10620,7 @@ "width": null } }, - "ee4f2d492a8a427986098361f18ff667": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f90949338fe246019f73c66d10e21727": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "feb40a4b31fd415aa44db460a40e1037": { + "72a50fd89db8497b97b625d05c3ed7f0": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -11523,7 +10640,7 @@ "disabled": false, "hidden": false, "label": "ANZ — current platform cost ($/yr)", - "layout": "IPY_MODEL_5de6c388add14a5ea90c4a2972efb4d5", + "layout": "IPY_MODEL_54f1095af078476ba27715f8826aa967", "layout_path": null, "max": 8000000.0, "min": 0.0, @@ -11536,6 +10653,884 @@ "url_key": "", "value": 677320.0 } + }, + "79ec042073404a6da4ec639b6b44a57c": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA — contract termination", + "layout": "IPY_MODEL_9cee46de8d854c66b67e25159283a55c", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "7b92a3a1d4ec49049e22687c38236912": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8309c39462b34cc5afad42785a0c53ff": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "87836824483549ff82f67ec4b169b098": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8c0f6f8f207c4f7b90aa037b58790d4c": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.slider.SliderWidget", + "_css": "\n .mljar-slider-container {\n --mljar-slider-thumb-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0;\n width: 100%;\n max-width: 100%;\n min-width: 120px;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n font-weight: normal;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-slider-stage {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding-top: 20px;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-floating-value {\n position: absolute;\n top: 0;\n left: 8px;\n transform: translateX(-50%);\n color: #007bff;\n font-weight: 700;\n font-size: 0.95em;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n pointer-events: none;\n z-index: 1;\n }\n\n .mljar-slider-top-label {\n margin-bottom: 6px;\n font-weight: 600;\n line-height: 1.2;\n }\n\n .mljar-slider-row {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-minmax-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n margin-top: 6px;\n color: #616673;\n font-size: 0.9em;\n line-height: 1.2;\n box-sizing: border-box;\n }\n\n .mljar-slider-min-label,\n .mljar-slider-max-label {\n color: inherit;\n white-space: nowrap;\n }\n\n .mljar-slider-input {\n display: block;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n background: transparent;\n -webkit-appearance: none;\n appearance: none;\n border: none;\n height: 24px;\n padding: 0;\n margin: 0;\n cursor: pointer;\n box-sizing: border-box;\n }\n\n .mljar-slider-input:focus {\n outline: none;\n }\n\n .mljar-slider-input:focus-visible::-webkit-slider-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n .mljar-slider-input:focus-visible::-moz-range-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n\n /* Track */\n .mljar-slider-input::-webkit-slider-runnable-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n margin: auto;\n }\n .mljar-slider-input::-moz-range-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n }\n\n /* Thumb */\n .mljar-slider-input::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n margin-top: -5px;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n .mljar-slider-input::-moz-range-thumb {\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n\n .mljar-slider-input:disabled {\n cursor: not-allowed;\n opacity: 0.7;\n }\n\n .mljar-slider-input:disabled::-webkit-slider-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:disabled::-moz-range-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:active::-webkit-slider-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n .mljar-slider-input:active::-moz-range-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-slider-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-slider-top-label\");\n\n const sliderStage = document.createElement(\"div\");\n sliderStage.classList.add(\"mljar-slider-stage\");\n\n const floatingValueLabel = document.createElement(\"div\");\n floatingValueLabel.classList.add(\"mljar-slider-floating-value\");\n\n const sliderRow = document.createElement(\"div\");\n sliderRow.classList.add(\"mljar-slider-row\");\n\n const slider = document.createElement(\"input\");\n slider.type = \"range\";\n slider.classList.add(\"mljar-slider-input\");\n\n const minMaxRow = document.createElement(\"div\");\n minMaxRow.classList.add(\"mljar-slider-minmax-row\");\n\n const minLabel = document.createElement(\"span\");\n minLabel.classList.add(\"mljar-slider-min-label\");\n\n const maxLabel = document.createElement(\"span\");\n maxLabel.classList.add(\"mljar-slider-max-label\");\n\n function positionFloatingValue() {\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const value = Number(model.get(\"value\"));\n const range = max - min;\n const ratio = range <= 0 ? 0 : (value - min) / range;\n const clampedRatio = Math.max(0, Math.min(1, ratio));\n const computed = getComputedStyle(container);\n const thumbSize =\n parseFloat(computed.getPropertyValue(\"--mljar-slider-thumb-size\")) || 16;\n const inputWidth = slider.clientWidth || 0;\n const stageWidth = sliderStage.clientWidth || inputWidth || 0;\n const labelWidth = floatingValueLabel.offsetWidth || 0;\n\n if (inputWidth <= 0 || stageWidth <= 0) {\n return;\n }\n\n const sliderOffsetLeft = slider.offsetLeft || 0;\n const usableWidth = Math.max(0, inputWidth - thumbSize);\n const idealCenter =\n sliderOffsetLeft + thumbSize / 2 + clampedRatio * usableWidth;\n const halfLabel = labelWidth / 2;\n const minCenter = halfLabel;\n const maxCenter = Math.max(halfLabel, stageWidth - halfLabel);\n const finalCenter = Math.min(Math.max(idealCenter, minCenter), maxCenter);\n\n floatingValueLabel.style.left = `${finalCenter}px`;\n }\n\n function syncFromModel() {\n slider.min = model.get(\"min\");\n slider.max = model.get(\"max\");\n slider.value = model.get(\"value\");\n floatingValueLabel.textContent = String(model.get(\"value\"));\n minLabel.textContent = String(model.get(\"min\"));\n maxLabel.textContent = String(model.get(\"max\"));\n\n slider.disabled = !!model.get(\"disabled\");\n topLabel.textContent = model.get(\"label\") || \"Select number\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n positionFloatingValue();\n }\n\n let debounceTimer = null;\n slider.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", Number(slider.value));\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n model.save_changes();\n }, 100);\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n sliderRow.appendChild(slider);\n minMaxRow.appendChild(minLabel);\n minMaxRow.appendChild(maxLabel);\n\n container.appendChild(topLabel);\n sliderStage.appendChild(floatingValueLabel);\n sliderStage.appendChild(sliderRow);\n sliderStage.appendChild(minMaxRow);\n container.appendChild(sliderStage);\n el.appendChild(container);\n\n syncFromModel();\n\n const resizeObserver = new ResizeObserver(() => {\n positionFloatingValue();\n });\n resizeObserver.observe(sliderStage);\n\n return () => {\n resizeObserver.disconnect();\n };\n\n // ---- read cell id (no DOM modifications) ----\n /*const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Predictive Routing eligibility (%)", + "layout": "IPY_MODEL_6ce50fa7157042a4be99b19f24be3ce7", + "layout_path": null, + "max": 100, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 100 + } + }, + "95256d5116704a59b90582870595a766": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ANZ — contract termination", + "layout": "IPY_MODEL_cf9caabe60a64877b8d0a4bd4894f499", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "9cee46de8d854c66b67e25159283a55c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a46ed7fed98548ddbf0b78433ba14c1c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a55328df761a4c889bf2f82b4e986153": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Include KB readiness (500–1,500 h)", + "last_changed_at": "", + "layout": "IPY_MODEL_30f9c2d5b1044cd2ad4bb3d697c6a89e", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "aa1d6205d7b94e04abb3e7fdcf0311c1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "b83db5f7d9154255ae5e8db2dc7f112a": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "13.5% (deck)", + "8.0% (CTM treasury)" + ], + "disabled": false, + "hidden": false, + "label": "NPV discount rate", + "layout": "IPY_MODEL_587042f29ec04fc09f76e2c21701a1fd", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "13.5% (deck)" + } + }, + "bf7b6f3bca5f48e6838dc54f84943731": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c2c1b1b71a354d27804630f9e5135ca2": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA Email implements early (Jan 2027)", + "last_changed_at": "", + "layout": "IPY_MODEL_2beb1f0f71ec465fb08e6e2f5caecf2b", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "cf9caabe60a64877b8d0a4bd4894f499": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d55f5a00e77747b5a9142f96092253d2": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "EMEA — current platform cost ($/yr)", + "layout": "IPY_MODEL_8309c39462b34cc5afad42785a0c53ff", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 1204124.0 + } + }, + "d69203bdb8584fc9975e475199fcc235": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Genesys ramp — licence-free months", + "layout": "IPY_MODEL_7b92a3a1d4ec49049e22687c38236912", + "layout_path": null, + "max": 24.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 1.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 6.0 + } + }, + "d7d4d74246ea490da7610c277c802b99": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "AI Translate eligibility 🟡", + "layout": "IPY_MODEL_1bf396a231374a2097e03bf45fc874ff", + "layout_path": null, + "max": 1.0, + "min": 0.0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 0.01, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 0.01 + } + }, + "d99a04715b7b45acb195ec30a704dc05": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "db128adf84c844139dcfffac697fd6ed": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "low", + "mid", + "high" + ], + "disabled": false, + "hidden": false, + "label": "Impl hours (V2 range)", + "layout": "IPY_MODEL_d99a04715b7b45acb195ec30a704dc05", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "mid" + } + }, + "f6ffc6bbb4a04db8b59c069718cbf704": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Copilot includes ASIA sites", + "last_changed_at": "", + "layout": "IPY_MODEL_53869a7ea78b44f4a134983d354228f0", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": false + } + }, + "f9605eccf55f4358908dcc4ce6fdf2a6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "cell_id": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_a46ed7fed98548ddbf0b78433ba14c1c", + "layout_path": null, + "placeholder": "​", + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "style": "IPY_MODEL_aa1d6205d7b94e04abb3e7fdcf0311c1", + "tabbable": null, + "tooltip": null, + "value": "" + } + }, + "fdde38396a7d447eb57f94bc198f8e2f": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Auto-respond tokens per message 🟡", + "layout": "IPY_MODEL_bf7b6f3bca5f48e6838dc54f84943731", + "layout_path": null, + "max": 0.5, + "min": 0.0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 0.005, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 0.05 + } } }, "version_major": 2, diff --git a/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_no_current_state.ipynb b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_no_current_state.ipynb new file mode 100644 index 0000000..86721c9 --- /dev/null +++ b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_no_current_state.ipynb @@ -0,0 +1,11350 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "5f5ffd6a", + "metadata": {}, + "source": [ + "# CTM × Genesys CCaaS — Business Case (Current Spend Excluded)\n", + "\n", + "**Thesis:** the Genesys/Broadreach business case presents **$13.6M annual / $15.0M 3-year benefits**\n", + "against a cost case of **$4.3M/yr CCaaS licences + $2.4M professional services + $167K training** —\n", + "but every claimed benefit is produced by AI capabilities whose **token consumption** and\n", + "**implementation effort** are absent from that cost case. Per CTM's direction this notebook\n", + "**excludes any consideration of current spend** — no do-nothing baseline, no double-billing,\n", + "no cost-avoidance credit. It keeps Genesys's benefits **verbatim** and judges the programme on\n", + "its **absolute** cost: net = Genesys benefits − full programme cost.\n", + "\n", + "| Layer | Source | Treatment |\n", + "|---|---|---|\n", + "| Benefits | *Appendix 4 — CCaaS Platform Benefit Calculations (Consolidated).pptx*, slides 5–21 | **Verbatim** — phased by Genesys's own deployment schedule, scaled to the deck's 3-yr totals |\n", + "| Base costs | Same deck (TCO slides 5–6) + signed contract | Deck verbatim, plus the ramp programme; licences at the **contracted $3.2M/yr** (deck pitched $4.3M) |\n", + "| AI token consumption | `tokencalc` engine, published Genesys meter rates | **Added** — missing cost #1 |\n", + "| AI implementation effort | `docs/ctm_ai_labour_estimate_V2.md` | **Added** — missing cost #2 |\n", + "| Existing platform run-off | — | **Excluded** — current spend carries no weight (CTM directive) |\n", + "\n", + "Confidence legend: 🟢 confirmed (published/contractual) · 🟡 estimated (working assumption) · 🔴 unknown.\n", + "\n", + "*Frame note: the standalone Genesys programme is thin without a current-spend tailwind — the **virtual-agents alternative** (`ctm_business_case_virtual_agents.ipynb`) is the companion case that carries the ROI. AI implementation uses the V2 hours-range × blended-rate model; the activity-level\n", + "`ImplementationEffort` engine sketched in the labour doc is deferred (see section 11). Timeline = 2026–2028,\n", + "contract start Jan 2026; schedule per the current PM delivery timeline (NA UAT Oct 2026 … last go-live Nov 2027).*\n", + "\n", + "*This notebook is the deliverable: serve it interactively with `mercury --working-dir notebooks/`, tune the 🟡 inputs live for the client, then export an LLM-readable report source with `python scripts/export_report.py`.*" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "26632e8d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tokencalc loaded — 9 sites, 2,088 named users (contracted: 2,088)\n" + ] + } + ], + "source": [ + "# ── Setup ──────────────────────────────────────────────────────────\n", + "import sys, pathlib\n", + "_ROOT = pathlib.Path.cwd()\n", + "if not (_ROOT / \"tokencalc\").exists(): # notebook lives in notebooks/\n", + " _ROOT = _ROOT.parent\n", + "sys.path.insert(0, str(_ROOT))\n", + "\n", + "import dataclasses\n", + "import datetime as dt\n", + "import math\n", + "\n", + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "\n", + "import mercury as mr\n", + "\n", + "from tokencalc import *\n", + "# Single source of truth for the corrected case — all math lives in the\n", + "# library; only presentation (and Mercury input widgets) lives here.\n", + "from tokencalc.appendix4 import (\n", + " YEARS, YEAR_INDEX, REGIONS, CAPABILITIES,\n", + " VERBATIM_BENEFITS, SLIDE_TOTALS, TCO_VERBATIM,\n", + " IMPL_MONTH, REALIZE_MONTH, BENEFIT_LAG_MONTHS, NA_EMAIL_IMPL_MONTH,\n", + " AI_IMPL_HOURS, KB_READINESS_HOURS, STEADY_STATE_HOURS,\n", + " DEFAULT_RAMP_MONTHS, SMELL_TEST_FLOOR,\n", + " autorespond_meter, benefits_by_year, build_impl_costs, build_rollouts,\n", + " build_scopes, case_flows, case_kpis, claim_scenario,\n", + " crossfoot_tolerance, current_costs_by_year, current_months_in_year,\n", + " current_state_inputs, hours_pick, impl_feature_regions,\n", + " MANAGED_SERVICES_ANNUAL, MCX_GO_LIVE, PS_CONTRACTED_TOTAL, PS_MILESTONES,\n", + " licence_costs_by_year, managed_services_by_year, money, html_money,\n", + " month_label, ps_costs_by_year, ps_milestones_dataframe, region_agents,\n", + " region_site_names, tco, token_costs_by_year,\n", + " verbatim_dataframe,\n", + ")\n", + "from tokencalc.staging import backstage\n", + "\n", + "pd.options.display.float_format = \"{:,.0f}\".format\n", + "\n", + "# ── Chart chrome (dataviz reference palette, light surface) ─────────\n", + "INK, INK2, MUTED = \"#0b0b0b\", \"#52514e\", \"#898781\"\n", + "SURFACE, GRID, BASELINE = \"#fcfcfb\", \"#e1e0d9\", \"#c3c2b7\"\n", + "CUMULATIVE, CONTEXT = \"#52514e\", \"#c3c2b7\" # neutral line; de-emphasized context series\n", + "FONT_STACK = 'system-ui, -apple-system, \"Segoe UI\", sans-serif'\n", + "\n", + "# Fixed capability colors — color follows the entity across every figure.\n", + "CAP_COLOR = {\n", + " \"Agent Copilot\": \"#2a78d6\", # blue\n", + " \"WFM\": \"#1baf7a\", # aqua\n", + " \"Email\": \"#eda100\", # yellow (sub-3:1 → relief via total labels + tables)\n", + " \"STA\": \"#008300\", # green\n", + " \"Predictive Routing\": \"#4a3aa7\", # violet\n", + " \"Supervisor Copilot\": \"#e34948\", # red\n", + "}\n", + "COST_COLOR = {\n", + " \"CCaaS platform licences (contracted, ramp-adjusted)\": \"#2a78d6\",\n", + " \"Base professional services + training\": \"#1baf7a\",\n", + " \"NTT PS + training (SOW billing milestones)\": \"#1baf7a\",\n", + " \"NTT managed services (from MCX go-live)\": \"#0b6b4a\",\n", + " \"Existing platform (term-contract run-off)\": \"#eda100\",\n", + " \"AI token consumption\": \"#008300\",\n", + " \"AI implementation + KB readiness\": \"#4a3aa7\",\n", + " \"AI steady-state tuning\": \"#e34948\",\n", + "}\n", + "SEQ_BLUES = [\"#cde2fb\", \"#b7d3f6\", \"#9ec5f4\", \"#86b6ef\", \"#6da7ec\", \"#5598e7\",\n", + " \"#3987e5\", \"#2a78d6\", \"#256abf\", \"#1c5cab\", \"#184f95\", \"#104281\", \"#0d366b\"]\n", + "\n", + "\n", + "def tei_layout(fig, title, subtitle=None, height=460):\n", + " t = f\"{title}\"\n", + " if subtitle:\n", + " t += f\"
{subtitle}\"\n", + " fig.update_layout(\n", + " title=dict(text=t, font=dict(size=16, color=INK), x=0.02, xanchor=\"left\"),\n", + " paper_bgcolor=SURFACE, plot_bgcolor=SURFACE,\n", + " font=dict(family=FONT_STACK, size=12, color=INK2),\n", + " legend=dict(orientation=\"h\", yanchor=\"top\", y=-0.10, x=0,\n", + " font=dict(size=11, color=INK2)),\n", + " xaxis=dict(type=\"category\", showgrid=False, linecolor=BASELINE,\n", + " tickfont=dict(color=MUTED)),\n", + " yaxis=dict(gridcolor=GRID, zerolinecolor=BASELINE, zerolinewidth=1.5,\n", + " tickformat=\"$~s\", tickfont=dict(color=MUTED)),\n", + " hovermode=\"x unified\", bargap=0.45, height=height,\n", + " margin=dict(t=70, r=30, b=80, l=70),\n", + " )\n", + " return fig\n", + "\n", + "\n", + "def bar(x, y, name, color):\n", + " return go.Bar(x=x, y=y, name=name,\n", + " marker=dict(color=color, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "def cum_line(x, y, name, color=CUMULATIVE, dash=None):\n", + " return go.Scatter(x=x, y=y, name=name, mode=\"lines+markers\",\n", + " line=dict(color=color, width=2, dash=dash),\n", + " marker=dict(size=8, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "backstage(f\"tokencalc loaded — {len(CTM_DEFAULT_SITES)} sites, \"\n", + " f\"{sum(s.named_users for s in CTM_DEFAULT_SITES):,} named users \"\n", + " f\"(contracted: {CONTRACTED_NAMED_USERS:,})\")" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7b33a037", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "8927a9e1a74b44ca98a8429274fcca31", + "position": "sidebar", + "widget": "MarkdownWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "8927a9e1a74b44ca98a8429274fcca31", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "MarkdownWidget(value='
{label}'\n", + " for n, label in _TOC)\n", + "_toc = mr.Markdown(\n", + " text=(f'Jump to section'\n", + " f'
    {_items}
'),\n", + " position=\"sidebar\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "fda63800", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 1 · Contract inputs — data collection (current spend excluded)\n", + "\n", + "Verbatim anchors (Appendix 4, slide 5–6): current solution costs **$7.3M/yr globally**\n", + "(ANZ, APAC, EMEA, NA — $22M over 3 years); CCaaS is **$4.3M/yr** with **$2.4M professional\n", + "services + $167K training in year 1**. The **contracted** licence run-rate is\n", + "**$3.2M/yr** (🟢 signed) — the corrected stack runs on it; the as-pitched stack keeps\n", + "the deck's $4.3M so the comparison stays anchored to the Genesys report. The signed NTT\n", + "SOW also fixes **PS at $2.03M over four billing milestones (2026–27)** and adds **managed\n", + "services at $411K/yr from MCX go-live (30 Sep 2026)** — the corrected stack bills both as\n", + "contracted; the as-pitched stack keeps the deck's year-1 PS lump.\n", + "\n", + "**CTM directive:** current spend is *excluded* from this case. The per-region current-platform table below is retained **for reference only** and is forced to **$0** downstream — there is no double-billing line and no cost-avoidance credit. Two contract facts the deck's cost case ignores are still collected here as editable inputs:\n", + "\n", + "1. **Genesys ramp programme** — `RAMP_MONTHS = 6` (🟢 order form): licences are free for\n", + " the first 6 months, so the licence commit bills from July 2026.\n", + "2. **NTT SOW milestones + managed services** — PS billed on the SOW milestones (PM timeline:\n", + " Mar 2026 / Oct 2026 / Oct 2027 / Nov 2027) and managed services ($411K/yr) from MCX go-live\n", + " (16 Oct 2026).\n", + "\n", + "*(The current-platform table stays visible so real regional figures can be dropped in later if\n", + "CTM ever wants the cost-avoidance frame back — but it does not affect any number in this case.)*" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7e4e12ce", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "dbc3ef2687aa4a6d988f7d5e88431c5e", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "dbc3ef2687aa4a6d988f7d5e88431c5e", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "78509e85c62c4dae93d36794f5549dac", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "78509e85c62c4dae93d36794f5549dac", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "ed62020b820644b18b7a0f984f34fee2", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "ed62020b820644b18b7a0f984f34fee2", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "b1fb0830beea4568812c65a865f1992a", + "position": "inline", + "widget": "DateInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "b1fb0830beea4568812c65a865f1992a", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "b83e27df803a478bac5dd66a21ed9e59", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "b83e27df803a478bac5dd66a21ed9e59", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "ff3bc880230a4a4a9073c4bda91e77fe", + "position": "inline", + "widget": "DateInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "ff3bc880230a4a4a9073c4bda91e77fe", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "4ba0efa7dcef40a1ac07e393d07de624", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "4ba0efa7dcef40a1ac07e393d07de624", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "9e9fdf5751d544bd98d46d3528f2e4e8", + "position": "inline", + "widget": "DateInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "9e9fdf5751d544bd98d46d3528f2e4e8", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "a41c8514526e4130b3ce57f8494b85d8", + "position": "inline", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "a41c8514526e4130b3ce57f8494b85d8", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "78e58d47e33b413ca597ff326a9cb27b", + "position": "inline", + "widget": "DateInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "78e58d47e33b413ca597ff326a9cb27b", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Contract inputs (model: tokencalc.appendix4) ─────────────────────\n", + "# ⚙ Interactive when served with Mercury; headless runs (nbconvert /\n", + "# regression gate) use the widget defaults below.\n", + "# NB: Mercury re-executes only cells BELOW a changed widget's cell,\n", + "# so this cell constructs widgets ONLY — .value is read downstream.\n", + "_ramp_w = mr.NumberInput(\n", + " label=\"Genesys ramp — licence-free months\", value=DEFAULT_RAMP_MONTHS,\n", + " min=0, max=24, step=1, position=\"inline\")\n", + "_licence_w = mr.NumberInput(\n", + " label=\"CCaaS licence run-rate ($/yr) — contracted\",\n", + " value=round(tco(\"ccaas_annual\")), # signed; deck pitched $4.3M/yr\n", + " min=0, max=10_000_000, step=100_000, position=\"inline\")\n", + "\n", + "sites = list(CTM_DEFAULT_SITES)\n", + "ALL_SITES = [s.site_name for s in sites]\n", + "ASIA_SITES = [n for n in ALL_SITES if n.startswith(\"APAC\")]\n", + "REGION_SITES = region_site_names(sites)\n", + "agents_by_region = region_agents(sites)\n", + "\n", + "# ── Per-region current-state inputs (EDIT as real data arrives) ──────\n", + "# Seeded as $7.3M × agent share; annual_cost and contract_termination\n", + "# are the editable inputs.\n", + "_seed = current_state_inputs(sites)\n", + "assert abs(_seed[\"annual_cost\"].sum() - TCO_VERBATIM[\"current_annual\"]) < 1\n", + "\n", + "_cost_w, _term_w = {}, {}\n", + "for _r in REGIONS:\n", + " _cost_w[_r] = mr.NumberInput(\n", + " label=f\"{_r} — current platform cost ($/yr)\",\n", + " value=round(float(_seed.loc[_r, \"annual_cost\"])),\n", + " min=0, max=8_000_000, step=10_000, position=\"inline\")\n", + " _term_w[_r] = mr.DateInput(\n", + " label=f\"{_r} — contract termination\",\n", + " value=_seed.loc[_r, \"contract_termination\"].isoformat(),\n", + " position=\"inline\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "52d1e89f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "current-state total $7.3M/yr (seed: $7.3M; deck 3-yr $22.0M — deck rounding)\n", + "deck CCaaS 3-yr (no ramp) = $15.5M (deck: $15.4M)\n", + "licence run-rate: contracted $3.2M/yr (deck pitched $4.3M/yr — as-pitched stack keeps it)\n", + "ramp-adjusted licences by year: {2026: '$1.6M', 2027: '$3.2M', 2028: '$3.2M'}\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
agentsshareannual_costcontract_terminationconfidence
region
NA89003,348,9692027-12-31🟡 agent-share allocation of the verbatim total
ANZ1800677,3202027-12-31🟡 agent-share allocation of the verbatim total
EMEA32001,204,1242027-12-31🟡 agent-share allocation of the verbatim total
ASIA55002,069,5882027-12-31🟡 agent-share allocation of the verbatim total
\n", + "
" + ], + "text/plain": [ + " agents share annual_cost contract_termination \\\n", + "region \n", + "NA 890 0 3,348,969 2027-12-31 \n", + "ANZ 180 0 677,320 2027-12-31 \n", + "EMEA 320 0 1,204,124 2027-12-31 \n", + "ASIA 550 0 2,069,588 2027-12-31 \n", + "\n", + " confidence \n", + "region \n", + "NA 🟡 agent-share allocation of the verbatim total \n", + "ANZ 🟡 agent-share allocation of the verbatim total \n", + "EMEA 🟡 agent-share allocation of the verbatim total \n", + "ASIA 🟡 agent-share allocation of the verbatim total " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
data itemstatus
0Per-region current-platform contract values— reference only; excluded from the case
1Actual termination / renewal dates per region— excluded from this case (current spend zeroed)
2Genesys ramp period in the actual order form🟢 6 months per order form
3Contracted licence run-rate🟢 $3.2M/yr signed (deck pitched $4.3M/yr)
4NTT PS billing milestones (SOW)🟢 $2.03M over four milestones (PM timeline)
5NTT managed services🟢 $410,918/yr from MCX go-live (16 Oct 2026)
6Contracted token rate (vs $1.00 US list)🔴 not sourced — list rate used
7Non-NAM site volumes & AHTs🟡 tokencalc placeholders (defaults.py warning)
8Early-termination / co-term options on NICE IE...🔴 unknown
\n", + "
" + ], + "text/plain": [ + " data item \\\n", + "0 Per-region current-platform contract values \n", + "1 Actual termination / renewal dates per region \n", + "2 Genesys ramp period in the actual order form \n", + "3 Contracted licence run-rate \n", + "4 NTT PS billing milestones (SOW) \n", + "5 NTT managed services \n", + "6 Contracted token rate (vs $1.00 US list) \n", + "7 Non-NAM site volumes & AHTs \n", + "8 Early-termination / co-term options on NICE IE... \n", + "\n", + " status \n", + "0 — reference only; excluded from the case \n", + "1 — excluded from this case (current spend zeroed) \n", + "2 🟢 6 months per order form \n", + "3 🟢 $3.2M/yr signed (deck pitched $4.3M/yr) \n", + "4 🟢 $2.03M over four milestones (PM timeline) \n", + "5 🟢 $410,918/yr from MCX go-live (16 Oct 2026) \n", + "6 🔴 not sourced — list rate used \n", + "7 🟡 tokencalc placeholders (defaults.py warning) \n", + "8 🔴 unknown " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Contract values → model (re-runs on any change to the widgets above) ─\n", + "RAMP_MONTHS = int(_ramp_w.value)\n", + "LICENCE_ANNUAL = float(_licence_w.value)\n", + "\n", + "current_state = _seed.copy()\n", + "for _r in REGIONS:\n", + " current_state.loc[_r, \"annual_cost\"] = float(_cost_w[_r].value)\n", + " current_state.loc[_r, \"contract_termination\"] = dt.date.fromisoformat(\n", + " _term_w[_r].value)\n", + "\n", + "current_by_year = current_costs_by_year(current_state)\n", + "# ── CTM directive: EXCLUDE current spend from this business case ──────\n", + "# The programme is judged on its own absolute cost vs Genesys benefits —\n", + "# no do-nothing baseline, no double-billing, no cost-avoidance credit.\n", + "# The current-state table above is retained for reference only; it is\n", + "# forced to $0 here so it carries NO weight downstream.\n", + "current_by_year = {y: 0.0 for y in YEARS}\n", + "licence_by_year = licence_costs_by_year(RAMP_MONTHS, LICENCE_ANNUAL)\n", + "\n", + "backstage(f\"current-state total {money(current_state['annual_cost'].sum())}/yr \"\n", + " f\"(seed: {money(TCO_VERBATIM['current_annual'])}; deck 3-yr \"\n", + " f\"{money(TCO_VERBATIM['current_3yr'])} — deck rounding)\")\n", + "backstage(f\"deck CCaaS 3-yr (no ramp) = \"\n", + " f\"{money(3 * TCO_VERBATIM['ccaas_annual'] + TCO_VERBATIM['prof_services_y1'] + TCO_VERBATIM['training_y1'])} \"\n", + " f\"(deck: {money(TCO_VERBATIM['ccaas_3yr'])})\")\n", + "print(f\"licence run-rate: contracted {money(LICENCE_ANNUAL)}/yr \"\n", + " f\"(deck pitched {money(TCO_VERBATIM['ccaas_annual'])}/yr — as-pitched stack keeps it)\")\n", + "backstage(f\"ramp-adjusted licences by year: \"\n", + " f\"{ {y: money(v) for y, v in licence_by_year.items()} }\")\n", + "display(current_state)\n", + "\n", + "# ── Remaining data-collection checklist ──────────────────────────────\n", + "display(pd.DataFrame([\n", + " (\"Per-region current-platform contract values\", \"— reference only; excluded from the case\"),\n", + " (\"Actual termination / renewal dates per region\", \"— excluded from this case (current spend zeroed)\"),\n", + " (\"Genesys ramp period in the actual order form\", \"🟢 6 months per order form\"),\n", + " (\"Contracted licence run-rate\", \"🟢 $3.2M/yr signed (deck pitched $4.3M/yr)\"),\n", + " (\"NTT PS billing milestones (SOW)\", \"🟢 $2.03M over four milestones (PM timeline)\"),\n", + " (\"NTT managed services\", \"🟢 $410,918/yr from MCX go-live (16 Oct 2026)\"),\n", + " (\"Contracted token rate (vs $1.00 US list)\", \"🔴 not sourced — list rate used\"),\n", + " (\"Non-NAM site volumes & AHTs\", \"🟡 tokencalc placeholders (defaults.py warning)\"),\n", + " (\"Early-termination / co-term options on NICE IEX et al.\", \"🔴 unknown\"),\n", + "], columns=[\"data item\", \"status\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "0c74e449", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 2 · Verbatim Genesys benefits & deployment schedule\n", + "\n", + "Benefits are taken **verbatim** from Appendix 4 slides 12–15 (per-region × capability, annual and\n", + "3-yr values). The deck gives no per-year split, so each cell's 3-yr value is **phased by Genesys's\n", + "own deployment schedule** (slides 17–21): implementation months NA=18, ANZ=21, EMEA=24, ASIA=27,\n", + "and *\"business benefits start 3 months after implementation\"* — realization Sep 2027 / Dec 2027 /\n", + "Mar 2028 / Jun 2028. Exception: the NA Gantt shows **Email realizing Apr 2027** (implemented month 13).\n", + "\n", + "**Month convention:** `RolloutPlan.go_live_month = m` means active from calendar month *m+1*; the\n", + "deck's labels are inclusive (NA \"realizes Sep 2027\" ⇒ September counts), so rollout keys are set to\n", + "*label − 1*. Deck-implied realization windows differ from its own schedule by ±1 month in places —\n", + "the per-cell scaling below absorbs that; don't \"fix\" it.\n", + "\n", + "The result: **benefits in 2026 are $0 — by Genesys's own schedule.**" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c7e86e80", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "cross-foot OK — cell grain sums to $15.0M 3-yr / $13.7M annual (deck headline: $15.0M / $13.6M — deck rounding)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
regionANZASIAEMEANATOTAL
capability
Agent Copilot390000006400034000007364000
WFM140000091400068700003001000
Email1430009300023500025000002971000
STA10500072000131000506000814000
Predictive Routing302000510006000167000526000
Supervisor Copilot27000049000291000367000
TOTAL587700011300001172000686400015043000
\n", + "
" + ], + "text/plain": [ + "region ANZ ASIA EMEA NA TOTAL\n", + "capability \n", + "Agent Copilot 3900000 0 64000 3400000 7364000\n", + "WFM 1400000 914000 687000 0 3001000\n", + "Email 143000 93000 235000 2500000 2971000\n", + "STA 105000 72000 131000 506000 814000\n", + "Predictive Routing 302000 51000 6000 167000 526000\n", + "Supervisor Copilot 27000 0 49000 291000 367000\n", + "TOTAL 5877000 1130000 1172000 6864000 15043000" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── VERBATIM Appendix 4 slides 12-15 (data: tokencalc.appendix4) ─────\n", + "verbatim = verbatim_dataframe()\n", + "\n", + "# Deck rounds to $0.1M — its own tables cross-foot ±$50-120K.\n", + "_tol = crossfoot_tolerance\n", + "\n", + "for r, expect in SLIDE_TOTALS[\"regional_3yr\"].items():\n", + " got = verbatim.loc[verbatim.region == r, \"three_yr\"].sum()\n", + " assert abs(got - expect) <= _tol(expect), (r, got, expect)\n", + "for c, expect in SLIDE_TOTALS[\"capability_3yr\"].items():\n", + " got = verbatim.loc[verbatim.capability == c, \"three_yr\"].sum()\n", + " assert abs(got - expect) <= _tol(expect), (c, got, expect)\n", + "assert abs(verbatim[\"three_yr\"].sum() - SLIDE_TOTALS[\"total_3yr\"]) <= _tol(15e6)\n", + "assert abs(verbatim[\"annual\"].sum() - SLIDE_TOTALS[\"total_annual\"]) <= _tol(13.6e6)\n", + "backstage(f\"cross-foot OK — cell grain sums to {money(verbatim['three_yr'].sum())} 3-yr / \"\n", + " f\"{money(verbatim['annual'].sum())} annual (deck headline: $15.0M / $13.6M — deck rounding)\")\n", + "\n", + "display(verbatim.pivot_table(index=\"capability\", columns=\"region\", values=\"three_yr\",\n", + " aggfunc=\"sum\", margins=True, margins_name=\"TOTAL\")\n", + " .reindex([*CAPABILITIES, \"TOTAL\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "5ace8c50", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "17cb8e9cdcb1451e9ba532a01a3f9a2e", + "position": "sidebar", + "widget": "CheckboxWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "17cb8e9cdcb1451e9ba532a01a3f9a2e", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Schedule option (Mercury sidebar — widgets only, no output) ──────\n", + "# NB: Mercury re-executes only cells BELOW a changed widget's cell,\n", + "# so this cell constructs widgets ONLY — .value is read downstream.\n", + "_na_email_w = mr.CheckBox(label=\"NA Email implements early (Jan 2027)\", value=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "fe3572d2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "NA Email exception: implemented Oct 2026, realizes Jan 2027\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
implementedbenefits realizetoken months 2026token months 2027token months 2028benefit months 2026benefit months 2027benefit months 2028
region
NAOct 2026 (m10)Jan 2027 (m13)3121201212
ANZJun 2027 (m18)Sep 2027 (m21)07120412
EMEAFeb 2027 (m14)May 2027 (m17)011120812
ASIAOct 2027 (m22)Jan 2028 (m25)03120012
\n", + "
" + ], + "text/plain": [ + " implemented benefits realize token months 2026 token months 2027 \\\n", + "region \n", + "NA Oct 2026 (m10) Jan 2027 (m13) 3 12 \n", + "ANZ Jun 2027 (m18) Sep 2027 (m21) 0 7 \n", + "EMEA Feb 2027 (m14) May 2027 (m17) 0 11 \n", + "ASIA Oct 2027 (m22) Jan 2028 (m25) 0 3 \n", + "\n", + " token months 2028 benefit months 2026 benefit months 2027 \\\n", + "region \n", + "NA 12 0 12 \n", + "ANZ 12 0 4 \n", + "EMEA 12 0 8 \n", + "ASIA 12 0 0 \n", + "\n", + " benefit months 2028 \n", + "region \n", + "NA 12 \n", + "ANZ 12 \n", + "EMEA 12 \n", + "ASIA 12 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Genesys/Broadreach deployment schedule (slides 17-21) ────────────\n", + "NA_EMAIL_EARLY = bool(_na_email_w.value)\n", + "# IMPL_MONTH = {NA: 18, ANZ: 21, EMEA: 24, ASIA: 27}; benefits realize\n", + "# +3 months; NA Email implements early (Jan 2027, realizes Apr 2027).\n", + "# go_live_month = label − 1 so the labelled month is included (section 2 note).\n", + "\n", + "TOKEN_ROLLOUT, EMAIL_TOKEN_ROLLOUT, BENEFIT_ROLLOUT = build_rollouts(\n", + " sites, NA_EMAIL_EARLY, RAMP_MONTHS)\n", + "\n", + "schedule = pd.DataFrame([\n", + " {\n", + " \"region\": r,\n", + " \"implemented\": f\"{month_label(IMPL_MONTH[r])} (m{IMPL_MONTH[r]})\",\n", + " \"benefits realize\": f\"{month_label(REALIZE_MONTH[r])} (m{REALIZE_MONTH[r]})\",\n", + " **{f\"token months {y}\": TOKEN_ROLLOUT.live_months_in_year(REGION_SITES[r][0], YEAR_INDEX[y])\n", + " for y in YEARS},\n", + " **{f\"benefit months {y}\": BENEFIT_ROLLOUT.live_months_in_year(r, YEAR_INDEX[y])\n", + " for y in YEARS},\n", + " }\n", + " for r in REGIONS\n", + "]).set_index(\"region\")\n", + "if NA_EMAIL_EARLY:\n", + " backstage(f\"NA Email exception: implemented {month_label(NA_EMAIL_IMPL_MONTH)}, \"\n", + " f\"realizes {month_label(NA_EMAIL_IMPL_MONTH + BENEFIT_LAG_MONTHS)}\")\n", + "display(schedule)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "a06e90b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "benefits by year: {2026: '$0K', 2027: '$5.4M', 2028: '$9.7M'}\n", + "⚠ 2026 benefits are $0 — the honest consequence of the deck's own deployment schedule.\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
year202620272028TOTAL
capability
Agent Copilot02,700,6004,663,4007,364,000
WFM0624,8002,376,2003,001,000
Email01,379,7501,591,2502,971,000
STA0331,650482,350814,000
Predictive Routing0161,400364,600526,000
Supervisor Copilot0171,850195,150367,000
TOTAL05,370,0509,672,95015,043,000
\n", + "
" + ], + "text/plain": [ + "year 2026 2027 2028 TOTAL\n", + "capability \n", + "Agent Copilot 0 2,700,600 4,663,400 7,364,000\n", + "WFM 0 624,800 2,376,200 3,001,000\n", + "Email 0 1,379,750 1,591,250 2,971,000\n", + "STA 0 331,650 482,350 814,000\n", + "Predictive Routing 0 161,400 364,600 526,000\n", + "Supervisor Copilot 0 171,850 195,150 367,000\n", + "TOTAL 0 5,370,050 9,672,950 15,043,000" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Phase each verbatim 3-yr value by its region's realization window ─\n", + "benefits_long = benefits_by_year(BENEFIT_ROLLOUT, NA_EMAIL_EARLY)\n", + "benefit_total_by_year = benefits_long.groupby(\"year\")[\"benefit\"].sum().to_dict()\n", + "\n", + "# Scaling is at the finest grain, so every verbatim total is reproduced exactly.\n", + "assert benefit_total_by_year[2026] == 0.0, \"2026 must be $0 under Genesys's own schedule\"\n", + "for (region, cap), (_, three_yr) in VERBATIM_BENEFITS.items():\n", + " got = benefits_long.query(\"region == @region and capability == @cap\")[\"benefit\"].sum()\n", + " assert abs(got - three_yr) < 1e-6, (region, cap)\n", + "\n", + "backstage(f\"benefits by year: { {y: money(v) for y, v in benefit_total_by_year.items()} }\")\n", + "print(\"⚠ 2026 benefits are $0 — the honest consequence of the deck's own deployment schedule.\")\n", + "display(benefits_long.pivot_table(index=\"capability\", columns=\"year\", values=\"benefit\",\n", + " aggfunc=\"sum\", margins=True, margins_name=\"TOTAL\")\n", + " .reindex([*CAPABILITIES, \"TOTAL\"]))" + ] + }, + { + "cell_type": "markdown", + "id": "3b9174ab", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 3 · Feature enablement → token consumption (missing cost #1)\n", + "\n", + "Every deck capability maps to Genesys AI Experience token meters (all 🟢 published rates unless\n", + "noted), consumed from each region's **implementation month** — three months *before* benefits:\n", + "\n", + "| Deck capability | Token meter(s) | Treatment |\n", + "|---|---|---|\n", + "| Agent Copilot | Agent Copilot **[named]** — 40 tokens/user/mo | Per V2 correction #1, this **includes email/chat Auto-Suggest** and interaction summarization. Scoped to NA/ANZ/EMEA by default (ASIA claims $0 Copilot benefit — toggle below). |\n", + "| Email | Email AI (**Auto-Respond**) — per message | 🔴→🟡 rate not published; working assumption below (anchored to Genesys Cloud Copilot's 20 AI actions/token), deck's 25.5% auto-respond rate. Sensitivity in section 9. |\n", + "| STA | Speech & Text Analytics **[named]** — 30 tokens/user/mo | All sites. |\n", + "| Predictive Routing | Predictive Routing — 17 routed interactions/token | All sites; `PR_ELIGIBILITY` scopes to PR-enabled queue share. |\n", + "| Supervisor Copilot | AI Summary & Insights (**$0 by Rule 1** — Copilot's rate already covers summarization) + AI Translate as small-volume proxy | Kept visible to show the coverage rule. |\n", + "| WFM | **No token meter** — WEM is licence-included | $0 row; impl effort assumed inside the base $2.4M PS (no V2 line). |\n", + "\n", + "Consumption uses a **claim-level scenario** (no maturity haircut — Genesys claims none) and bills\n", + "with Genesys's monthly `ceil` token rounding, per-region pricing at $1.00/token US list (🔴 contracted\n", + "rates not sourced)." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "977418e1", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "6b80ee9107a6483c843063b90b59341e", + "position": "sidebar", + "widget": "CheckboxWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "6b80ee9107a6483c843063b90b59341e", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "213fedc568424f12b2160bf25c4ba94e", + "position": "sidebar", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "213fedc568424f12b2160bf25c4ba94e", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "01f8dbc02c7742108ef578d56d29197c", + "position": "sidebar", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "01f8dbc02c7742108ef578d56d29197c", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "1e617074f6f74576b7639ac100ccf60a", + "position": "sidebar", + "widget": "SliderWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "1e617074f6f74576b7639ac100ccf60a", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "52b0cc956e1e44dbb9df55d00d4395f8", + "position": "sidebar", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "52b0cc956e1e44dbb9df55d00d4395f8", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── CONFIG — token model knobs (Mercury sidebar; defaults when headless) ─\n", + "# NB: Mercury re-executes only cells BELOW a changed widget's cell,\n", + "# so this cell constructs widgets ONLY — .value is read downstream.\n", + "_copilot_asia_w = mr.CheckBox(\n", + " label=\"Copilot includes ASIA sites\", value=False)\n", + "# deck claims $0 Copilot benefit in ASIA → excluded by default\n", + "_email_rate_w = mr.NumberInput(\n", + " label=\"Email auto-respond rate\", value=0.255,\n", + " min=0.0, max=0.6, step=0.005)\n", + "# deck: \"Reduced Email Interactions with Auto-Respond 25.5%\"\n", + "_email_tokens_w = mr.NumberInput(\n", + " label=\"Auto-respond tokens per message 🟡\", value=0.05,\n", + " min=0.0, max=0.5, step=0.005)\n", + "# 🟡 ≈ one AI action per generated response (20 actions/token)\n", + "_pr_elig_w = mr.Slider(\n", + " label=\"Predictive Routing eligibility (%)\", value=100,\n", + " min=0, max=100)\n", + "# share of voice volume on PR-enabled queues\n", + "_ai_translate_w = mr.NumberInput(\n", + " label=\"AI Translate eligibility 🟡\", value=0.01,\n", + " min=0.0, max=1.0, step=0.01)\n", + "# 🟡 supervisor-evaluation slice of interactions" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9767177f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
featuremeter_typeunits_per_tokentokens_per_unitconfidencenotes
0Agent Copilot [named]per_user_per_monthNaN40🟢 confirmed40 tokens per named user per month. Includes i...
1Speech & Text Analytics [named]per_user_per_monthNaN30🟢 confirmedSTA named licence; 30 tokens per named user pe...
2Predictive Routingper_interaction170🟢 confirmedPredictive routing; 17 routes per token.
3AI Summary & Insightsper_summary500🟢 confirmedSupervisor standalone summarization; 50 summar...
4AI Translateper_interaction20🟢 confirmedAI translation; 2 translations per token.
5Email AI (Auto-Respond)per_message200🟡 estimatedWORKING ASSUMPTION — rate unpublished; ≈1 AI a...
\n", + "
" + ], + "text/plain": [ + " feature meter_type units_per_token \\\n", + "0 Agent Copilot [named] per_user_per_month NaN \n", + "1 Speech & Text Analytics [named] per_user_per_month NaN \n", + "2 Predictive Routing per_interaction 17 \n", + "3 AI Summary & Insights per_summary 50 \n", + "4 AI Translate per_interaction 2 \n", + "5 Email AI (Auto-Respond) per_message 20 \n", + "\n", + " tokens_per_unit confidence \\\n", + "0 40 🟢 confirmed \n", + "1 30 🟢 confirmed \n", + "2 0 🟢 confirmed \n", + "3 0 🟢 confirmed \n", + "4 0 🟢 confirmed \n", + "5 0 🟡 estimated \n", + "\n", + " notes \n", + "0 40 tokens per named user per month. Includes i... \n", + "1 STA named licence; 30 tokens per named user pe... \n", + "2 Predictive routing; 17 routes per token. \n", + "3 Supervisor standalone summarization; 50 summar... \n", + "4 AI translation; 2 translations per token. \n", + "5 WORKING ASSUMPTION — rate unpublished; ≈1 AI a... " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Claim-level scenario, meter override & scopes (tokencalc.appendix4) ─\n", + "# No adoption_curve on any scope — a curve would silently override the\n", + "# claim scenario's flat consumption realization.\n", + "COPILOT_INCLUDES_ASIA = bool(_copilot_asia_w.value)\n", + "EMAIL_AUTO_RESPOND_RATE = float(_email_rate_w.value)\n", + "EMAIL_AUTORESPOND_TOKENS_PER_MSG = float(_email_tokens_w.value)\n", + "PR_ELIGIBILITY = _pr_elig_w.value / 100\n", + "AI_TRANSLATE_ELIGIBILITY = float(_ai_translate_w.value)\n", + "USE_CONTRACTED_RATES = False # no contracted token rate sourced yet\n", + "\n", + "GENESYS_CLAIM = claim_scenario(EMAIL_AUTO_RESPOND_RATE)\n", + "AUTORESPOND_METER = autorespond_meter(EMAIL_AUTORESPOND_TOKENS_PER_MSG)\n", + "METERS = {**DEFAULT_METERS, \"Email AI (Auto-Respond)\": AUTORESPOND_METER}\n", + "CORE_SCOPES, EMAIL_SCOPES = build_scopes(\n", + " sites, COPILOT_INCLUDES_ASIA, PR_ELIGIBILITY, AI_TRANSLATE_ELIGIBILITY)\n", + "\n", + "USED_FEATURES = [sc.feature for sc in CORE_SCOPES + EMAIL_SCOPES]\n", + "display(meters_dataframe({f: METERS[f] for f in USED_FEATURES})\n", + " .drop(columns=[\"source\"]))" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3f3102f4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI token consumption (missing cost #1): {2026: '$424K', 2027: '$2.7M', 2028: '$3.5M'} → 3-yr $6.6M\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
yearconf2026202720283-yr
cost_line
Predictive Routing🟢214,2991,362,3231,851,8163,428,438
Agent Copilot [named]🟢114,000662,400715,2001,491,600
Speech & Text Analytics [named]🟢85,500550,620751,6801,387,800
Email AI (Auto-Respond)🟡10,55167,08091,500169,131
AI Translate🟢034,18569,540103,725
AI Summary & Insights🟢0000
WFM (no token meter — licence-included)🟢0000
TOTAL424,3502,676,6083,479,7366,580,694
\n", + "
" + ], + "text/plain": [ + "year conf 2026 2027 2028 \\\n", + "cost_line \n", + "Predictive Routing 🟢 214,299 1,362,323 1,851,816 \n", + "Agent Copilot [named] 🟢 114,000 662,400 715,200 \n", + "Speech & Text Analytics [named] 🟢 85,500 550,620 751,680 \n", + "Email AI (Auto-Respond) 🟡 10,551 67,080 91,500 \n", + "AI Translate 🟢 0 34,185 69,540 \n", + "AI Summary & Insights 🟢 0 0 0 \n", + "WFM (no token meter — licence-included) 🟢 0 0 0 \n", + "TOTAL 424,350 2,676,608 3,479,736 \n", + "\n", + "year 3-yr \n", + "cost_line \n", + "Predictive Routing 3,428,438 \n", + "Agent Copilot [named] 1,491,600 \n", + "Speech & Text Analytics [named] 1,387,800 \n", + "Email AI (Auto-Respond) 169,131 \n", + "AI Translate 103,725 \n", + "AI Summary & Insights 0 \n", + "WFM (no token meter — licence-included) 0 \n", + "TOTAL 6,580,694 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Token cost by year — engine call, rollout-gated ──────────────────\n", + "tokens_long = token_costs_by_year(\n", + " sites, METERS, DEFAULT_PRICING, GENESYS_CLAIM, CORE_SCOPES, EMAIL_SCOPES,\n", + " TOKEN_ROLLOUT, EMAIL_TOKEN_ROLLOUT, use_contracted=USE_CONTRACTED_RATES)\n", + "\n", + "tokens_pivot = tokens_long.pivot_table(index=\"cost_line\", columns=\"year\",\n", + " values=\"annual_cost\", aggfunc=\"sum\")\n", + "tokens_pivot.loc[\"WFM (no token meter — licence-included)\"] = [0.0, 0.0, 0.0]\n", + "tokens_pivot[\"3-yr\"] = tokens_pivot.sum(axis=1)\n", + "tokens_pivot = tokens_pivot.sort_values(\"3-yr\", ascending=False)\n", + "tokens_pivot.loc[\"TOTAL\"] = tokens_pivot.sum()\n", + "token_total_by_year = {y: float(tokens_pivot.loc[\"TOTAL\", y]) for y in YEARS}\n", + "\n", + "conf = {f: METERS[f].confidence.icon for f in USED_FEATURES}\n", + "tokens_pivot.insert(0, \"conf\", [conf.get(i, \"🟢\") for i in tokens_pivot.index[:-1]] + [\"\"])\n", + "backstage(f\"AI token consumption (missing cost #1): \"\n", + " f\"{ {y: money(v) for y, v in token_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(token_total_by_year.values()))}\")\n", + "display(tokens_pivot)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e62ecfa7", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agent Copilot [named]", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 114000, + 662400, + 715200 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Speech & Text Analytics [named]", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 85500, + 550620, + 751680 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Predictive Routing", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 214299, + 1362323, + 1851816 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#eda100", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Email AI (Auto-Respond)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 10551, + 67080, + 91500 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI Translate", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 34185, + 69540 + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$424K", + "x": 0, + "y": 424350, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$2.7M", + "x": 1, + "y": 2676608, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$3.5M", + "x": 2, + "y": 3479736, + "yshift": 12 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 460, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "AI Experience token consumption — absent from the Genesys cost case
Claim-level usage · published meter rates · $1.00/token US list · consumption starts at each region's implementation month", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "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7zxISp0FAABAbAg2sPx4pNWseZ1rSEaKDCxXrlxu277/zv28a9dOa9vuZte7Kr5GyrYfvre1a8/toal/BCscnTBhrFtWqlRZ9w9o73NbvvnaZs+eYfnzn5lzTw4f/s1u73Kna1TpH8rqmfP9d1tty5av3T/U1XDLnDmz620g+gd8lSrVXaMxfHs8CQWW27adadRVqlTVKlxTMRQ++O27GsoD3u1rDz78hFsuXjByRZYr7J3+b1nxsCD410MHrU3bDpYzV67QsunTJlvGDBnO68UVXqaiobp79ux2PQwTCi4i93falElWslQpK39VhXNeC1//xE/HWYYMGa1KteqhuQ4jv18PU2jVqp0r5/j2S/MI5smbN9QzKb45LCMbqGqIqwegwiQ1nhS4KXRNqC5EBhXqfRTfdv3626/nNf7DRe6r6oPq6oFffrHTroG21R557M++jdGE6p5fuYZ7f0A/a9T4BitTplxomezbu8e++GKJazRXrFjZHbtwiQWW77+nXpMRgWWlyq5H0fp1a61tWGDZr+8brlGueq4GrnopVql65rhENly3bNls69eusXbqGXeW1n311RVc76K3+7xuWbNlt1at2rhQRT6bO8v1MNNyUQ+qvHnyuvoeWQ8UJuTIkTMU/nviDSybt3K9bUU9pxTAqNxUZpE9v/wCS32v3zGKrFvhVCcGf/Ceq58KPjzaT/VKrXzt/+baU50c8F6/c8LjL1ettJ9//tEFupH1zjvH1GPKb7sSOu/D13XmHOpr9953/nU4ssy1P+PHj7G77r7Hd7+99WbLmu2C98Xjd231CyzjO/8iQzXx22a/sgvndy3XfSyyvodLqB7rXqS5NFXuuubpATEaQq0wtHXbm86bhiD8Oqw/vGnkgM7BcJH7mtD1bfXqlW67qp8N4eILLLUP2XPkdFNFePQHLm9kQfj3KZRST3lNHxEusbINrwdJ+beAeukmVCfDA8uErrVJrf9JXYeE7nlZsiS5jvrdI8L3T3WiRas2od6JP/643z3QRtfyyHKI73qY1HK90O/yOw+SUmcBAAAQGwINLPUPVQ290jCnSOENn6+/3mibNm5wTzfV+zUcu0rV6la0aLF4GymRjS79I1k95aqGNZg83ue++eZrF4q0j+epph8Oft8633qHG1qo3h9qNKkxoX+Ya/hppPDt8SjcGDzovVADRI0PL7D0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Xn179NFHrEiRom5Zhw43Wf78+a1379728suvWNGiRW3NmtWWNm1a93r37j1s7Nix1qZNGxs+fJhbBuDSIbAEAAAAAAApwuLFS6xly5b22muvWc+ePdw2HzhwwHLkyOF+VqD5888/29Chw+yFF16wGTNmWO3atc7Zt88//9zatWtvq1attFKlSoWWy6OPPmrDhg23EydO2JAhH1r79u1t586dVrFiJddTs3r1am6dAC4tAksAAAAAAJAibNy40WrXvs6qVLnWXn31NatZs8Y52z1y5Ejr1es+9/Ptt99u77zT/+wr/6OekuoxqdevueYa+9OfuoUCz65du9mGDRssX768ljZtOps8eZLrdTl48IdWuXJlN5x85coVZ9cE4FIhsAQAAAAAAClGnz597dlnn7Xjx49b48aNrX//flaoUCG3/Zrf8osvvrDRo8fYhAkT7P33B9gtt9xyzr4tWLDQRowYYd9+u8WWLFlqhQsXtgUL5lvu3Lmtdes2dvjw79arVy+7995etnDhAvdwn27dutq2bdtt2rRp7kE/AC4tAksAAAAAAJCiaA7Lt99+2/r3f8dKly5tixcvCs03KadOnbLSpctYw4YNbfDgD0LLI02cONH1qnz99detR4/uVrduPcubN6+NGTPaKlS4xvLkyWObNm2ydevW2ptvvmnvvz/Qfv75p9DnAVwaBJYAAAAAACBF+vvf/259+77tAsVixYqF9kFzWZYocaV7gviHHw62n376yQWa3tBvzzfffGPVq9dw6/nLX/7s5qqsVKmSffTRCPeE8TfeeNOtY/ToUa5X5+uv/9d27NhuWbNmDa0DwMVHYAkAAAAAAFKENWvWWO/eL7ong+fIkd2FleptuXXrt/a3v/3NDh8+4oZ4z5kz2774YpkNGPCeGxJerlx5y5Qpk61du8aefPJJy5Ili2XOnNkmTJjo5sWcM2eOVatW1YWcTZs2tUGDBrr5KtX78oUXnndDz1955VX3IJ9NmzZawYIFU0R5ASkVgSUAAAAAAEgRFi1abE899ZQLGU+ePOmGg7/yyssuZHz66adt1KjRtn//fhda9uzZ0x599BG3X02aNLHMmbO4h+j06NHT5s6dawcPHnSff+qpJ0PzXObJk9c6d+7s5sWMpHBUPTFXrFhuZcqUCS0HcPERWAIAAAAAgBRl1qxZ7mE4egBOzpw5U9S2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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Chart: token cost by meter per year (stacked) ────────────────────\n", + "TOKEN_LINE_COLOR = { # entity colors consistent with the capability palette\n", + " \"Agent Copilot [named]\": CAP_COLOR[\"Agent Copilot\"],\n", + " \"Speech & Text Analytics [named]\": CAP_COLOR[\"STA\"],\n", + " \"Predictive Routing\": CAP_COLOR[\"Predictive Routing\"],\n", + " \"Email AI (Auto-Respond)\": CAP_COLOR[\"Email\"],\n", + " \"AI Translate\": CAP_COLOR[\"Supervisor Copilot\"],\n", + " \"AI Summary & Insights\": \"#e87ba4\",\n", + "}\n", + "X = [str(y) for y in YEARS]\n", + "fig = go.Figure()\n", + "for line, color in TOKEN_LINE_COLOR.items():\n", + " vals = [tokens_long.query(\"cost_line == @line and year == @y\")[\"annual_cost\"].sum()\n", + " for y in YEARS]\n", + " if sum(vals) > 0:\n", + " fig.add_trace(bar(X, vals, line, color))\n", + "for i, y in enumerate(YEARS): # annotations anchor by category index, not label\n", + " total = token_total_by_year[y]\n", + " fig.add_annotation(x=i, y=total, text=f\"{money(total)}\", showarrow=False,\n", + " yshift=12, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"AI Experience token consumption — absent from the Genesys cost case\",\n", + " \"Claim-level usage · published meter rates · $1.00/token US list · \"\n", + " \"consumption starts at each region's implementation month\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bf0108e5", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 4 · AI implementation effort (missing cost #2)\n", + "\n", + "The deck's $2.4M professional services covers **base platform implementation only** — none of the\n", + "five AI capabilities are turn-key. Hours below are the **V2 corrected LoE**\n", + "(`docs/ctm_ai_labour_estimate_V2.md`; V1 was vendor-inflated ~4×): configuration, tuning cycles and\n", + "enablement at CTM scale, not custom development. Per V2 correction #1, Agent Copilot's line\n", + "includes email/chat Auto-Suggest — one implementation.\n", + "\n", + "**Model (deliberately simple — the activity-level engine is deferred):**\n", + "hours(feature) × blended rate, allocated to each feature's scoped regions by agent share, spread\n", + "uniformly from contract start to that region's implementation month; **KB readiness** is a\n", + "separately-flagged prerequisite project; **steady-state** is an absolute 500–900 h/yr\n", + "(tuning, KB refresh, drift correction) booked program-level in 2027–2028." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3b395fbd", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "5971e8eb0b88489b844e79389b1aa454", + "position": "sidebar", + "widget": "SelectWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "5971e8eb0b88489b844e79389b1aa454", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "b9ff942d6df348acae9bab10a224f717", + "position": "sidebar", + "widget": "SelectWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "b9ff942d6df348acae9bab10a224f717", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "427872643bae4ee49202bef61c18b6dd", + "position": "sidebar", + "widget": "CheckboxWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "427872643bae4ee49202bef61c18b6dd", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── CONFIG — AI implementation (V2 LoE; Mercury sidebar, widgets only) ─\n", + "# Hours ranges live in tokencalc.appendix4 (AI_IMPL_HOURS / KB_READINESS_HOURS\n", + "# / STEADY_STATE_HOURS), sourced from docs/ctm_ai_labour_estimate_V2.md.\n", + "# NB: Mercury re-executes only cells BELOW a changed widget's cell,\n", + "# so this cell constructs widgets ONLY — .value is read downstream.\n", + "_hours_mode_w = mr.Select(label=\"Impl hours (V2 range)\", value=\"mid\",\n", + " choices=[\"low\", \"mid\", \"high\"])\n", + "_blended_rate_w = mr.Select( # 175 offshore-heavy | 225 typical | 275 onshore\n", + " label=\"Blended rate ($/h)\", value=\"225\",\n", + " choices=[\"175\", \"225\", \"275\"])\n", + "_kb_w = mr.CheckBox(label=\"Include KB readiness (500–1,500 h)\", value=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "23756537", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
workstreamregionslow hhigh h
0Agent CopilotNA, ANZ, EMEA12001800
1Email Auto-RespondNA, ANZ, EMEA, ASIA8001400
2STANA, ANZ, EMEA, ASIA8001200
3Supervisor CopilotNA, ANZ, EMEA200400
4Predictive RoutingNA, ANZ, EMEA, ASIA400700
5Cross-cutting (PM, governance, testing, integr...NA, ANZ, EMEA, ASIA10001800
6KB readiness (prerequisite)NA, ANZ, EMEA, ASIA5001500
7Steady-state (h/yr, 2027-28)NA, ANZ, EMEA, ASIA500900
\n", + "
" + ], + "text/plain": [ + " workstream regions \\\n", + "0 Agent Copilot NA, ANZ, EMEA \n", + "1 Email Auto-Respond NA, ANZ, EMEA, ASIA \n", + "2 STA NA, ANZ, EMEA, ASIA \n", + "3 Supervisor Copilot NA, ANZ, EMEA \n", + "4 Predictive Routing NA, ANZ, EMEA, ASIA \n", + "5 Cross-cutting (PM, governance, testing, integr... NA, ANZ, EMEA, ASIA \n", + "6 KB readiness (prerequisite) NA, ANZ, EMEA, ASIA \n", + "7 Steady-state (h/yr, 2027-28) NA, ANZ, EMEA, ASIA \n", + "\n", + " low h high h \n", + "0 1200 1800 \n", + "1 800 1400 \n", + "2 800 1200 \n", + "3 200 400 \n", + "4 400 700 \n", + "5 1000 1800 \n", + "6 500 1500 \n", + "7 500 900 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI implementation (mid, $225/h): Y1 LoE $1.3M + KB readiness $225K + steady-state $315K (2027-28)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
hourscost202620272028
workstream
Agent Copilot1,500337,500311,83225,6680
Cross-cutting (PM, governance, testing, integration)1,400315,000257,24257,7580
Email Auto-Respond1,100247,500202,11945,3810
KB readiness (prerequisite)1,000225,000183,74441,2560
Predictive Routing550123,750101,05922,6910
STA1,000225,000183,74441,2560
Supervisor Copilot30067,50062,3665,1340
Steady-state tuning (2027-28)1,400315,0000157,500157,500
TOTAL8,2501,856,2501,302,108396,642157,500
\n", + "
" + ], + "text/plain": [ + " hours cost 2026 \\\n", + "workstream \n", + "Agent Copilot 1,500 337,500 311,832 \n", + "Cross-cutting (PM, governance, testing, integra... 1,400 315,000 257,242 \n", + "Email Auto-Respond 1,100 247,500 202,119 \n", + "KB readiness (prerequisite) 1,000 225,000 183,744 \n", + "Predictive Routing 550 123,750 101,059 \n", + "STA 1,000 225,000 183,744 \n", + "Supervisor Copilot 300 67,500 62,366 \n", + "Steady-state tuning (2027-28) 1,400 315,000 0 \n", + "TOTAL 8,250 1,856,250 1,302,108 \n", + "\n", + " 2027 2028 \n", + "workstream \n", + "Agent Copilot 25,668 0 \n", + "Cross-cutting (PM, governance, testing, integra... 57,758 0 \n", + "Email Auto-Respond 45,381 0 \n", + "KB readiness (prerequisite) 41,256 0 \n", + "Predictive Routing 22,691 0 \n", + "STA 41,256 0 \n", + "Supervisor Copilot 5,134 0 \n", + "Steady-state tuning (2027-28) 157,500 157,500 \n", + "TOTAL 396,642 157,500 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── V2 hours-range × rate model (tokencalc.appendix4.build_impl_costs) ─\n", + "# Swap point for the future activity-level LoE engine.\n", + "HOURS_MODE = str(_hours_mode_w.value)\n", + "BLENDED_RATE = int(_blended_rate_w.value)\n", + "INCLUDE_KB_READINESS = bool(_kb_w.value)\n", + "\n", + "_regions = impl_feature_regions(COPILOT_INCLUDES_ASIA)\n", + "display(pd.DataFrame(\n", + " [{\"workstream\": f, \"regions\": \", \".join(_regions.get(f, REGIONS)),\n", + " \"low h\": lo, \"high h\": hi}\n", + " for f, (lo, hi) in {**AI_IMPL_HOURS,\n", + " \"KB readiness (prerequisite)\": KB_READINESS_HOURS,\n", + " \"Steady-state (h/yr, 2027-28)\": STEADY_STATE_HOURS}.items()]))\n", + "\n", + "impl_detail, impl_by_year, kb_by_year, steady_by_year = build_impl_costs(\n", + " sites, HOURS_MODE, BLENDED_RATE, INCLUDE_KB_READINESS,\n", + " COPILOT_INCLUDES_ASIA, NA_EMAIL_EARLY)\n", + "\n", + "impl_summary = impl_detail.groupby(\"workstream\")[[\"hours\", \"cost\", *YEARS]].sum()\n", + "impl_summary.loc[\"Steady-state tuning (2027-28)\"] = [\n", + " hours_pick(STEADY_STATE_HOURS, HOURS_MODE) * 2,\n", + " sum(steady_by_year.values()), *[steady_by_year[y] for y in YEARS]]\n", + "impl_summary.loc[\"TOTAL\"] = impl_summary.sum()\n", + "print(f\"AI implementation ({HOURS_MODE}, ${BLENDED_RATE}/h): \"\n", + " f\"Y1 LoE {money(sum(impl_by_year.values()))}\"\n", + " + (f\" + KB readiness {money(sum(kb_by_year.values()))}\" if INCLUDE_KB_READINESS else \"\")\n", + " + f\" + steady-state {money(sum(steady_by_year.values()))} (2027-28)\")\n", + "display(impl_summary)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "1ffaa74a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI implementation $1.3M ÷ benefit claim $15.0M = 8.8% → FLAG — under-modelled by the industry benchmark\n", + "Industry benchmark: 20-40% of the Y1 benefit claim goes to implementation.\n", + "Reading: V2 deliberately strips vendor-services inflation from V1 (which sat at ~31%).\n", + "If the corrected case still looks good at V2 hours, it is robust to the higher estimate;\n", + "Section 9 sweeps hours × rate to show exactly how much the conclusion depends on this line.\n" + ] + } + ], + "source": [ + "# ── Smell test (V1 doc rule): AI impl should be ≥15% of the AI benefit claim ──\n", + "_impl_total = sum(impl_by_year.values())\n", + "_ratio = _impl_total / SLIDE_TOTALS[\"total_3yr\"]\n", + "_verdict = (\"PASS\" if _ratio >= SMELL_TEST_FLOOR\n", + " else \"FLAG — under-modelled by the industry benchmark\")\n", + "print(f\"AI implementation {money(_impl_total)} ÷ benefit claim \"\n", + " f\"{money(SLIDE_TOTALS['total_3yr'])} = {_ratio:.1%} → {_verdict}\")\n", + "backstage(\"Industry benchmark: 20-40% of the Y1 benefit claim goes to implementation.\")\n", + "backstage(\"Reading: V2 deliberately strips vendor-services inflation from V1 (which sat at ~31%).\")\n", + "backstage(\"If the corrected case still looks good at V2 hours, it is robust to the higher estimate;\")\n", + "backstage(\"Section 9 sweeps hours × rate to show exactly how much the conclusion depends on this line.\")" + ] + }, + { + "cell_type": "markdown", + "id": "b958f93d", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 5 · Corrected cost stack\n", + "\n", + "The pitched case vs the same case with the three missing costs added — plus the signed\n", + "contract mechanics: the **6-month ramp** (🟢 order form) means licences bill only half of\n", + "2026; PS bills on the **SOW milestones** (50/50 across 2026–27, $2.03M total vs the deck's\n", + "$2.4M year-1 lump); and **managed services** ($411K/yr from MCX go-live) join the stack —\n", + "an ongoing cost the deck's TCO never carried. **No existing-platform line appears** — current\n", + "spend is excluded from this case per CTM's direction." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "ad880bda", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Corrected programme cost: {2026: '$4.6M', 2027: '$7.7M', 2028: '$7.2M'} → 3-yr $19.5M\n", + "As pitched (deck): {2026: '$6.9M', 2027: '$4.3M', 2028: '$4.3M'} → 3-yr $15.5M (deck says $15.4M — rounding)\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
2026202720283-yr
CCaaS platform licences (contracted, ramp-adjusted)1,600,0003,200,0003,200,0008,000,000
NTT PS + training (SOW billing milestones)1,179,7231,012,72302,192,446
NTT managed services (from MCX go-live)68,486410,918410,918890,323
AI token consumption424,3502,676,6083,479,7366,580,694
AI implementation + KB readiness1,302,108239,14201,541,250
AI steady-state tuning0157,500157,500315,000
TOTAL — corrected4,574,6687,696,8927,248,15419,519,714
\n", + "
" + ], + "text/plain": [ + " 2026 2027 \\\n", + "CCaaS platform licences (contracted, ramp-adjus... 1,600,000 3,200,000 \n", + "NTT PS + training (SOW billing milestones) 1,179,723 1,012,723 \n", + "NTT managed services (from MCX go-live) 68,486 410,918 \n", + "AI token consumption 424,350 2,676,608 \n", + "AI implementation + KB readiness 1,302,108 239,142 \n", + "AI steady-state tuning 0 157,500 \n", + "TOTAL — corrected 4,574,668 7,696,892 \n", + "\n", + " 2028 3-yr \n", + "CCaaS platform licences (contracted, ramp-adjus... 3,200,000 8,000,000 \n", + "NTT PS + training (SOW billing milestones) 0 2,192,446 \n", + "NTT managed services (from MCX go-live) 410,918 890,323 \n", + "AI token consumption 3,479,736 6,580,694 \n", + "AI implementation + KB readiness 0 1,541,250 \n", + "AI steady-state tuning 157,500 315,000 \n", + "TOTAL — corrected 7,248,154 19,519,714 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
2026202720283-yr
CCaaS platform licences (as pitched, no ramp)4,300,0004,300,0004,300,00012,900,000
Base professional services + training2,567,000002,567,000
TOTAL — as pitched6,867,0004,300,0004,300,00015,467,000
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 \\\n", + "CCaaS platform licences (as pitched, no ramp) 4,300,000 4,300,000 4,300,000 \n", + "Base professional services + training 2,567,000 0 0 \n", + "TOTAL — as pitched 6,867,000 4,300,000 4,300,000 \n", + "\n", + " 3-yr \n", + "CCaaS platform licences (as pitched, no ramp) 12,900,000 \n", + "Base professional services + training 2,567,000 \n", + "TOTAL — as pitched 15,467,000 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
dateshareamount
SOW billing milestone
SOW Effective Date2026-03-1530%$607,633.94
Start of client UAT (first region, NA)2026-10-1620%$405,089.30
Start of client UAT (last region, APAC)2027-10-0130%$607,633.94
Completion of last go-live migration2027-11-0120%$405,089.30
\n", + "
" + ], + "text/plain": [ + " date share amount\n", + "SOW billing milestone \n", + "SOW Effective Date 2026-03-15 30% $607,633.94\n", + "Start of client UAT (first region, NA) 2026-10-16 20% $405,089.30\n", + "Start of client UAT (last region, APAC) 2027-10-01 30% $607,633.94\n", + "Completion of last go-live migration 2027-11-01 20% $405,089.30" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ps_by_year = ps_costs_by_year(contracted=True) # SOW milestones + verbatim training\n", + "ps_pitched_by_year = ps_costs_by_year() # deck's verbatim year-1 lump\n", + "managed_by_year = managed_services_by_year() # 🟢 bills from MCX go-live (16 Oct 2026)\n", + "impl_kb_by_year = {y: impl_by_year[y] + kb_by_year[y] for y in YEARS}\n", + "\n", + "corrected_costs = pd.DataFrame({\n", + " \"CCaaS platform licences (contracted, ramp-adjusted)\": licence_by_year,\n", + " \"NTT PS + training (SOW billing milestones)\": ps_by_year,\n", + " \"NTT managed services (from MCX go-live)\": managed_by_year,\n", + " \"AI token consumption\": token_total_by_year,\n", + " \"AI implementation + KB readiness\": impl_kb_by_year,\n", + " \"AI steady-state tuning\": steady_by_year,\n", + "}).T[YEARS]\n", + "corrected_costs[\"3-yr\"] = corrected_costs.sum(axis=1)\n", + "corrected_total_by_year = {y: float(corrected_costs[y].sum()) for y in YEARS}\n", + "\n", + "pitched_costs = pd.DataFrame({\n", + " \"CCaaS platform licences (as pitched, no ramp)\": {y: TCO_VERBATIM[\"ccaas_annual\"] for y in YEARS},\n", + " \"Base professional services + training\": ps_pitched_by_year,\n", + "}).T[YEARS]\n", + "pitched_costs[\"3-yr\"] = pitched_costs.sum(axis=1)\n", + "pitched_total_by_year = {y: float(pitched_costs[y].sum()) for y in YEARS}\n", + "\n", + "kb_note = \"\" if INCLUDE_KB_READINESS else \" (KB readiness excluded)\"\n", + "backstage(f\"Corrected programme cost{kb_note}: \"\n", + " f\"{ {y: money(v) for y, v in corrected_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(corrected_total_by_year.values()))}\")\n", + "backstage(f\"As pitched (deck): { {y: money(v) for y, v in pitched_total_by_year.items()} } \"\n", + " f\"→ 3-yr {money(sum(pitched_total_by_year.values()))} (deck says $15.4M — rounding)\")\n", + "display(pd.concat([corrected_costs,\n", + " pd.DataFrame(corrected_costs.sum()).T.set_axis([\"TOTAL — corrected\"])]))\n", + "display(pd.concat([pitched_costs,\n", + " pd.DataFrame(pitched_costs.sum()).T.set_axis([\"TOTAL — as pitched\"])]))\n", + "\n", + "_ms = ps_milestones_dataframe()\n", + "_ms[\"amount\"] = _ms[\"amount\"].map(\"${:,.2f}\".format)\n", + "_ms[\"share\"] = _ms[\"share\"].map(\"{:.0%}\".format)\n", + "display(_ms.set_index(\"name\").rename_axis(\"SOW billing milestone\"))\n" + ] + }, + { + "cell_type": "markdown", + "id": "be744ec4", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 6 · Figure 1 — Benefits over 3 years (verbatim Genesys)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "2e2b207c", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agent Copilot", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 2700600, + 4663400 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "WFM", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 624800, + 2376200 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#eda100", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Email", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 1379750, + 1591250 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "STA", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 331650, + 482350 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Predictive Routing", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 161400, + 364600 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Supervisor Copilot", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 171850, + 195150 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative benefits", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "AAAAAAAAAAAAAACAMHxUQQAAAAA3sWxB", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$0K", + "x": 0, + "y": 0, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$5.4M", + "x": 1, + "y": 5370050, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$9.7M", + "x": 2, + "y": 9672950, + "yshift": 12 + }, + { + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr total $15.0M (verbatim)", + "x": 2, + "xshift": -40, + "y": 15043000, + "yshift": 16 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 460, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Benefits over 3 years — verbatim Genesys (Appendix 4)
Phased by Genesys's own deployment schedule: benefits realize +3 months after regional go-live — $0 in 2026 · WFM = Workforce Forecast & Scheduling", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAABSwAAAHMCAYAAADf4OZ1AAAgAElEQVR4XuzdBZhV5drG8YfuRpGSDilpkO4ZUOzEABFb1M886vEYx65jgIFii4ENyMwwdEl3t5QIqCCd33W/w1ouFntvZpCYgf/v+84ls3at3mvd+3nfN9OePbv3GwAAAAAAAACkA5kILAEAAAAAAACkFwSWAAAAAAAAANINAksAAAAAAAAA6QaBJQAAAAAAAIB0g8ASAAAAAAAAQLpBYAkAAAAAAAAg3SCwBAAAAAAAAJBuEFgCAAAAAAAASDcILAEAAAAAAACkGwSWAAAAAAAAANINAksAAAAAAAAA6QaBJQAAAAAAAIB0g8ASAAAAAAAAQLpBYAkAAAAAAAAg3SCwBAAAAAAAAJBuEFgCAAAAAAAASDcILAEAAAAAAACkGwSWAAAAAAAAANINAksAAAAAAAAA6QaBJQAAAAAAAIB0g8ASAI6zffv2WebMmY/zp546WL8AAAAAkLERWAbs2bPHxo8fb8uXL7fChQtbs2bNrECBArZ161bLkSOHZc2a1X8ucDzdddddlpCQaO+887a1atXqmHz0a6+9bs8//7z997//tRtu6O5Px9G1fft2a9++g/XocYN169bNn36yeemll+2jjz6yO++80268sYetX7/e4uLi3bk0MTHB8ufPf9QXeffu3fb444/bvHnz7ZtvvrZMmTL5jwEAAAAAMo7jGlguXbrULrvsclu5cqXt3LnTsmfPbqeffro1atTIbr31VmvQoP4JW3O//fabXXTRxTZ79mx/Hr799hsrVKiwxcfHW6lSpWz8+HHuZnvgwIE2Y8YM69q1q5t+Mvn999/tjTfesO+//8Ftp0KFClnHjh3tscf+Y0WKFDmZFjVDadq0mds3v//+O2vduvURz/vkyVNs1KhRVr9+PWvRooU/XbSdx40bb3FxcfbVV1/6048W7U/atxS8rlmzxnLmzGnVqp1l553X2W699RbLli2b/9yT2aOPPmqvv/6GdevW1V577bWDFvW22263zz77zMqXL29Tp07J0IHb9dd3t2+//dZeeOEFu/nmm2zkyJF2/vkXuOUcMWK41alT58BSH7lbb73N+vXr5/bnoUOHusrKFi1a2qxZs6xXrzfs2muv9Z8LAAAAAMg4jmtgOXPmTGvePCUkqVy5sru5XL16tas4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8Ez/07//+OMPy5Y1qz39zFMHBZZLly6xpKQku+WWW/1pYb/8ssLefqu3PfPsC+5vzdMdt99q7/X9wLZv326PPPyQnV37bCtRoqS1b9/BPWfgwAEp/x3w4yHzKk8/9V+79rrr7Mwzy1jPO263Z597zvLmzecee/V/r1jHTuda+fLl7aMPP7AVK5bb3r177eyza9sVV17lnvN5v89swYL5ikGsYaNGLkjt3et1O+300233rl2WI2dOu+mmm61YsTPc8yOty0jrfffu3X7wOXr0KBv800DLniOH7dq502rUrGVLlixx21vLfV3Xrla9eg332tdff83atmlr1Wuk/A3gYASWAAAAAACk0ueff2ZDkpKseYsW1q1b90P6Idy6das9/NCD9trrvdzf/b/6yoqdUcxatGgZMTj0hCss1fT6/vvutYcf/reVLFXKVRc+8/RT9ryrSswcs9Jv8+bN9sQTj9nLL//P/R3pczdu3GD/fuRhe/iRR6106dL+9KA///jjkMBy7py59tHHH1j+fPmtcpUqdu655/lVp54pUybbiOHD7d777venqaqyd++37M8//7S33upld991jz3x5OP20kuvuHDxkUcesqefftbuvef/7OVX/ndIU/vbb7vVLa8C1b7v9bGqZ1Wzpk2budfqNa/871X76qsvXKVop07nuirPZ5952s6/4AKrUaOmPfnE43bDDT3cuhRVWP7w/Xf2r4cedn/PmDHdNdV+5tnn3d+e4LqMVGEZrNT85uuvLV/+fNahQ5zbDxSsvvra665Sde7cufbVl5/b40/8173uu2+/deH1RRddfOCdAAQRWAIAAAAAkAZq0vt+3/es2BlnuMBJ1X9//bXZChUqbA/+6yFXndfp3POsWrVq9p//PGL33fuA5S9QIGJw6AkHlpMnT7IRI0bYfYHQ73+vvGxNmzWzWrVqHRKcqYJPgd1v636z/bbfZs2caZ982s89Fu1zVyxfbj/88L39tWWLtW7d2po0aeo/JpECS4/WQVJSos2cMcMeDg3oo8By5IgRds+99/nTetxwvfV+821btWqlffPN165J+YcfvG9ly5a17Tt22p7du63z+efbgw/e7x4L9wl580032jt9UpZBIeGggQPsvvsfsJkzZ9jkSZNcc/MHH7jf8uXLZ9lzpISd6mu0Q4d4a9asecTAMtwk/PbbbnGBpcLS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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for cap in CAPABILITIES:\n", + " vals = [benefits_long.query(\"capability == @cap and year == @y\")[\"benefit\"].sum()\n", + " for y in YEARS]\n", + " fig.add_trace(bar(X, vals, cap, CAP_COLOR[cap]))\n", + "cum = pd.Series([benefit_total_by_year[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum, \"Cumulative benefits\"))\n", + "for i, y in enumerate(YEARS):\n", + " fig.add_annotation(x=i, y=benefit_total_by_year[y],\n", + " text=f\"{money(benefit_total_by_year[y])}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "fig.add_annotation(x=len(YEARS) - 1, y=float(cum.iloc[-1]),\n", + " text=f\"3-yr total {money(float(cum.iloc[-1]))} (verbatim)\",\n", + " showarrow=False, yshift=16, xshift=-40, font=dict(size=12, color=INK2))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"Benefits over 3 years — verbatim Genesys (Appendix 4)\",\n", + " \"Phased by Genesys's own deployment schedule: benefits realize +3 months after \"\n", + " \"regional go-live — $0 in 2026 · WFM = Workforce Forecast & Scheduling\")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "95e3aa5a", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 7 · Figure 2 — Costs over 3 years, corrected\n", + "\n", + "The stack is the full programme cost — **excluding current spend** (no existing-platform line).\n", + "The dashed grey line is the deck's cumulative cost case ($15.4M) for reference — the gap between\n", + "the lines is what the pitch left out (tokens + AI implementation), net of the ramp credit and\n", + "contracted-rate savings it also left out." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c2c9418f", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "CCaaS platform licences (contracted, ramp-adjusted)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 1600000, + 3200000, + 3200000 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "NTT PS + training (SOW billing milestones)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 1179723.24, + 1012723.24, + 0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#0b6b4a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "NTT managed services (from MCX go-live)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 68486.40000000001, + 410918.4000000001, + 410918.4000000001 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#008300", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI token consumption", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 424350, + 2676608, + 3479736 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI implementation + KB readiness", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 1302107.96532141, + 239142.03467859002, + 0 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "AI steady-state tuning", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 157500, + 157500 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative — corrected", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "A5a95nJzUUGQwvXo+GdnQa5H4RqOnXJB", + "dtype": "f8" + } + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#c3c2b7", + "dash": "dash", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative — as pitched", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "AAAAAA4yWkEAAAAAo0xlQQAAAAA/gG1B", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$4.6M", + "x": 0, + "y": 4574667.60532141, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$7.7M", + "x": 1, + "y": 7696891.67467859, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$7.2M", + "x": 2, + "y": 7248154.4, + "yshift": 12 + }, + { + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr $19.5M — $4.1M above the pitch", + "x": 2, + "xshift": -70, + "y": 19519713.68, + "yshift": 18 + } + ], + "bargap": 0.45, + "barmode": "stack", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 500, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Programme cost over 3 years — with the missed costs
Existing platforms bill until 31 Dec 2027 term-contract end (double-billing) · licences at the contracted run-rate, ramp-free for 6 months · SOW-billed PS + managed services · tokens + AI implementation added", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAABSwAAAH0CAYAAAA3yWO1AAAgAElEQVR4XuzdB3gU1RoG4D8hhB46SAm9hJpCR4r0qnTpAgKKCvbutfeOig2kCIJ0FEF6lyYlhV5Ch4D0DoEk9/n+ZMbJMLvZQIAFv/e595Hs7E49074554zP1atXEoSIiIiIiIiIiIjIC/gwsCQiIiIiIiIiIiJvwcCSiIiIiIiIiIiIvAYDSyIiIiIiIiIiIvIaDCyJiIiIiIiIiIjIazCwJCIiIiIiIiIiIq/BwJKIiIiIiIiIiIi8BgNLIiIiIiIiIiIi8hoMLImIiIiIiIiIiMhrMLAkIiIiIiIiIiIir8HAkoiIiIiIiIiIiLwGA0siIiIiIiIiIiLyGgwsiYiIiIiIiIiIyGswsCQiIiIiIiIiIiKvwcCSiIiIiIiIiIiIvAYDSyIiIiIiIiIiIvIaDCyJiIiIiIiIiIjIazCwJCIiIiIiIiIiIq/BwJKIiIiIiIiIiIi8BgNLIiIiIiIiIiIi8hoMLImIiIiIiIiIiMhrMLAkIiIiIiIiIiIir8HAkohuuq5du8nq1atl0qRJUqVKmNvpjRo1Sr777ntp166dvPLKy+bn1yutx3ejli37S3r27ClNmjSRYcOGmp/favPmzZM33nhTypcvL8OH/3SrJ09ERERERETk0i0NLPfs2SN9+/aVvXv3yenTpyUhIUHuueceqVmzpgwc+ISEhISYM0Z3hhkzZkhkZKT06tVLChcufGfMtMXWrVtl8OCvZMGCBXLy5EkJDAyUHj16yNNPPyXp0qUzv0fX78qVK5I3bz7d399991158slBsm/fPvnzz1mSOXMmeeihh8zvwssvvyzff/+DPP/8c/L666+bn7uT1uO7md5//3355JNPJUuWLHLo0EHz81tt+PAR8uyzz8oDDzwgY8aMvtWTVxcuXJApU6bI3r175X//+99tmQciIiIiIiLyPrc0sIyKipK6devpWvD19dX/xsfH63/9/Pxk4sQJ0qhRI/2b7gxBQeUkJiZGli1bKpUrV74zZtri7bffli+++FLLX/bs2eX48eP6+YsvviCvvfZa0rfoRo0dO1bWrl2roVTu3Lnl559/lieffEpatmwpv/46zvweXE/AmNbju5mOHDki77//gdSrV1c6duxofn6reUNgGR0dLWFhVSQgIED27993q1cBEREREREReanbFlieOnVSw8odO3bIU089JatWrZZKlSrJX38t89JVRU7u9MAS4RFqVyI48vf3l6+//kZDLdT83bZtq7mclLbSOmBM6/H9FzCwJCIiIiIiIm912wLL06dPmetk0aJF0rZtO/33iRPH5cCBA/LFF1/I33+v0X9fvXpV6tevL+PH/6rfQfPPt956W3+H5qbVq1eXN954PVmT8ri4OPnmmyEybtw4bYqO5pcFCxaU4OBg+e67b/U76Etu6NChsmnTJjl48KBkypRJBg0aJM8996zWBPv99+kaaGXIkEGqVq0qH3zwvpQrV05/u2XLFnn33ff0v//884/OY7169aRfv74yatTPsmzZMq1FWqdOHRk8+EvJly+f/u6PP/6QoUOHya5du+To0aOSOXNm6dSpoy4D5iUqaoPWNurc+UF56623tOafYenSpfLBBx9IeHiEfueBB+7X72TLls38jt2aNWu1Ceq6des0IK5QobyMGDHCbL7911/L5cMPP5D168N1HTVr1kzeeutNyZs3rw5HM140mUY/gFhHqB0XFhZmhkJGYGm1fv06KVmypPm3FYIlhEdY/gIFCki3bt10fWM5sS5r1qyl623v3j26jPDDDz/KSy+9JI0bN5YpUybrZymtC3fb1h2jxhe+f/hw8uWCn34aLs8995yEhobI4sWLzc8feKCNLFmyRL755muzSTICoR9++EHLH5qaP/zww/LEE4+Lj4+PDp8wYYJ8/vkXcujQIS0/ZcuWkRdeeEFat26tw1G2sYwY7759++XUqVNSrFgxmTNntq6vN954Q2bMmClnz5411+VLL72ov71ZjG2E5vIHDx5IWk+HpWzZIJ3vsWN/0e99+eVg3R7oN3LUqJFy7711ZOPGjfLbb9OkQYMGZsBo9eCDD2qfjkbAiND44sWLui+Hhobq/ueqFm9ajS+lbWZ16dIleeqpp2XDhg26D2A7BAUFae1cbLM//pghZ86ckYoVK2iNyho1quvv0I1C9+49pGbNGjJnzhz9DF0T/O9/r8uKFSt0WkWLFpXXX/+ftGjRIsV9EFLaH2D58hXy7rvvSEREpOTMmVPy5cur/06pSfj48ePl22+/k+3bt+sxAse00aN/1mFYB5jupEmT5cSJE1K2bFntTqF9+/ZJv3a/bMb+ZoWyvHXrFo+Wm4iIiIiIiO5OXhFY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ueQS3X//D7VgwQLnJf7oR/+mr3/96zaQ9HhqnONvZsLJ1157zW6XkJaWZo9deeVVikYjuuWWW/S5z92i1177kx3uc/3112nXrt164YUX7KAfAAAATD8ElgCA82LD+pfe8nMnJnJnuMcG38zJn++sTRemPbW1tcW2epug0gwdOd7y5QVjrd5enwoLC23rKjCTmT0sf/CDH+j++3+klStXasOG9c5+kx/60Id04ECXGhrqT/oW9Pb2auXKQl177bW6777vO8cvvPB9thL56aefslWaeXl52rhxo5qbm/S9731PDz74kI4cOew8HwAAANMHgSUA4IyZ1ub+/rDC4T7b1l1UtEqJiWOBxMls7WhTckrKeZ/IfTaYKcbB8b0ozYTi46tFU1JStLqi0oaUptU7N/fUE8uBmeqf//mfdd99P7CB4pIlS+wWCWYK+Be+cJv+5V/+xXnZhw8ftoHmxF6Xv//97/Xxj3/CBpMm4Jxg9qqsqKjQE0/8TN/85jf13e9+z04hf+qpJ21V5733fkd79uxWZmam8z0AgJnjpZdespX1ppp+1qxZM+eFAbAILAEA70h392H19nQrYtq4oyak7HXWJlRUrlFmZrbzeCbasX27DSiDwTpt3rxFo6Ojzss0oaQZ+GGqKCsrq5SamuqsAfGisbFRd9xxp50MnpOTbcNKU225bdtWG0i+8sor+shHrtGDDz6gT3ziE/ZtMX+PiotL7PYITU2N9tjENPCNG9s1f/4bVdfLli23g3jM3pdmv0rTNn777f9iW8/vuutu3X777W/5HgDAzGA6WG6//Q49+uijuvXWz9tfWF188cX6u7/7e/3mN7+Z9CLNv8O6ug44j43XXltvrxn19Q2aO3euvva1r+pTn/qUXXvkkUf0pS99WXfeeaduu+3W8e+Q/aX0RRd9QJ/97M26++67neMAzg0CSwDAO9La4ld391HnsZGSmmancrszxlq5Z8+eaysLZxLT6m0ClIlKyoMHD056eabV1Uz09vq8WrFipXMciFfr12/Q1772NTt8Z3h42P4dueuub9uQ0Xj44Uf05S9/WX/848uqqXlj/8pLL71ULpfbDtExbrzxJr344ou2WvJ4eXlzbJu42RfzzUw4aio6zdYMZusFAMDMMTAwoDVr1tpfgpWXl2nv3k77y7GHHnpQzz//vDo6ttoX29vbY3/htXbt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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for line in corrected_costs.index:\n", + " fig.add_trace(bar(X, [corrected_costs.loc[line, y] for y in YEARS], line, COST_COLOR[line]))\n", + "cum_corrected = pd.Series([corrected_total_by_year[y] for y in YEARS]).cumsum()\n", + "cum_pitched = pd.Series([pitched_total_by_year[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum_corrected, \"Cumulative — corrected\"))\n", + "fig.add_trace(cum_line(X, cum_pitched, \"Cumulative — as pitched\", color=CONTEXT, dash=\"dash\"))\n", + "for i, y in enumerate(YEARS):\n", + " fig.add_annotation(x=i, y=corrected_total_by_year[y],\n", + " text=f\"{money(corrected_total_by_year[y])}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "delta = float(cum_corrected.iloc[-1] - cum_pitched.iloc[-1])\n", + "fig.add_annotation(x=len(YEARS) - 1, y=float(cum_corrected.iloc[-1]),\n", + " text=f\"3-yr {html_money(float(cum_corrected.iloc[-1]))} — \"\n", + " f\"{html_money(delta)} above the pitch\",\n", + " showarrow=False, yshift=18, xshift=-70, font=dict(size=12, color=INK2))\n", + "fig.update_layout(barmode=\"stack\")\n", + "tei_layout(fig, \"Programme cost over 3 years — with the missed costs\",\n", + " \"Existing platforms bill until 31 Dec 2027 term-contract end (double-billing) · \"\n", + " \"licences at the contracted run-rate, ramp-free for 6 months · SOW-billed PS \"\n", + " \"+ managed services · tokens + AI implementation added\", height=500)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "82335f50", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#c3c2b7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "As pitched (deck)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 6867000, + 4300000, + 4300000 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Corrected", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 4574667.60532141, + 7696891.67467859, + 7248154.4 + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "−$2.3M", + "x": 0, + "xshift": 16, + "y": 4574667.60532141, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "+$3.4M", + "x": 1, + "xshift": 16, + "y": 7696891.67467859, + "yshift": 12 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "+$2.9M", + "x": 2, + "xshift": 16, + "y": 7248154.4, + "yshift": 12 + } + ], + "bargap": 0.45, + "bargroupgap": 0.15, + "barmode": "group", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 400, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Cost case: as pitched vs corrected, by year
Delta labels = what each year's pitch understates", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "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mdp2S/JJ3wEAAACgZVJYLgApC8t33nknDj7owDj5Jz+Nvn1XqL2Gf8ecOXNiypQp0b179/jb3x6OqVPfjC9+8b9r98/vrbfeijPPPC2OP/7HtX2f1bHfPTpOOvmn0aZNm9q+wi8uvyzWXGutGDRo3dq+whW/+kWsvfagGLjmmrV9VVx66SWx0UYbx+c+N6C27/777o3xz4+PPfbYq7avuaUsLJ977u9x5ZW/Kr8ThQEDBsS++3597itZ8H52ztlx+LeO+MxP9EnfAQAAAKBlUlguACkLy+uvuy5eeeXlaNW6dRx00MG11/Cf+tMf74i33n47dtjhf2r75regCssZM2bESScOjxOGn1jb16Q5C8vCD39wXPzo+OHRtm3b2r7mlKqwfP311+MnJ58URxx5ZPTps3z53FOnTo2lllpq7itZ8L7z7SPijDPP/sxP9HHfAQAAAKBbkf+yAAAJXUlEQVTlUlguAKkKy2JF5PeP+26cMPykOO64Y2P48BOjU6dO5f5LLr4wXn11UlkC7bPPvrF8376113fjjWPK1YVt27aL2bNnxW67fa08XLq+gCwKy+tvuD569OgZq666auy0084xduz9cf11o6J9+47l47feenAcPmxo9OvXL2bM+CBWWGGFOPCbB0erVq3i3XffjYsuujCmTn0jYk7EPvvuXx7GXaz+u/DnF5SrNzt06BgvvDAhLrp4xDxl1V/G3h8vvjgxdt5ll/K9XHXlyHj00UeiU+fOMfXNN2P//b9RFpYvTJgQl19+WcyeMys6d1osDhs6LLp06RITJ06MS0dcEnPmzI4uSywZRx11dK2wXG211eO0006Nr351p/Lw8F//+spYffU1Yp11BtWevzmlKiwvueSiWGON/4pNN/1C7bmb/OOll2JE8XnE7GjTpm0cdNAh0aNHj3jmmWdj5BW/iDZt28bKK60c62+w0Tzbe+61d9x7z91x8803lb/T/v37x35fP6C8Pf93YZdddo1hQw+LZZdbrnzO/fc/IBZffPF/+zsAAAAAtFwKywUgVWFZlHjjHnigPP/f7665JpZcasnYcsutyhWXI6+4Io4+5ru111SvfrXha69NjuEnnBA/+ekpMXv27HkKy/oVlq+/PiV++pOTy1WPnTt3LvfNv8Ly7LPOjMGDt4nV11i9LNCK5xg4cM3yOc792TnluTEvvujnZUG4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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "fig.add_trace(bar(X, [pitched_total_by_year[y] for y in YEARS], \"As pitched (deck)\", CONTEXT))\n", + "fig.add_trace(bar(X, [corrected_total_by_year[y] for y in YEARS], \"Corrected\", \"#2a78d6\"))\n", + "for i, y in enumerate(YEARS):\n", + " d = corrected_total_by_year[y] - pitched_total_by_year[y]\n", + " fig.add_annotation(x=i, y=corrected_total_by_year[y], xshift=16,\n", + " text=f\"{'+' if d >= 0 else '−'}{money(abs(d))}\",\n", + " showarrow=False, yshift=12, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"group\", bargap=0.35, bargroupgap=0.15)\n", + "tei_layout(fig, \"Cost case: as pitched vs corrected, by year\",\n", + " \"Delta labels = what each year's pitch understates\", height=400)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "bde5fbf2", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 8 · Figure 3 — Overall business case / ROI (absolute)\n", + "\n", + "**Frame:** absolute — **current spend excluded** per CTM. Incremental cost = **full programme\n", + "cost** (no $7.3M baseline); net = verbatim Genesys benefits − programme cost. There is no\n", + "double-billing penalty and no cost-avoidance credit; the programme stands on the benefits it\n", + "generates alone. This is a deliberately harsher frame than the deck's — and it is why the\n", + "**virtual-agents alternative** matters (companion notebook).\n", + "\n", + "*Not modelled: any early-termination fees, and migration costs beyond the PS/impl lines (section 11).*" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "de7679d8", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "aad65db253294eddbecef4224a146196", + "position": "sidebar", + "widget": "SelectWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "aad65db253294eddbecef4224a146196", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── NPV rate (Mercury sidebar — widgets only, no output) ────────────\n", + "# NB: Mercury re-executes only cells BELOW a changed widget's cell,\n", + "# so this cell constructs widgets ONLY — .value is read downstream.\n", + "_npv_rate_w = mr.Select(label=\"NPV discount rate\", value=\"13.5% (deck)\",\n", + " choices=[\"13.5% (deck)\", \"8.0% (CTM treasury)\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "08433116", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "corrected: net by year {2026: '-$4.6M', 2027: '-$2.3M', 2028: '$2.4M'}\n", + "pitched: net by year {2026: '-$6.9M', 2027: '$1.1M', 2028: '$5.4M'}\n" + ] + } + ], + "source": [ + "# ABSOLUTE frame (CTM directive — current spend excluded):\n", + "# incremental cost = full programme cost; net = Genesys benefits − cost.\n", + "# No $7.3M do-nothing baseline (baseline_annual = 0).\n", + "DISCOUNT_RATE = (TCO_VERBATIM[\"npv_discount_rate\"]\n", + " if _npv_rate_w.value.startswith(\"13.5\") else 0.08)\n", + "BASELINE_ANNUAL = 0.0\n", + "\n", + "inc_corrected, net_corrected = case_flows(\n", + " corrected_total_by_year, benefit_total_by_year, BASELINE_ANNUAL)\n", + "inc_pitched, net_pitched = case_flows(\n", + " pitched_total_by_year, benefit_total_by_year, BASELINE_ANNUAL)\n", + "kpi_corrected = case_kpis(inc_corrected, net_corrected, DISCOUNT_RATE)\n", + "kpi_pitched = case_kpis(inc_pitched, net_pitched, DISCOUNT_RATE)\n", + "\n", + "\n", + "def fmt_roi(k):\n", + " return f\"{k['roi']:.0%}\" if k[\"roi\"] is not None else \"n/a — net cost saving\"\n", + "backstage(f\"corrected: net by year { {y: money(v) for y, v in net_corrected.items()} }\")\n", + "backstage(f\"pitched: net by year { {y: money(v) for y, v in net_pitched.items()} }\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "04c43658", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Benefits (verbatim Genesys)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + 0, + 5370050, + 9672950 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#e34948", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Programme cost (current spend excluded)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028" + ], + "y": [ + -4574667.60532141, + -7696891.67467859, + -7248154.4 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#52514e", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative net", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028" + ], + "y": { + "bdata": "A5a95nJzUcEfhetRwVNawbkehWvKE1HB", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$4.6M", + "x": 0, + "y": -4574667.60532141, + "yshift": -14 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$6.9M", + "x": 1, + "y": -6901509.28, + "yshift": -14 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$4.5M", + "x": 2, + "y": -4476713.680000001, + "yshift": -14 + }, + { + "align": "left", + "bgcolor": "#fcfcfb", + "bordercolor": "#e1e0d9", + "borderwidth": 1, + "font": { + "color": "#52514e", + "size": 12 + }, + "showarrow": false, + "text": "3-yr net -$4.5M · ROI -23%
NPV@13.5% -$4.2M · payback beyond 2028", + "x": 0.01, + "xref": "paper", + "y": 0.98, + "yref": "paper" + } + ], + "bargap": 0.45, + "barmode": "relative", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 500, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Corrected business case — benefits vs incremental cost
No do-nothing baseline · benefits realize from 2027 on the PM timeline", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "iVBORw0KGgoAAAANSUhEUgAABSwAAAH0CAYAAAA3yWO1AAAgAElEQVR4XuzdBXQU5xoG4BcnOAR3KO7uxd3d3d1pC7TFiktxC+7uEtzd3R0S3CFI5J7vC7N3WTabpNDspnmfc+9hd3Z2duafme3ZN9///6E8PT/7gIiIiIiIiIiIiMgBhGJgSURERERERERERI6CgSURERERERERERE5DAaWRERERERERERE5DAYWBIREREREREREZHDYGBJREREREREREREDoOBJRERERERERERETkMBpZERERERERERETkMBhYEhERERERERERkcNgYElEREREREREREQOg4ElEREREREREREROQwGlkREREREREREROQwGFgSERERERERERGRw2BgSURERERERERERA6DgSURERERERERERE5DAaWRERERERERERE5DAYWBIREREREREREZHDYGBJREREREREREREDoOBJRERERERERERETkMBpZERERERERERETkMBhYEhERERERERERkcNgYElERD+Mj48P3r17hyhRopiWEREREREREQUGA0sH4ebmhkaNGuHx4yc4d+6s1b3q0qULXF23YNq0qShatKhp+Y+2b99+3ZdSpUrBxWW6aTkFvVmzZmPixImoWbMG+vTpE/Q7YMPLly9x9OhRPHz4EEmTJkWhQoUQNmxYvHr1CtGjRzet54iePHmCMmXKIkKECNiyxRXRokVzxN0MdkaNGo1Ro0ahQ4f2+OOPP4Ld/hMREREREZFjsEtgKdU3U6ZMxerVq3H9+nUNDZInT47y5cujU6eOiBw5smO0jh/u3r2LTZs2I1IkJzRu3Ni0/HtcuXIFefLkRaxYsXDr1k3TcnMFCxbC+fPnsWbNahQrVsy0/EcbPHgwRowYqefBze2BaTkFvd9++03vlZ49ezhUAHTgwEG99p8+faqNEiZMGNy9ewcLFy7CL7/8grp162qw/v79e6xcuRJ37tzB77//HvQN6Ic9e/agcuUq+nj37l3Inj37l1dCtu89Xz179oSLywy7Xq/fewxERERERERkf0EeWD5+/FiDgkuXLn1z9OHDh8f169ccvjpr7ty56Ny5iwasixcvMu3/93CkwPLRo0cYPHgIChf+GTVr1jTtIwU9Rw0sc+bMpX9sqFChAsqUKa3dgJs2bYru3btj5sxZyJo1K/bu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OI1y7UngJlWFJUuW0O8CYW0fAhJYin+ybWvH59f5kmtP3rdp02YNCOUak+9l+QOTi8t0/SOKtI2ch6VLl6Fq1SraXduSrfYz7jtb3/3yxx0JZ48fP673v4SQefLkwYAB/fUPE8KvY5A2ISIiIiIiIsdnl8CSiIiIiIiIiIiIyBoGlkREREREREREROQwGFgSERERERERERGRw2BgSURERERERERERA6DgSURERERERERERE5DAaWRERERERERERE5DAYWBIREREREREREZHDYGBJREREREREREREDoOBJRERERERERERETkMBpZERERERERERETkMBhYEhERERERERERkcNgYEkhzuNHD/HwoRuiRImKlD+lDnHHT0RERERERETkyP4TgeX79+9x5PABFCteym5tvXHDWlSoWOWbz7916wbOnD6FqtVqmpZ9j/37diNLluyIFj26admPtm3rZuTNW0A/Y8nieShcpCQSJkxoev1HC4pjMly/fhVHDu1H5izZEC5ceKRKnRarVi5FjZp1ETZsWNN6QUkC1CtXLuHnwsXw4cMHzJvjgpatOyB06ND/eDe8vb3hunkDXr96qdupbsfjC678uqcdyY++P4P63iciIiIiIiKyJkgDyxPHj+LWzRuoXrPOV2HMpAl/o0OnbqbngfXq1Uts3rgOdes3Ni37N23dsgkFCxVB5MiRTcumTZmANu06mZ4bvLy8NFCNGjWqadn3WL50EYqXLA1n59imZT/asiULUKpMecSMGQuvX79C1KjRECpUKNPrP1pQHJNh/brVyJY9J5IkSWpadvnSBaRLnxFubm64c/sm8hcoZHotKNy7dwfnzp5B+QqV9eNevnyBGDFiftdHX7l8EXdu30bpsuVNy/5tr1+9wq5d2+Hx/h0+f/6MNGnTI2++AqbXJZi+ffsmfLx9kDN3HmTIkFmXnz51ApcuntdrTL4XjGvv+fNn2Oq68cu75V7yxNu376zeZ98rMPd0QO3csRXubvcRJkxY3Xdn57goVLgookSJosd77OhhNGrS4qt7S4LmubNd9B5PliyFabk4c/okokWLhhQpU5mW/ej7M6jvfSIiIiIiIiJrgjywPHXyOJImS4HSZcqZdiK4BZYL5s9Clao1NSgwfG+4EVBBEe6ZhxZBISiOybB82SJkyZINUaNFRySnSIgRMyYePXRHvPgJ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JRERERERERERETkMBpZERERERERERETkMBhYEhERERERERERkcNgYElEREREREREREQOg4ElEREREREREREROQwGlkREREREREREROQwGFgSERERERERERGRw2BgSURERERERERERA6DgSURERERERERERE5DAaWRERERERERERE5DAYWBIREREREREREZHDYGBJREREREREREREDoOBJRERERERERERETkMBpZERERERERERETkMBhYEhERERERERERkcMIFoHl8qWLULlKdUSIGNFhGs7Sg/s3TY+JKPhJlDhl8NtpIiIiIiIiov+gYB1Yvn//HqtXLkHefAWRKnVa0+m5euUSjh09BB8fIHGSpCharKQuf/v2DWa6TEHzFm0RNVq0L2v72uK6EVGiREHBQkVMywJDAksGHkTBE+9fIiIiIiIiIsfh0IGlBIz79+3Bo4fuiBotOpIkSYrcefJp6507exoXL5yDD3yQK1deU2D5+vUrrFi2GA0bN0f48OGxft1qpE2XHmnSpIO7uztWr1yKFCl/QrnylUxn4cmTx1i5fDFSpEiFMuUqmJYHBgMPouCL9y8RERERERGR43DowHLzpvVInDgJMmfJpi3m6emJsGHD6uNXr14iWrTo2L7NFSlSpDQFlqdOHsebN69RuEhxfX7r1g2cO3tGu5TfuHENt2/dwMOH7ihbrhKcnWPrOmtWLcdPqVLj2rWrqF6jti4LLAYeRMEX718iIiIiIiIix+HQgeWO7VsQJnRoFC5aAqFDh7baatu2bv4qsNyze4cGmdlz5NLnz54+gavrBjRo2Aznz53RMDNu3Hi4fPkSKlSsotWbhw7uR4lSZbB+7UrUb9hM3+fmdlv/DSgfb+//XJfwT58+oWGDeujZ6xfkyZPXdKxE/zUSWIby4zuGiIiIiIiIKKRKmDC5XQ7doQNLb29vrZi8cf0q4sSJi9x5C+g4k+asB5YxkD1HTn0ugeUW1w0aRB4/dgShQoVCzlx5sHD+LFSoWA179+5Cnjz5EcvZGfPnzUSLlu30ffLZgeHudvs/FVhKNeu2rVsxe/ZM5MqVG3ny5kXhwkW+Co63uLpi5kwXNGzUGJUrVzEtN7Rr2wbPnj01Pa9XvwGqVasON7cH6NqlM3Llzo1ffvnN9LoEpB3at0W0aNEwesxY03JHIPu2fNlS7N27Fx8+eCBd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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "fig.add_trace(bar(X, [benefit_total_by_year[y] for y in YEARS],\n", + " \"Benefits (verbatim Genesys)\", \"#2a78d6\"))\n", + "fig.add_trace(bar(X, [-inc_corrected[y] for y in YEARS],\n", + " \"Programme cost (current spend excluded)\", \"#e34948\"))\n", + "cum_net = pd.Series([net_corrected[y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum_net, \"Cumulative net\"))\n", + "for i, c in enumerate(cum_net):\n", + " fig.add_annotation(x=i, y=float(c), text=f\"{money(float(c))}\", showarrow=False,\n", + " yshift=14 if c >= 0 else -14, font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"relative\")\n", + "fig.add_annotation(\n", + " xref=\"paper\", yref=\"paper\", x=0.01, y=0.98, align=\"left\", showarrow=False,\n", + " font=dict(size=12, color=INK2), bgcolor=SURFACE, bordercolor=GRID, borderwidth=1,\n", + " text=(f\"3-yr net {html_money(kpi_corrected['net_3yr'])} · \"\n", + " f\"ROI {fmt_roi(kpi_corrected)}
\"\n", + " f\"NPV@{DISCOUNT_RATE:.1%} {html_money(kpi_corrected['npv'])} · \"\n", + " f\"payback {kpi_corrected['payback']}\"))\n", + "tei_layout(fig, \"Corrected business case — benefits vs incremental cost\",\n", + " \"No do-nothing baseline · benefits realize from 2027 on the PM timeline\", height=500)\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "a8c9584e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The missed costs move the 3-yr case by $4.1M (-$424K pitched → -$4.5M corrected).\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
As pitched (deck)Corrected
3-yr benefits$15.0M$15.0M
3-yr incremental cost$15.5M$19.5M
3-yr net-$424K-$4.5M
ROI (net ÷ incremental cost)-3%-23%
NPV @ 13.5% (deck rate)-$1.5M-$4.2M
NPV @ 8.0% (CTM treasury)-$1.2M-$4.3M
Paybackbeyond 2028beyond 2028
\n", + "
" + ], + "text/plain": [ + " As pitched (deck) Corrected\n", + "3-yr benefits $15.0M $15.0M\n", + "3-yr incremental cost $15.5M $19.5M\n", + "3-yr net -$424K -$4.5M\n", + "ROI (net ÷ incremental cost) -3% -23%\n", + "NPV @ 13.5% (deck rate) -$1.5M -$4.2M\n", + "NPV @ 8.0% (CTM treasury) -$1.2M -$4.3M\n", + "Payback beyond 2028 beyond 2028" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def kpi_display(k, net):\n", + " return {\n", + " \"3-yr benefits\": money(k[\"benefits_3yr\"]),\n", + " \"3-yr incremental cost\": money(k[\"incremental_cost_3yr\"]),\n", + " \"3-yr net\": money(k[\"net_3yr\"]),\n", + " \"ROI (net ÷ incremental cost)\": fmt_roi(k),\n", + " f\"NPV @ {k['discount_rate']:.1%} (deck rate)\": money(k[\"npv\"]),\n", + " \"NPV @ 8.0% (CTM treasury)\": money(npv([net[y] for y in YEARS], 0.08)),\n", + " \"Payback\": k[\"payback\"],\n", + " }\n", + "\n", + "\n", + "kpis_fmt = pd.DataFrame({\"As pitched (deck)\": kpi_display(kpi_pitched, net_pitched),\n", + " \"Corrected\": kpi_display(kpi_corrected, net_corrected)})\n", + "delta_net = kpi_pitched[\"net_3yr\"] - kpi_corrected[\"net_3yr\"]\n", + "print(f\"The missed costs move the 3-yr case by {money(delta_net)} \"\n", + " f\"({money(kpi_pitched['net_3yr'])} pitched → {money(kpi_corrected['net_3yr'])} corrected).\")\n", + "display(kpis_fmt)" + ] + }, + { + "cell_type": "markdown", + "id": "e7d54d0d", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 9 · Sensitivity\n", + "\n", + "The two weakest inputs, swept: the **Email Auto-Respond token rate** (🔴 unpublished — the working\n", + "assumption is the only estimated meter in the token stack) and the **AI implementation LoE**\n", + "(V1 vs V2 disagreed by ~4×)." + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "6671014d", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "colorscale": [ + [ + 0, + "#cde2fb" + ], + [ + 0.08333333333333333, + "#b7d3f6" + ], + [ + 0.16666666666666666, + "#9ec5f4" + ], + [ + 0.25, + "#86b6ef" + ], + [ + 0.3333333333333333, + "#6da7ec" + ], + [ + 0.4166666666666667, + "#5598e7" + ], + [ + 0.5, + "#3987e5" + ], + [ + 0.5833333333333334, + "#2a78d6" + ], + [ + 0.6666666666666666, + "#256abf" + ], + [ + 0.75, + "#1c5cab" + ], + [ + 0.8333333333333334, + "#184f95" + ], + [ + 0.9166666666666666, + "#104281" + ], + [ + 1, + "#0d366b" + ] + ], + "hovertemplate": "%{y} × %{x}: %{z:$,.0f}", + "showscale": false, + "type": "heatmap", + "x": [ + "0.02 tokens/msg", + "0.05 tokens/msg", + "0.1 tokens/msg" + ], + "xgap": 2, + "y": [ + "10.0% auto-respond", + "25.5% auto-respond", + "50.0% auto-respond" + ], + "ygap": 2, + "z": [ + [ + 26532, + 66303, + 132606 + ], + [ + 67688, + 169131, + 338167 + ], + [ + 132606, + 331515, + 663030 + ] + ] + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$27K", + "x": 0, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$66K", + "x": 1, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$133K", + "x": 2, + "y": 0 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$68K", + "x": 0, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$169K", + "x": 1, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$338K", + "x": 2, + "y": 1 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$133K", + "x": 0, + "y": 2 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$332K", + "x": 1, + "y": 2 + }, + { + "font": { + "color": "#ffffff", + "size": 12 + }, + "showarrow": false, + "text": "$663K", + "x": 2, + "y": 2 + } + ], + "bargap": 0.45, + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 380, + "hovermode": "closest", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Email Auto-Respond tokens — 3-yr cost under the unpublished rate
Deck claims a $2.9M 3-yr Email benefit; even the worst corner stays well below it", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + }, + "image/png": "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elmE5m04ce/WWM7B0ji0f5e6Nq2s3PkKa9CkRfcdhAASKoIlvNXOzWs0qO9rOn54r/1+ZaaDd3/tE5UoF7sW+typIzV5xJe68stFla0Spj5vfy3/5IF2LeShX7yl5RHT7P3M6MnWz/zL/uwcLI2jB3brxfahyp6nsD4fFcG2AV6DYAlvYNaanPLdv3X66D47ct5MB2/R6yPlLxXqeHmuwdKseTz4nfY6tneLoqIilTEktxp0eVMFy9TQycN7FP758zp9bJ/dr85RsIyaP/ehMoTkineWcBMsjZVzxmri1/9SaLNn1bTH+/d+I5AwJehgiT/PdUq4OxAs4Y3WrlioV7s31+w1RxSUIqU3vkTgb0ewhLfbuGqR3n++lcYvPqDAYLYNwO9BsIQ32bV+sQa+1UZ9p+1VQBDbAeDPIFg+pgiWwN/jxNGDmj9rsto985JdNgHAbyNYwtuZM30vnTvFnunbzKQB8NsIlvAmZnr2hkXTVLNVHyVhOwD8KQRLuA0jLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkAcEawhNsQLAEABsESAOCKYAkA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" + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── 3-yr Email Auto-Respond token cost: tokens/msg × auto-respond rate ─\n", + "TOK_GRID = [0.02, 0.05, 0.10]\n", + "RATE_GRID = [0.10, 0.255, 0.50]\n", + "\n", + "\n", + "def email_token_cost_3yr(tokens_per_msg, respond_rate):\n", + " m = {**METERS, \"Email AI (Auto-Respond)\": autorespond_meter(tokens_per_msg)}\n", + " sc = claim_scenario(respond_rate)\n", + " return sum(\n", + " calculate_total_cost(sites, EMAIL_SCOPES, m, DEFAULT_PRICING, sc, YEAR_INDEX[y],\n", + " include_platform=False, rollout=EMAIL_TOKEN_ROLLOUT)\n", + " [\"annual_cost\"].sum()\n", + " for y in YEARS)\n", + "\n", + "\n", + "grid = [[email_token_cost_3yr(t, r) for t in TOK_GRID] for r in RATE_GRID]\n", + "zmax = max(max(row) for row in grid)\n", + "fig = go.Figure(go.Heatmap(\n", + " z=grid, x=[f\"{t} tokens/msg\" for t in TOK_GRID],\n", + " y=[f\"{r:.1%} auto-respond\" for r in RATE_GRID],\n", + " colorscale=[[i / (len(SEQ_BLUES) - 1), c] for i, c in enumerate(SEQ_BLUES)],\n", + " showscale=False, xgap=2, ygap=2,\n", + " hovertemplate=\"%{y} × %{x}: %{z:$,.0f}\"))\n", + "for i, r in enumerate(RATE_GRID):\n", + " for j, t in enumerate(TOK_GRID):\n", + " v = grid[i][j]\n", + " fig.add_annotation(x=j, y=i, text=f\"{money(v)}\", showarrow=False,\n", + " font=dict(size=12, color=\"#ffffff\" if v > 0.55 * zmax else INK))\n", + "tei_layout(fig, \"Email Auto-Respond tokens — 3-yr cost under the unpublished rate\",\n", + " \"Deck claims a $2.9M 3-yr Email benefit; even the worst corner stays well below it\",\n", + " height=380)\n", + "fig.update_layout(xaxis=dict(showgrid=False), yaxis=dict(showgrid=False, tickformat=None),\n", + " hovermode=\"closest\")\n", + "fig.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "faa1e95f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3-yr corrected net under implementation-cost assumptions (V2 LoE range × blended rate):\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$175/h$225/h$275/h
low hours18,247,03617,952,03617,657,036
mid hours17,835,78617,423,28617,010,786
high hours17,424,53616,894,53616,364,536
\n", + "
" + ], + "text/plain": [ + " $175/h $225/h $275/h\n", + "low hours 18,247,036 17,952,036 17,657,036\n", + "mid hours 17,835,786 17,423,286 17,010,786\n", + "high hours 17,424,536 16,894,536 16,364,536" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Spread across the whole grid: $1.9M — the conclusion is not hostage to the implementation estimate; tokens and double-billing dominate.\n" + ] + } + ], + "source": [ + "# ── 3-yr corrected net across impl hours mode × blended rate ─────────\n", + "def corrected_net_3yr(mode, rate):\n", + " _, iby, kby, ssby = build_impl_costs(sites, mode, rate, INCLUDE_KB_READINESS,\n", + " COPILOT_INCLUDES_ASIA, NA_EMAIL_EARLY)\n", + " total = {y: licence_by_year[y] + ps_by_year[y] + managed_by_year[y]\n", + " + current_by_year[y] + token_total_by_year[y]\n", + " + iby[y] + kby[y] + ssby[y] for y in YEARS}\n", + " _, net = case_flows(total, benefit_total_by_year)\n", + " return sum(net.values())\n", + "\n", + "\n", + "impl_sens = pd.DataFrame(\n", + " {f\"${r}/h\": {m: corrected_net_3yr(m, r) for m in (\"low\", \"mid\", \"high\")}\n", + " for r in (175, 225, 275)})\n", + "impl_sens.index = [f\"{m} hours\" for m in impl_sens.index]\n", + "print(\"3-yr corrected net under implementation-cost assumptions \"\n", + " \"(V2 LoE range × blended rate):\")\n", + "display(impl_sens)\n", + "spread = float(impl_sens.max().max() - impl_sens.min().min())\n", + "print(f\"Spread across the whole grid: {money(spread)} — the conclusion is not \"\n", + " f\"hostage to the implementation estimate; tokens and double-billing dominate.\")" + ] + }, + { + "cell_type": "markdown", + "id": "46cd8894", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 10 · Verification & assertions\n", + "\n", + "The next cell re-derives key numbers independently and **raises on any failure**, so\n", + "`jupyter nbconvert --execute` acts as a regression gate for this notebook. Config-dependent\n", + "checks are guarded so the cell stays valid after you edit the CONFIG cells." + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "6516ea96", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "All assertions passed.\n", + " benefits 3-yr $15.0M · tokens 3-yr $6.6M · corrected net 3-yr -$4.5M\n" + ] + } + ], + "source": [ + "def _approx(got, want, tol=0.5):\n", + " assert abs(got - want) <= tol, f\"got {got:,.2f}, want {want:,.2f}\"\n", + "\n", + "\n", + "# Section 1 — contract mechanics. Engine pins use explicit default arguments so the\n", + "# gate tests tokencalc.appendix4, not the current widget state; live-state\n", + "# checks only run when the inputs sit at their defaults.\n", + "_seed_check = current_state_inputs(sites)\n", + "_approx(_seed_check[\"annual_cost\"].sum(), 7_300_000)\n", + "_l12 = licence_costs_by_year(12)\n", + "_approx(_l12[2026], 0)\n", + "_approx(_l12[2027], 4_300_000) # deck rate stays verbatim\n", + "assert DEFAULT_RAMP_MONTHS == 6 # 🟢 order form\n", + "_approx(licence_costs_by_year()[2026], 2_150_000) # 6-mo ramp: half-year bills in 2026\n", + "# NTT SOW — billing milestones + managed services (🟢 contractual)\n", + "_approx(PS_CONTRACTED_TOTAL, 2_025_446.48, tol=0.01)\n", + "_ps_c = ps_costs_by_year(contracted=True)\n", + "_approx(_ps_c[2026], 607_633.94 + 405_089.30 + 167_000, tol=0.01) # 30% + 20% + training\n", + "_approx(_ps_c[2027], 607_633.94 + 405_089.30, tol=0.01) # 30% + 20%\n", + "_approx(managed_services_by_year()[2026],\n", + " MANAGED_SERVICES_ANNUAL * 2 / 12, tol=0.01) # Nov–Dec after go-live\n", + "_approx(tco(\"ccaas_annual\"), 3_200_000) # signed contract overlay\n", + "_approx(licence_costs_by_year(12, tco(\"ccaas_annual\"))[2027], 3_200_000)\n", + "_contracts_at_default = (\n", + " (current_state[\"annual_cost\"] - _seed_check[\"annual_cost\"]).abs().max() < 1\n", + " and all(t == dt.date(2027, 12, 31) for t in current_state[\"contract_termination\"])\n", + ")\n", + "# Current spend is excluded from this case → the run-off line is $0 in\n", + "# every year regardless of the (reference-only) widget state.\n", + "for _y in YEARS:\n", + " _approx(current_by_year[_y], 0)\n", + "\n", + "# Section 2 — verbatim benefits reproduce the deck\n", + "_approx(benefit_total_by_year[2026], 0)\n", + "_approx(sum(benefit_total_by_year.values()), verbatim[\"three_yr\"].sum())\n", + "assert abs(sum(benefit_total_by_year.values()) - SLIDE_TOTALS[\"total_3yr\"]) <= _tol(15e6)\n", + "for r in REGIONS:\n", + " _approx(benefits_long.query(\"region == @r\")[\"benefit\"].sum(),\n", + " verbatim.query(\"region == @r\")[\"three_yr\"].sum())\n", + "\n", + "# Section 3 — token hand-checks (independent derivations)\n", + "sta = tokens_long.query(\"cost_line == 'Speech & Text Analytics [named]'\")\n", + "_named = {s.site_name: s.named_users for s in sites}\n", + "_sta_2028 = sum(_named[n] * 30 * TOKEN_ROLLOUT.live_months_in_year(n, 3) for n in ALL_SITES)\n", + "_approx(sta.query(\"year == 2028\")[\"annual_cost\"].sum(), _sta_2028)\n", + "if not COPILOT_INCLUDES_ASIA:\n", + " _approx(_sta_2028, 751_680) # PM timeline: every site live all 12 months in 2028\n", + " cp = tokens_long.query(\"cost_line == 'Agent Copilot [named]' and year == 2028\")\n", + " _approx(cp[\"annual_cost\"].sum(), 1_490 * 40 * 12) # = $715,200\n", + "assert (tokens_long.query(\"cost_line == 'AI Summary & Insights'\")[\"annual_cost\"] == 0).all(), \\\n", + " \"Rule 1: Copilot must cover AI Summary at Copilot sites\"\n", + "assert math.ceil(1_214_358 * DEFAULT_METERS[\"Predictive Routing\"].tokens_per_unit) == 71_433\n", + "# PM timeline: NA live Oct 2026 → tokens DO accrue in 2026 (NA-only).\n", + "_approx(tokens_long.query(\"year == 2026\")[\"annual_cost\"].sum(), 424_350)\n", + "\n", + "# Section 4 — impl model reconciles with the V2 doc (explicit default args)\n", + "_, _impl_p, _kb_p, _steady_p = build_impl_costs(sites, \"mid\", 225, True, False, True)\n", + "_approx(sum(_impl_p.values()), 5_850 * 225) # $1,316,250 — V2's \"$1.3M\"\n", + "_approx(sum(_kb_p.values()), 1_000 * 225)\n", + "_approx(sum(_steady_p.values()), 700 * 225 * 2)\n", + "\n", + "# Section 5 — cost stacks\n", + "_approx(sum(pitched_total_by_year.values()),\n", + " 3 * TCO_VERBATIM[\"ccaas_annual\"] + ps_pitched_by_year[2026]) # ≈ deck's $15.4M\n", + "_approx(sum(corrected_total_by_year.values()),\n", + " sum(licence_by_year.values()) + sum(ps_by_year.values())\n", + " + sum(managed_by_year.values()) + sum(current_by_year.values())\n", + " + sum(token_total_by_year.values()) + sum(impl_kb_by_year.values())\n", + " + sum(steady_by_year.values()))\n", + "\n", + "# Section 8 — flows tie out\n", + "for y in YEARS:\n", + " _approx(net_corrected[y],\n", + " benefit_total_by_year[y] - (corrected_total_by_year[y] - BASELINE_ANNUAL))\n", + "\n", + "backstage(\"All assertions passed.\")\n", + "backstage(f\" benefits 3-yr {money(sum(benefit_total_by_year.values()))} · \"\n", + " f\"tokens 3-yr {money(sum(token_total_by_year.values()))} · \"\n", + " f\"corrected net 3-yr {money(kpi_corrected['net_3yr'])}\")" + ] + }, + { + "cell_type": "markdown", + "id": "8275984f", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 11 · Risks, gaps & next steps\n", + "\n", + "**Findings to lead with**\n", + "- **Predictive Routing is net-negative standalone at claimed scope:** ~$1.8M/yr in tokens at 100%\n", + " eligibility vs a $470K/yr claimed benefit. `PR_ELIGIBILITY` is the honest knob — Genesys likely\n", + " intends PR on a queue subset; ask which queues.\n", + "- **2026 is a pure cost year** — $0 benefits (earliest region, NA, realizes Jan 2027 on the PM\n", + " timeline), while CTM already funds PS, licences (post-ramp) and early AI implementation.\n", + "- **Current spend is excluded** (CTM directive): no double-billing penalty and no\n", + " cost-avoidance credit. This is a harsher frame than the deck's — the standalone Genesys\n", + " programme is thin on its own, which is exactly why the **virtual-agents alternative**\n", + " (`ctm_business_case_virtual_agents.ipynb`) is the case to lead with.\n", + "- The cost corrections are not one-directional: the ramp credit (6 licence-free months, 🟢\n", + " order form) and the contracted licence rate ($3.2M/yr vs the pitched $4.3M) improve the\n", + " case, while the SOW's managed services ($411K/yr from MCX go-live) add a run-rate cost\n", + " the deck's TCO never carried.\n", + "\n", + "**Known gaps / data wanted**\n", + "- Non-NAM site volumes & AHTs are `tokencalc` placeholders (🟡) — all non-NA token figures inherit\n", + " that. Regional current-cost split is an agent-share allocation pending real contract data.\n", + "- Email Auto-Respond token rate is unpublished (🔴) — section 9 bounds it; even the worst corner is small\n", + " relative to the Email benefit claim.\n", + "- Early-termination fees, co-term options, and migration costs beyond PS/impl are not modelled.\n", + "- The smell test (section 4) flags V2 implementation hours as below the 20–40% industry band — V2\n", + " deliberately strips vendor inflation; section 9 shows the conclusion is robust across the whole\n", + " V2 range × rate grid.\n", + "- Deck internal rounding: cell-level benefits cross-foot to $15.04M vs the $15.0M headline\n", + " (and $13.72M vs $13.6M annual); tolerated, not \"fixed\".\n", + "\n", + "**Next steps**\n", + "- Replace section 4's hours-range model with the activity-level `ImplementationEffort` engine sketched in\n", + " `docs/ctm_ai_labour_estimate.md` (dataclasses + `tokencalc/implementation.py` + tests).\n", + "- Confirm contracted token rates and regional pricing (EU/AU/APAC flagged TBD at $1.00 list).\n", + "- Feed real per-region current-platform contract values and termination dates into section 1." + ] + }, + { + "cell_type": "markdown", + "id": "8ce1bad2", + "metadata": {}, + "source": [ + "\n", + "\n", + "## 12 · Machine-readable data appendix\n", + "\n", + "Every number behind the figures above, emitted as markdown tables plus a JSON block, so an\n", + "LLM can draft the client report or presentation directly from the HTML/markdown export\n", + "(`python scripts/export_report.py`). Plotly figures export as JavaScript an LLM cannot\n", + "read — this section carries the data. The dump renders **backstage** (JupyterLab and the exports) and stays hidden in the Mercury app. The tables reflect the **current** input state, so a\n", + "client-customized session exports a client-customized appendix.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "c4c025b5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "#### Current-state contracts by region (inputs)\n", + "\n", + "| region | agents | annual_cost | contract_termination | confidence |\n", + "|:---------|---------:|--------------:|:-----------------------|:------------------------------------------------|\n", + "| NA | 890 | 3,348,969 | 2027-12-31 | 🟡 agent-share allocation of the verbatim total |\n", + "| ANZ | 180 | 677,320 | 2027-12-31 | 🟡 agent-share allocation of the verbatim total |\n", + "| EMEA | 320 | 1,204,124 | 2027-12-31 | 🟡 agent-share allocation of the verbatim total |\n", + "| ASIA | 550 | 2,069,588 | 2027-12-31 | 🟡 agent-share allocation of the verbatim total |\n", + "\n", + "#### Verbatim Genesys benefits — capability × region (3-yr $)\n", + "\n", + "| capability | ANZ | ASIA | EMEA | NA |\n", + "|:-------------------|--------:|-------:|-------:|--------:|\n", + "| Agent Copilot | 3900000 | 0 | 64000 | 3400000 |\n", + "| WFM | 1400000 | 914000 | 687000 | 0 |\n", + "| Email | 143000 | 93000 | 235000 | 2500000 |\n", + "| STA | 105000 | 72000 | 131000 | 506000 |\n", + "| Predictive Routing | 302000 | 51000 | 6000 | 167000 |\n", + "| Supervisor Copilot | 27000 | 0 | 49000 | 291000 |\n", + "\n", + "#### Benefits by capability × year (schedule-phased, $)\n", + "\n", + "| capability | 2026 | 2027 | 2028 |\n", + "|:-------------------|-------:|----------:|----------:|\n", + "| Agent Copilot | 0 | 2,700,600 | 4,663,400 |\n", + "| WFM | 0 | 624,800 | 2,376,200 |\n", + "| Email | 0 | 1,379,750 | 1,591,250 |\n", + "| STA | 0 | 331,650 | 482,350 |\n", + "| Predictive Routing | 0 | 161,400 | 364,600 |\n", + "| Supervisor Copilot | 0 | 171,850 | 195,150 |\n", + "\n", + "#### AI token cost by meter × year ($)\n", + "\n", + "| cost_line | 2026 | 2027 | 2028 | 3-yr |\n", + "|:----------------------------------------|--------:|----------:|----------:|----------:|\n", + "| Predictive Routing | 214,299 | 1,362,323 | 1,851,816 | 3,428,438 |\n", + "| Agent Copilot [named] | 114,000 | 662,400 | 715,200 | 1,491,600 |\n", + "| Speech & Text Analytics [named] | 85,500 | 550,620 | 751,680 | 1,387,800 |\n", + "| Email AI (Auto-Respond) | 10,551 | 67,080 | 91,500 | 169,131 |\n", + "| AI Translate | 0 | 34,185 | 69,540 | 103,725 |\n", + "| AI Summary & Insights | 0 | 0 | 0 | 0 |\n", + "| WFM (no token meter — licence-included) | 0 | 0 | 0 | 0 |\n", + "| TOTAL | 424,350 | 2,676,608 | 3,479,736 | 6,580,694 |\n", + "\n", + "#### AI implementation by workstream ($, phased)\n", + "\n", + "| workstream | hours | cost | 2026 | 2027 | 2028 |\n", + "|:-----------------------------------------------------|--------:|----------:|----------:|--------:|--------:|\n", + "| Agent Copilot | 1,500 | 337,500 | 311,832 | 25,668 | 0 |\n", + "| Cross-cutting (PM, governance, testing, integration) | 1,400 | 315,000 | 257,242 | 57,758 | 0 |\n", + "| Email Auto-Respond | 1,100 | 247,500 | 202,119 | 45,381 | 0 |\n", + "| KB readiness (prerequisite) | 1,000 | 225,000 | 183,744 | 41,256 | 0 |\n", + "| Predictive Routing | 550 | 123,750 | 101,059 | 22,691 | 0 |\n", + "| STA | 1,000 | 225,000 | 183,744 | 41,256 | 0 |\n", + "| Supervisor Copilot | 300 | 67,500 | 62,366 | 5,134 | 0 |\n", + "| Steady-state tuning (2027-28) | 1,400 | 315,000 | 0 | 157,500 | 157,500 |\n", + "| TOTAL | 8,250 | 1,856,250 | 1,302,108 | 396,642 | 157,500 |\n", + "\n", + "#### Corrected programme cost stack ($)\n", + "\n", + "| | 2026 | 2027 | 2028 | 3-yr |\n", + "|:----------------------------------------------------|----------:|----------:|----------:|----------:|\n", + "| CCaaS platform licences (contracted, ramp-adjusted) | 1,600,000 | 3,200,000 | 3,200,000 | 8,000,000 |\n", + "| NTT PS + training (SOW billing milestones) | 1,179,723 | 1,012,723 | 0 | 2,192,446 |\n", + "| NTT managed services (from MCX go-live) | 68,486 | 410,918 | 410,918 | 890,323 |\n", + "| AI token consumption | 424,350 | 2,676,608 | 3,479,736 | 6,580,694 |\n", + "| AI implementation + KB readiness | 1,302,108 | 239,142 | 0 | 1,541,250 |\n", + "| AI steady-state tuning | 0 | 157,500 | 157,500 | 315,000 |\n", + "\n", + "#### As-pitched cost stack ($)\n", + "\n", + "| | 2026 | 2027 | 2028 | 3-yr |\n", + "|:----------------------------------------------|----------:|----------:|----------:|-----------:|\n", + "| CCaaS platform licences (as pitched, no ramp) | 4,300,000 | 4,300,000 | 4,300,000 | 12,900,000 |\n", + "| Base professional services + training | 2,567,000 | 0 | 0 | 2,567,000 |\n", + "\n", + "#### KPIs — as pitched vs corrected\n", + "\n", + "| | As pitched (deck) | Corrected |\n", + "|:-----------------------------|:--------------------|:------------|\n", + "| 3-yr benefits | $15.0M | $15.0M |\n", + "| 3-yr incremental cost | $15.5M | $19.5M |\n", + "| 3-yr net | -$424K | -$4.5M |\n", + "| ROI (net ÷ incremental cost) | -3% | -23% |\n", + "| NPV @ 13.5% (deck rate) | -$1.5M | -$4.2M |\n", + "| NPV @ 8.0% (CTM treasury) | -$1.2M | -$4.3M |\n", + "| Payback | beyond 2028 | beyond 2028 |\n", + "\n", + "#### Model state (JSON)\n", + "\n", + "```json\n", + "{\n", + " \"benefits_by_year\": {\n", + " \"2026\": 0,\n", + " \"2027\": 5370050,\n", + " \"2028\": 9672950\n", + " },\n", + " \"corrected_cost_by_year\": {\n", + " \"2026\": 4574668,\n", + " \"2027\": 7696892,\n", + " \"2028\": 7248154\n", + " },\n", + " \"pitched_cost_by_year\": {\n", + " \"2026\": 6867000,\n", + " \"2027\": 4300000,\n", + " \"2028\": 4300000\n", + " },\n", + " \"net_by_year_corrected\": {\n", + " \"2026\": -4574668,\n", + " \"2027\": -2326842,\n", + " \"2028\": 2424796\n", + " },\n", + " \"kpis_corrected\": {\n", + " \"benefits_3yr\": 15043000,\n", + " \"incremental_cost_3yr\": 19519714,\n", + " \"net_3yr\": -4476714,\n", + " \"roi\": -0.2293,\n", + " \"npv\": -4178389,\n", + " \"discount_rate\": 0.135,\n", + " \"payback\": \"beyond 2028\"\n", + " },\n", + " \"kpis_pitched\": {\n", + " \"benefits_3yr\": 15043000,\n", + " \"incremental_cost_3yr\": 15467000,\n", + " \"net_3yr\": -424000,\n", + " \"roi\": -0.0274,\n", + " \"npv\": -1544853,\n", + " \"discount_rate\": 0.135,\n", + " \"payback\": \"beyond 2028\"\n", + " },\n", + " \"assumptions\": {\n", + " \"ramp_months\": 6,\n", + " \"licence_annual\": 3200000,\n", + " \"deck_ccaas_annual\": 4300000,\n", + " \"ps_contracted_total\": 2025446.48,\n", + " \"ps_milestones\": [\n", + " {\n", + " \"name\": \"SOW Effective Date\",\n", + " \"date\": \"2026-03-15\",\n", + " \"amount\": 607633.94\n", + " },\n", + " {\n", + " \"name\": \"Start of client UAT (first region, NA)\",\n", + " \"date\": \"2026-10-16\",\n", + " \"amount\": 405089.3\n", + " },\n", + " {\n", + " \"name\": \"Start of client UAT (last region, APAC)\",\n", + " \"date\": \"2027-10-01\",\n", + " \"amount\": 607633.94\n", + " },\n", + " {\n", + " \"name\": \"Completion of last go-live migration\",\n", + " \"date\": \"2027-11-01\",\n", + " \"amount\": 405089.3\n", + " }\n", + " ],\n", + " \"managed_services_annual\": 410918.4,\n", + " \"mcx_go_live\": \"2026-10-16\",\n", + " \"hours_mode\": \"mid\",\n", + " \"blended_rate\": 225,\n", + " \"include_kb_readiness\": true,\n", + " \"copilot_includes_asia\": false,\n", + " \"na_email_early\": true,\n", + " \"pr_eligibility\": 1.0,\n", + " \"email_auto_respond_rate\": 0.255,\n", + " \"email_tokens_per_msg\": 0.05,\n", + " \"use_contracted_rates\": false,\n", + " \"discount_rate\": 0.135,\n", + " \"current_state_by_region\": {\n", + " \"NA\": {\n", + " \"annual_cost\": 3348969,\n", + " \"contract_termination\": \"2027-12-31\"\n", + " },\n", + " \"ANZ\": {\n", + " \"annual_cost\": 677320,\n", + " \"contract_termination\": \"2027-12-31\"\n", + " },\n", + " \"EMEA\": {\n", + " \"annual_cost\": 1204124,\n", + " \"contract_termination\": \"2027-12-31\"\n", + " },\n", + " \"ASIA\": {\n", + " \"annual_cost\": 2069588,\n", + " \"contract_termination\": \"2027-12-31\"\n", + " }\n", + " }\n", + " }\n", + "}\n", + "```\n" + ] + } + ], + "source": [ + "# ── Data appendix — LLM-readable dump of every model output ──────────\n", + "# Renders backstage only (JupyterLab / nbconvert exports) — hidden on\n", + "# the Mercury stage, where the narrative and figures carry the story.\n", + "import json as _json\n", + "\n", + "\n", + "def _section(title, df, **kw):\n", + " backstage(f\"\\n#### {title}\\n\")\n", + " backstage(df.to_markdown(floatfmt=\",.0f\", **kw))\n", + "\n", + "\n", + "_section(\"Current-state contracts by region (inputs)\",\n", + " current_state.reset_index().drop(columns=[\"share\"]), index=False)\n", + "_section(\"Verbatim Genesys benefits — capability × region (3-yr $)\",\n", + " verbatim.pivot_table(index=\"capability\", columns=\"region\",\n", + " values=\"three_yr\", aggfunc=\"sum\").reindex(CAPABILITIES))\n", + "_section(\"Benefits by capability × year (schedule-phased, $)\",\n", + " benefits_long.pivot_table(index=\"capability\", columns=\"year\",\n", + " values=\"benefit\", aggfunc=\"sum\").reindex(CAPABILITIES))\n", + "_section(\"AI token cost by meter × year ($)\", tokens_pivot.drop(columns=[\"conf\"]))\n", + "_section(\"AI implementation by workstream ($, phased)\", impl_summary)\n", + "_section(\"Corrected programme cost stack ($)\", corrected_costs)\n", + "_section(\"As-pitched cost stack ($)\", pitched_costs)\n", + "_section(\"KPIs — as pitched vs corrected\", kpis_fmt)\n", + "\n", + "\n", + "def _jval(v):\n", + " if isinstance(v, float):\n", + " return round(v, 4) if abs(v) < 10 else round(v)\n", + " return v\n", + "\n", + "\n", + "backstage(\"\\n#### Model state (JSON)\\n\")\n", + "backstage(\"```json\")\n", + "backstage(_json.dumps({\n", + " \"benefits_by_year\": {str(y): round(benefit_total_by_year[y]) for y in YEARS},\n", + " \"corrected_cost_by_year\": {str(y): round(corrected_total_by_year[y]) for y in YEARS},\n", + " \"pitched_cost_by_year\": {str(y): round(pitched_total_by_year[y]) for y in YEARS},\n", + " \"net_by_year_corrected\": {str(y): round(net_corrected[y]) for y in YEARS},\n", + " \"kpis_corrected\": {k: _jval(v) for k, v in kpi_corrected.items()},\n", + " \"kpis_pitched\": {k: _jval(v) for k, v in kpi_pitched.items()},\n", + " \"assumptions\": {\n", + " \"ramp_months\": RAMP_MONTHS,\n", + " \"licence_annual\": round(LICENCE_ANNUAL),\n", + " \"deck_ccaas_annual\": round(TCO_VERBATIM[\"ccaas_annual\"]),\n", + " \"ps_contracted_total\": round(PS_CONTRACTED_TOTAL, 2),\n", + " \"ps_milestones\": [{\"name\": m[\"name\"], \"date\": m[\"date\"].isoformat(),\n", + " \"amount\": m[\"amount\"]} for m in PS_MILESTONES],\n", + " \"managed_services_annual\": MANAGED_SERVICES_ANNUAL,\n", + " \"mcx_go_live\": MCX_GO_LIVE.isoformat(),\n", + " \"hours_mode\": HOURS_MODE,\n", + " \"blended_rate\": BLENDED_RATE,\n", + " \"include_kb_readiness\": INCLUDE_KB_READINESS,\n", + " \"copilot_includes_asia\": COPILOT_INCLUDES_ASIA,\n", + " \"na_email_early\": NA_EMAIL_EARLY,\n", + " \"pr_eligibility\": PR_ELIGIBILITY,\n", + " \"email_auto_respond_rate\": EMAIL_AUTO_RESPOND_RATE,\n", + " \"email_tokens_per_msg\": EMAIL_AUTORESPOND_TOKENS_PER_MSG,\n", + " \"use_contracted_rates\": USE_CONTRACTED_RATES,\n", + " \"discount_rate\": DISCOUNT_RATE,\n", + " \"current_state_by_region\": {\n", + " r: {\"annual_cost\": round(float(current_state.loc[r, \"annual_cost\"])),\n", + " \"contract_termination\": current_state.loc[r, \"contract_termination\"].isoformat()}\n", + " for r in REGIONS},\n", + " },\n", + "}, indent=2))\n", + "backstage(\"```\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "61677b64-31c4-4b42-bba7-2fe4031c6e5d", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "017a151c582947e3863296a36265e531": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "07dc99326a784494a3cd7debc40f00b5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0c130b50aae44b58b1a908d97cd5bd5b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0ea9dd580548444480ebcdf1d670bd3d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0fedd1577870444797ca09ff5585eda1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "102e5a07ae634521b8b227533ec3efd6": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA — contract termination", + "layout": "IPY_MODEL_b13cb118cd464b7b8f3681b409c82645", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "1aa2430b53bf49f1a0447e0c8d912ef1": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "EMEA — contract termination", + "layout": "IPY_MODEL_92b7bca1e3924286b4d33f135e7e458f", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "25f437e26077490e8e6d4cacf5d9aef4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "33904adcad3846a889f6fc54b7547d72": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Email auto-respond rate", + "layout": "IPY_MODEL_0c130b50aae44b58b1a908d97cd5bd5b", + "layout_path": null, + "max": 0.6, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 0.005, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 0.255 + } + }, + "3671a93bb2c84529a7df60afb5f7717f": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA — current platform cost ($/yr)", + "layout": "IPY_MODEL_a81c32a4e2fe4dbea2cbe6fc8f931dc5", + "layout_path": null, + "max": 8000000, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3348969 + } + }, + "3ca4dd3dd7e94b3d96da593e59135568": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "4f247778c0c142c3b3e33fe3c305f99c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4f6b392701a84b0bab2fe540e6f4e7d0": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "NA Email implements early (Jan 2027)", + "last_changed_at": "", + "layout": "IPY_MODEL_bf8c77a2a21d4241a190c49f217aa324", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "59f45f967ddd43488462821fd145a655": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ANZ — current platform cost ($/yr)", + "layout": "IPY_MODEL_8697c0d2b9444a0cbf8fbb6530eb07ef", + "layout_path": null, + "max": 8000000, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 677320 + } + }, + "5eaafef4e78f4aa78714b5674c17aee0": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6263f6cd732244a695f8a03352f62a73": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "680921ece5304830999d27ed3b568d10": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "70858dc708fb458a9d959c19f42f5797": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7c917d16558d495e99f6d7c22e830786": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "cell_id": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_6263f6cd732244a695f8a03352f62a73", + "layout_path": null, + "placeholder": "​", + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "style": "IPY_MODEL_3ca4dd3dd7e94b3d96da593e59135568", + "tabbable": null, + "tooltip": null, + "value": "" + } + }, + "7fdb7cf5a0aa476cbea97513d13f3218": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8697c0d2b9444a0cbf8fbb6530eb07ef": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8be1579c5e304bf0a7c1a211c5525c3a": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "EMEA — current platform cost ($/yr)", + "layout": "IPY_MODEL_5eaafef4e78f4aa78714b5674c17aee0", + "layout_path": null, + "max": 8000000, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 1204124 + } + }, + "8d41dfe1b7244b3e8117abf9384baebd": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ASIA — current platform cost ($/yr)", + "layout": "IPY_MODEL_25f437e26077490e8e6d4cacf5d9aef4", + "layout_path": null, + "max": 8000000, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 2069588 + } + }, + "8effbc33f36444908062fbf8d6a02c28": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Auto-respond tokens per message 🟡", + "layout": "IPY_MODEL_9d250ae0110b4730bc22ead4d2ff898e", + "layout_path": null, + "max": 0.5, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 0.005, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 0.05 + } + }, + "92b7bca1e3924286b4d33f135e7e458f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "953d50cd99ce4cb9ac53b234e5715af4": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ANZ — contract termination", + "layout": "IPY_MODEL_f0608ef0d6a74ac9aca6e4230926afbc", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "980b4421a67b435abb982f8a16742897": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9d250ae0110b4730bc22ead4d2ff898e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a81c32a4e2fe4dbea2cbe6fc8f931dc5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a86270961a634af691f138beb4476834": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "CCaaS licence run-rate ($/yr) — contracted", + "layout": "IPY_MODEL_07dc99326a784494a3cd7debc40f00b5", + "layout_path": null, + "max": 10000000, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 100000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3200000 + } + }, + "b13cb118cd464b7b8f3681b409c82645": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "bf8c77a2a21d4241a190c49f217aa324": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "c504335a40ff419fac8c15da3ab9c673": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Include KB readiness (500–1,500 h)", + "last_changed_at": "", + "layout": "IPY_MODEL_4f247778c0c142c3b3e33fe3c305f99c", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "c5225c1fa18141c994e18b0206c5d5da": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cbe9d046880d47efa96880af499d66a5": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.slider.SliderWidget", + "_css": "\n .mljar-slider-container {\n --mljar-slider-thumb-size: 16px;\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 0;\n width: 100%;\n max-width: 100%;\n min-width: 120px;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n font-weight: normal;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-slider-stage {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n padding-top: 20px;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-floating-value {\n position: absolute;\n top: 0;\n left: 8px;\n transform: translateX(-50%);\n color: #007bff;\n font-weight: 700;\n font-size: 0.95em;\n line-height: 1;\n text-align: center;\n white-space: nowrap;\n pointer-events: none;\n z-index: 1;\n }\n\n .mljar-slider-top-label {\n margin-bottom: 6px;\n font-weight: 600;\n line-height: 1.2;\n }\n\n .mljar-slider-row {\n position: relative;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n overflow: visible;\n box-sizing: border-box;\n }\n\n .mljar-slider-minmax-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n margin-top: 6px;\n color: #616673;\n font-size: 0.9em;\n line-height: 1.2;\n box-sizing: border-box;\n }\n\n .mljar-slider-min-label,\n .mljar-slider-max-label {\n color: inherit;\n white-space: nowrap;\n }\n\n .mljar-slider-input {\n display: block;\n width: 100%;\n max-width: 100%;\n min-width: 0;\n background: transparent;\n -webkit-appearance: none;\n appearance: none;\n border: none;\n height: 24px;\n padding: 0;\n margin: 0;\n cursor: pointer;\n box-sizing: border-box;\n }\n\n .mljar-slider-input:focus {\n outline: none;\n }\n\n .mljar-slider-input:focus-visible::-webkit-slider-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n .mljar-slider-input:focus-visible::-moz-range-thumb {\n box-shadow: 0 0 0 3px #e6f2ff;\n }\n\n /* Track */\n .mljar-slider-input::-webkit-slider-runnable-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n margin: auto;\n }\n .mljar-slider-input::-moz-range-track {\n height: 6px;\n background: #e0e0e0;\n border-radius: 6px;\n }\n\n /* Thumb */\n .mljar-slider-input::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n margin-top: -5px;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n .mljar-slider-input::-moz-range-thumb {\n width: var(--mljar-slider-thumb-size);\n height: var(--mljar-slider-thumb-size);\n border-radius: 50%;\n background: #007bff;\n cursor: pointer;\n transition: transform 0.14s ease, background-color 0.14s ease;\n }\n\n .mljar-slider-input:disabled {\n cursor: not-allowed;\n opacity: 0.7;\n }\n\n .mljar-slider-input:disabled::-webkit-slider-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:disabled::-moz-range-thumb {\n cursor: not-allowed;\n transform: none;\n }\n\n .mljar-slider-input:active::-webkit-slider-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n .mljar-slider-input:active::-moz-range-thumb {\n transform: scale(1.08);\n background: #007bff;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-slider-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-slider-top-label\");\n\n const sliderStage = document.createElement(\"div\");\n sliderStage.classList.add(\"mljar-slider-stage\");\n\n const floatingValueLabel = document.createElement(\"div\");\n floatingValueLabel.classList.add(\"mljar-slider-floating-value\");\n\n const sliderRow = document.createElement(\"div\");\n sliderRow.classList.add(\"mljar-slider-row\");\n\n const slider = document.createElement(\"input\");\n slider.type = \"range\";\n slider.classList.add(\"mljar-slider-input\");\n\n const minMaxRow = document.createElement(\"div\");\n minMaxRow.classList.add(\"mljar-slider-minmax-row\");\n\n const minLabel = document.createElement(\"span\");\n minLabel.classList.add(\"mljar-slider-min-label\");\n\n const maxLabel = document.createElement(\"span\");\n maxLabel.classList.add(\"mljar-slider-max-label\");\n\n function positionFloatingValue() {\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const value = Number(model.get(\"value\"));\n const range = max - min;\n const ratio = range <= 0 ? 0 : (value - min) / range;\n const clampedRatio = Math.max(0, Math.min(1, ratio));\n const computed = getComputedStyle(container);\n const thumbSize =\n parseFloat(computed.getPropertyValue(\"--mljar-slider-thumb-size\")) || 16;\n const inputWidth = slider.clientWidth || 0;\n const stageWidth = sliderStage.clientWidth || inputWidth || 0;\n const labelWidth = floatingValueLabel.offsetWidth || 0;\n\n if (inputWidth <= 0 || stageWidth <= 0) {\n return;\n }\n\n const sliderOffsetLeft = slider.offsetLeft || 0;\n const usableWidth = Math.max(0, inputWidth - thumbSize);\n const idealCenter =\n sliderOffsetLeft + thumbSize / 2 + clampedRatio * usableWidth;\n const halfLabel = labelWidth / 2;\n const minCenter = halfLabel;\n const maxCenter = Math.max(halfLabel, stageWidth - halfLabel);\n const finalCenter = Math.min(Math.max(idealCenter, minCenter), maxCenter);\n\n floatingValueLabel.style.left = `${finalCenter}px`;\n }\n\n function syncFromModel() {\n slider.min = model.get(\"min\");\n slider.max = model.get(\"max\");\n slider.value = model.get(\"value\");\n floatingValueLabel.textContent = String(model.get(\"value\"));\n minLabel.textContent = String(model.get(\"min\"));\n maxLabel.textContent = String(model.get(\"max\"));\n\n slider.disabled = !!model.get(\"disabled\");\n topLabel.textContent = model.get(\"label\") || \"Select number\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n positionFloatingValue();\n }\n\n let debounceTimer = null;\n slider.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", Number(slider.value));\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n model.save_changes();\n }, 100);\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n sliderRow.appendChild(slider);\n minMaxRow.appendChild(minLabel);\n minMaxRow.appendChild(maxLabel);\n\n container.appendChild(topLabel);\n sliderStage.appendChild(floatingValueLabel);\n sliderStage.appendChild(sliderRow);\n sliderStage.appendChild(minMaxRow);\n container.appendChild(sliderStage);\n el.appendChild(container);\n\n syncFromModel();\n\n const resizeObserver = new ResizeObserver(() => {\n positionFloatingValue();\n });\n resizeObserver.observe(sliderStage);\n\n return () => {\n resizeObserver.disconnect();\n };\n\n // ---- read cell id (no DOM modifications) ----\n /*const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Predictive Routing eligibility (%)", + "layout": "IPY_MODEL_980b4421a67b435abb982f8a16742897", + "layout_path": null, + "max": 100, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 100 + } + }, + "e05a62420de1441cb674d21455c9bcac": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "AI Translate eligibility 🟡", + "layout": "IPY_MODEL_70858dc708fb458a9d959c19f42f5797", + "layout_path": null, + "max": 1, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 0.01, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 0.01 + } + }, + "e074535aaefe479db9b3af810e3c0091": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "175", + "225", + "275" + ], + "disabled": false, + "hidden": false, + "label": "Blended rate ($/h)", + "layout": "IPY_MODEL_0fedd1577870444797ca09ff5585eda1", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "225" + } + }, + "e2dee7e397234208a377bd44d2046974": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "13.5% (deck)", + "8.0% (CTM treasury)" + ], + "disabled": false, + "hidden": false, + "label": "NPV discount rate", + "layout": "IPY_MODEL_680921ece5304830999d27ed3b568d10", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "13.5% (deck)" + } + }, + "e631b576de444ebd85b131ce9bb235a6": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Copilot includes ASIA sites", + "last_changed_at": "", + "layout": "IPY_MODEL_0ea9dd580548444480ebcdf1d670bd3d", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": false + } + }, + "e86eb5e70e034abab1e1c23d3d21d00b": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Genesys ramp — licence-free months", + "layout": "IPY_MODEL_017a151c582947e3863296a36265e531", + "layout_path": null, + "max": 24, + "min": 0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 1, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 6 + } + }, + "f0608ef0d6a74ac9aca6e4230926afbc": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f474fa6c1e744e09983ed11f7cde918a": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ASIA — contract termination", + "layout": "IPY_MODEL_c5225c1fa18141c994e18b0206c5d5da", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "f93ddd9cd9f941ad944210cc0245ae78": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "low", + "mid", + "high" + ], + "disabled": false, + "hidden": false, + "label": "Impl hours (V2 range)", + "layout": "IPY_MODEL_7fdb7cf5a0aa476cbea97513d13f3218", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "mid" + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_virtual_agents.ipynb b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_virtual_agents.ipynb new file mode 100644 index 0000000..2956f91 --- /dev/null +++ b/studies/202607_CTM_GenesysCX/notebooks/ctm_business_case_virtual_agents.ipynb @@ -0,0 +1,3768 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a4a92a25", + "metadata": {}, + "source": [ + "# CTM × Genesys CCaaS — Virtual-Agents Business Case (5-Year)\n", + "\n", + "**The strategic reframe.** Contact-centre transformation is not a 3-year decision. The Genesys\n", + "case was built on a 3-year window because that is how vendor cases work — it forces every benefit\n", + "forward and waves away every dependency. The honest question is whether the transformation pencils\n", + "over the **natural 5-year lifecycle** of the platform investment. On that basis the case is very\n", + "different — and it is carried by **containment** (Voice Bot, then Agentic VA), not feature\n", + "productivity.\n", + "\n", + "**Honest phasing (this is the correction that makes it defensible):**\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|---|---|---|---|---|---|\n", + "| **Voice Bot** | $0 — no ESB / integration | mid-year go-live, partial | ramping to mature | mature | mature |\n", + "| **Agentic VA** | $0 | $0 | pilot / build | partial-year go-live, ramp | mature |\n", + "| **Cost** | nothing live | VB tokens H2 + impl loads | VB mature, VA build | both running, VA ramping | both mature |\n", + "\n", + "- Real economics don't start until **2027**; 2026 is near-zero on both sides.\n", + "- Agentic VA gets the runway it needs — its Y1 is **2029**, by which point implementation is\n", + " amortized and adoption is tuning, so its benefit-to-cost ratio can breathe.\n", + "- The **5-year cumulative view is the headline**, discounted at CTM's treasury rate (**8% primary**,\n", + " 13.5% sensitivity — the discount effect is material over five years).\n", + "\n", + "Scenarios: **Floor** and **Realistic** only (Stretch dropped from anything client-facing). Anchor on\n", + "Realistic. Benefit realization uses the pressure-tested `tokencalc` haircuts\n", + "(`va_completion_rate × va_labour_realization × (1 − va_callback_discount)`).\n", + "\n", + "*Serve with `mercury --working-dir notebooks/`; the sidebar tunes the phasing, deflection and\n", + "discount-rate live.*" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "9b5a64ed", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "tokencalc loaded — 9 sites, 1,940 agents\n" + ] + } + ], + "source": [ + "# ── Setup ──────────────────────────────────────────────────────────\n", + "import sys, pathlib\n", + "_ROOT = pathlib.Path.cwd()\n", + "if not (_ROOT / \"tokencalc\").exists():\n", + " _ROOT = _ROOT.parent\n", + "sys.path.insert(0, str(_ROOT))\n", + "\n", + "import dataclasses\n", + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "\n", + "import mercury as mr\n", + "from tokencalc import *\n", + "from tokencalc import appendix4 as a4\n", + "from tokencalc.rollout import NO_ROLLOUT\n", + "from tokencalc.scenarios import SCENARIOS\n", + "from tokencalc.benefit_model import calculate_va_deflection_benefit\n", + "from tokencalc.cost_model import calculate_total_cost\n", + "from tokencalc.staging import backstage\n", + "\n", + "pd.options.display.float_format = \"{:,.0f}\".format\n", + "money = a4.money\n", + "html_money = a4.html_money\n", + "\n", + "# ── Chart chrome ────────────────────────────────────────────────────\n", + "INK, INK2, MUTED = \"#0b0b0b\", \"#52514e\", \"#898781\"\n", + "SURFACE, GRID, BASELINE = \"#fcfcfb\", \"#e1e0d9\", \"#c3c2b7\"\n", + "CUMULATIVE, CONTEXT = \"#52514e\", \"#c3c2b7\"\n", + "FONT_STACK = 'system-ui, -apple-system, \"Segoe UI\", sans-serif'\n", + "SCEN_COLOR = {\"floor\": \"#eda100\", \"realistic\": \"#2a78d6\"}\n", + "COST_COLOR = {\"Platform licence (voice-attributable)\": \"#c3c2b7\",\n", + " \"Implementation\": \"#4a3aa7\",\n", + " \"Voice Bot tokens\": \"#2a78d6\",\n", + " \"Agentic VA tokens\": \"#1baf7a\"}\n", + "BEN_COLOR = {\"Voice Bot deflection\": \"#2a78d6\", \"Agentic VA deflection\": \"#1baf7a\"}\n", + "\n", + "\n", + "def tei_layout(fig, title, subtitle=None, height=460):\n", + " t = f\"{title}\"\n", + " if subtitle:\n", + " t += f\"
{subtitle}\"\n", + " fig.update_layout(\n", + " title=dict(text=t, font=dict(size=16, color=INK), x=0.02, xanchor=\"left\"),\n", + " paper_bgcolor=SURFACE, plot_bgcolor=SURFACE,\n", + " font=dict(family=FONT_STACK, size=12, color=INK2),\n", + " legend=dict(orientation=\"h\", yanchor=\"top\", y=-0.10, x=0,\n", + " font=dict(size=11, color=INK2)),\n", + " xaxis=dict(type=\"category\", showgrid=False, linecolor=BASELINE,\n", + " tickfont=dict(color=MUTED)),\n", + " yaxis=dict(gridcolor=GRID, zerolinecolor=BASELINE, zerolinewidth=1.5,\n", + " tickformat=\"$~s\", tickfont=dict(color=MUTED)),\n", + " hovermode=\"x unified\", bargap=0.42, height=height,\n", + " margin=dict(t=70, r=30, b=80, l=70))\n", + " return fig\n", + "\n", + "\n", + "def bar(x, y, name, color):\n", + " return go.Bar(x=x, y=y, name=name,\n", + " marker=dict(color=color, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "def cum_line(x, y, name, color=CUMULATIVE, dash=None):\n", + " return go.Scatter(x=x, y=y, name=name, mode=\"lines+markers\",\n", + " line=dict(color=color, width=2, dash=dash),\n", + " marker=dict(size=8, line=dict(width=2, color=SURFACE)),\n", + " hovertemplate=\"%{fullData.name}: %{y:$,.0f}\")\n", + "\n", + "\n", + "YEARS = [2026, 2027, 2028, 2029, 2030] # 5-year lifecycle window\n", + "X = [str(y) for y in YEARS]\n", + "sites = list(CTM_DEFAULT_SITES)\n", + "ALL = [s.site_name for s in sites]\n", + "meters, pricing = dict(DEFAULT_METERS), dict(DEFAULT_PRICING)\n", + "backstage(f\"tokencalc loaded — {len(sites)} sites, {sum(s.agents for s in sites):,} agents\")" + ] + }, + { + "cell_type": "markdown", + "id": "a45a5a8c", + "metadata": {}, + "source": [ + "## 1 · Mature-year anchors (from the token engine)\n", + "\n", + "Steady-state (all sites live, full realization) benefit and token cost for each feature, computed\n", + "by the `tokencalc` deflection + cost engines at the chosen scenario. These are the *mature* numbers;\n", + "the phasing in §2 ramps them into each year." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e1576add", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
mature benefit / yrmature tokens / yrbenefit : token
scenariofeature
floorVoice Bot7,978,377370,41621.5:1
Agentic VA1,595,6751,511,0641.1:1
realisticVoice Bot13,962,160972,24014.4:1
Agentic VA3,889,4593,683,1961.1:1
\n", + "
" + ], + "text/plain": [ + " mature benefit / yr mature tokens / yr benefit : token\n", + "scenario feature \n", + "floor Voice Bot 7,978,377 370,416 21.5:1\n", + " Agentic VA 1,595,675 1,511,064 1.1:1\n", + "realistic Voice Bot 13,962,160 972,240 14.4:1\n", + " Agentic VA 3,889,459 3,683,196 1.1:1" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def mature(scenario_name, feature, params=\"realistic\"):\n", + " \"\"\"Mature-year (steady-state) benefit and token cost for one feature.\"\"\"\n", + " sc = dataclasses.replace(SCENARIOS[scenario_name],\n", + " benefit_realization={1: 1.0, 2: 1.0, 3: 1.0},\n", + " consumption_cost_realization={1: 1.0, 2: 1.0, 3: 1.0})\n", + " scope = FeatureScope(feature, ALL, phase=1)\n", + " ben = float(calculate_va_deflection_benefit(\n", + " sites, scope, sc, 1, params=params, rollout=NO_ROLLOUT)[\"annual_value\"].sum())\n", + " tok = float(calculate_total_cost(\n", + " sites, [scope], meters, pricing, sc, 1,\n", + " include_platform=False, rollout=NO_ROLLOUT)[\"annual_cost\"].sum())\n", + " return ben, tok\n", + "\n", + "\n", + "MATURE = {scen: {\"Voice Bot\": mature(scen, \"Voice Bot\"),\n", + " \"Agentic VA\": mature(scen, \"Agentic Virtual Agent\")}\n", + " for scen in (\"floor\", \"realistic\")}\n", + "\n", + "anchor = pd.DataFrame([\n", + " {\"scenario\": scen, \"feature\": feat,\n", + " \"mature benefit / yr\": b, \"mature tokens / yr\": t,\n", + " \"benefit : token\": f\"{b / t:,.1f}:1\"}\n", + " for scen, d in MATURE.items() for feat, (b, t) in d.items()]).set_index([\"scenario\", \"feature\"])\n", + "display(anchor)" + ] + }, + { + "cell_type": "markdown", + "id": "d67be4dd", + "metadata": {}, + "source": [ + "## 2 · Phasing & cost inputs (editable)\n", + "\n", + "The honest phasing: Voice Bot goes live mid-2027 and ramps to mature by 2029; Agentic VA builds in\n", + "2028, goes live partially in 2029, matures 2030. Implementation is a **separate line** (loads early,\n", + "tapers to tuning). The platform-licence line carries only the **voice-attributable share** (default\n", + "= full contracted $3.2M/yr with a 6-month ramp in 2026) so this case isn't double-charged against\n", + "the feature-productivity notebook." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d6646cc1", + "metadata": {}, + "outputs": [ + { + "data": { + "application/mercury+json": { + "model_id": "9537759ac3a24455a1ad5e12b6967c82", + "position": "sidebar", + "widget": "SelectWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "9537759ac3a24455a1ad5e12b6967c82", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "48a9b503a4b644df8578be36582850ad", + "position": "sidebar", + "widget": "SelectWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "48a9b503a4b644df8578be36582850ad", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "5f73500e219244b2ae119b6497e0ed1e", + "position": "sidebar", + "widget": "NumberInputWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "5f73500e219244b2ae119b6497e0ed1e", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/mercury+json": { + "model_id": "5210138abf164a0cb59db327b6e19b37", + "position": "sidebar", + "widget": "CheckboxWidget" + }, + "application/vnd.jupyter.widget-view+json": { + "model_id": "5210138abf164a0cb59db327b6e19b37", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Sidebar levers (widgets only) ───────────────────────────────────\n", + "_scn_w = mr.Select(label=\"Headline scenario\", value=\"realistic\",\n", + " choices=[\"floor\", \"realistic\"])\n", + "_disc_w = mr.Select(label=\"NPV discount rate (primary)\", value=\"8.0% (CTM treasury)\",\n", + " choices=[\"8.0% (CTM treasury)\", \"13.5% (deck)\"])\n", + "_lic_w = mr.NumberInput(label=\"Voice-attributable platform licence ($/yr)\",\n", + " value=3_200_000, min=0, max=6_000_000, step=100_000)\n", + "_va_on_w = mr.CheckBox(label=\"Include Agentic VA layer\", value=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f19d7263", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
20262027202820292030
Voice Bot benefit ramp00111
Agentic VA benefit ramp00001
VB implementation ($)150,000450,000150,000150,000150,000
VA implementation ($)00400,000150,000150,000
Platform licence ($)1,600,0003,200,0003,200,0003,200,0003,200,000
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 2029 2030\n", + "Voice Bot benefit ramp 0 0 1 1 1\n", + "Agentic VA benefit ramp 0 0 0 0 1\n", + "VB implementation ($) 150,000 450,000 150,000 150,000 150,000\n", + "VA implementation ($) 0 0 400,000 150,000 150,000\n", + "Platform licence ($) 1,600,000 3,200,000 3,200,000 3,200,000 3,200,000" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# ── Phasing matrices (fraction of mature run-rate realized each year) ─\n", + "HEADLINE = str(_scn_w.value)\n", + "DISCOUNT_PRIMARY = 0.08 if _disc_w.value.startswith(\"8.0\") else 0.135\n", + "DISCOUNT_SENS = 0.135 if DISCOUNT_PRIMARY == 0.08 else 0.08\n", + "LICENCE_ANNUAL = float(_lic_w.value)\n", + "INCLUDE_VA = bool(_va_on_w.value)\n", + "\n", + "# Voice Bot: $0 in 2026 (no ESB/integration), mid-2027 go-live (partial),\n", + "# ramp through 2028, mature 2029-30.\n", + "VB_RAMP = {2026: 0.00, 2027: 0.25, 2028: 0.65, 2029: 1.00, 2030: 1.00}\n", + "# Agentic VA: build 2028, partial-year go-live 2029, mature 2030.\n", + "VA_RAMP = {2026: 0.00, 2027: 0.00, 2028: 0.00, 2029: 0.40, 2030: 1.00}\n", + "# Implementation (separate, $) — loads early, tapers to tuning.\n", + "VB_IMPL = {2026: 150_000, 2027: 450_000, 2028: 150_000, 2029: 150_000, 2030: 150_000}\n", + "VA_IMPL = {2026: 0, 2027: 0, 2028: 400_000, 2029: 150_000, 2030: 150_000}\n", + "# Platform licence: 6-month ramp in 2026, full thereafter.\n", + "LICENCE = {2026: LICENCE_ANNUAL * 0.5, **{y: LICENCE_ANNUAL for y in YEARS[1:]}}\n", + "\n", + "phasing = pd.DataFrame({\n", + " \"Voice Bot benefit ramp\": VB_RAMP,\n", + " \"Agentic VA benefit ramp\": VA_RAMP,\n", + " \"VB implementation ($)\": VB_IMPL,\n", + " \"VA implementation ($)\": VA_IMPL,\n", + " \"Platform licence ($)\": LICENCE,\n", + "}).T[YEARS]\n", + "display(phasing)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2dcc82f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "headline realistic: 5-yr net $21.8M, NPV@8.0% $15.4M\n" + ] + } + ], + "source": [ + "# ── Build the 5-year phased model for one scenario ──────────────────\n", + "def build_5yr(scenario_name, include_va=True):\n", + " vb_b, vb_t = MATURE[scenario_name][\"Voice Bot\"]\n", + " va_b, va_t = MATURE[scenario_name][\"Agentic VA\"]\n", + " rows = {}\n", + " rows[\"Voice Bot deflection\"] = {y: vb_b * VB_RAMP[y] for y in YEARS}\n", + " rows[\"Agentic VA deflection\"] = {y: (va_b * VA_RAMP[y] if include_va else 0.0) for y in YEARS}\n", + " ben = {y: sum(rows[k][y] for k in rows) for y in YEARS}\n", + "\n", + " cost = {\n", + " \"Platform licence (voice-attributable)\": dict(LICENCE),\n", + " \"Implementation\": {y: VB_IMPL[y] + (VA_IMPL[y] if include_va else 0) for y in YEARS},\n", + " \"Voice Bot tokens\": {y: vb_t * VB_RAMP[y] for y in YEARS},\n", + " \"Agentic VA tokens\": {y: (va_t * VA_RAMP[y] if include_va else 0.0) for y in YEARS},\n", + " }\n", + " cost_total = {y: sum(cost[k][y] for k in cost) for y in YEARS}\n", + " net = {y: ben[y] - cost_total[y] for y in YEARS}\n", + " return {\"benefit_lines\": rows, \"benefit\": ben, \"cost_lines\": cost,\n", + " \"cost\": cost_total, \"net\": net,\n", + " \"mature\": {\"Voice Bot\": (vb_b, vb_t), \"Agentic VA\": (va_b, va_t)}}\n", + "\n", + "\n", + "def npv(flows, r):\n", + " return sum(cf / (1 + r) ** i for i, cf in enumerate(flows, start=1))\n", + "\n", + "\n", + "def payback_label(net):\n", + " cum, prev = 0.0, None\n", + " for y in YEARS:\n", + " prev = cum\n", + " cum += net[y]\n", + " if cum >= 0 and net[y] > 0:\n", + " frac = (-prev / net[y]) if prev < 0 else 0.0\n", + " m = a4._MONTHS[int(frac * 12) % 12]\n", + " return f\"{m} {y}\"\n", + " return f\"beyond {YEARS[-1]}\"\n", + "\n", + "\n", + "CASES = {scen: build_5yr(scen, INCLUDE_VA) for scen in (\"floor\", \"realistic\")}\n", + "HCASE = CASES[HEADLINE]\n", + "\n", + "# KPI summary\n", + "def kpis(case):\n", + " net_list = [case[\"net\"][y] for y in YEARS]\n", + " return {\"5-yr benefits\": sum(case[\"benefit\"].values()),\n", + " \"5-yr cost\": sum(case[\"cost\"].values()),\n", + " \"5-yr net\": sum(net_list),\n", + " f\"NPV @ {DISCOUNT_PRIMARY:.1%}\": npv(net_list, DISCOUNT_PRIMARY),\n", + " f\"NPV @ {DISCOUNT_SENS:.1%}\": npv(net_list, DISCOUNT_SENS),\n", + " \"payback\": payback_label(case[\"net\"])}\n", + "\n", + "\n", + "backstage(f\"headline {HEADLINE}: 5-yr net \"\n", + " f\"{money(sum(HCASE['net'].values()))}, \"\n", + " f\"NPV@{DISCOUNT_PRIMARY:.1%} {money(npv([HCASE['net'][y] for y in YEARS], DISCOUNT_PRIMARY))}\")" + ] + }, + { + "cell_type": "markdown", + "id": "43827fcd", + "metadata": {}, + "source": [ + "## 3 · Figure 1 — 5-year business case (headline scenario)\n", + "\n", + "Benefits (deflection labour avoided) stacked against the cost lines, with cumulative net. 2026–27\n", + "are the investment years; the case turns positive in 2028 and compounds as both features mature." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9ae520ec", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Voice Bot deflection", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + 0, + 3490539.888701922, + 9075403.710624997, + 13962159.554807687, + 13962159.554807687 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agentic VA deflection", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + 0, + 0, + 0, + 1555783.49325, + 3889458.733125 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#c3c2b7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Platform licence (voice-attributable)", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + -1600000, + -3200000, + -3200000, + -3200000, + -3200000 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#4a3aa7", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Implementation", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + -150000, + -450000, + -550000, + -300000, + -300000 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#2a78d6", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Voice Bot tokens", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + 0, + -243060, + -631956, + -972240, + -972240 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "marker": { + "color": "#1baf7a", + "line": { + "color": "#fcfcfb", + "width": 2 + } + }, + "name": "Agentic VA tokens", + "type": "bar", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": [ + 0, + 0, + 0, + -1473278.4000000001, + -3683196 + ] + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#0b0b0b", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Cumulative net", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": { + "bdata": "AAAAAPCzOsHzAz8OJGxAwZW+tsy/YkNBHpPqB7kaZ0HdqJDol8x0QQ==", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$1.8M", + "x": 0, + "y": -1750000, + "yshift": -16 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "-$2.2M", + "x": 1, + "y": -2152520.111298078, + "yshift": -16 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$2.5M", + "x": 2, + "y": 2540927.5993269184, + "yshift": 16 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$12.1M", + "x": 3, + "y": 12113352.247384604, + "yshift": 16 + }, + { + "font": { + "color": "#0b0b0b", + "size": 12 + }, + "showarrow": false, + "text": "$21.8M", + "x": 4, + "y": 21809534.53531729, + "yshift": 16 + } + ], + "bargap": 0.42, + "barmode": "relative", + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 520, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Virtual agents — 5-year case (Realistic)
Benefits (up) vs cost lines (down) · 5-yr net $21.8M · NPV@8.0% $15.4M · payback Jun 2028", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "# Benefits — positive stacked bars.\n", + "for line, color in BEN_COLOR.items():\n", + " vals = [HCASE[\"benefit_lines\"][line][y] for y in YEARS]\n", + " if sum(vals) > 0:\n", + " fig.add_trace(bar(X, vals, line, color))\n", + "# Costs — negative stacked bars (so benefit vs cost read as mirror halves).\n", + "for line, color in COST_COLOR.items():\n", + " vals = [-HCASE[\"cost_lines\"][line][y] for y in YEARS]\n", + " if any(v != 0 for v in vals):\n", + " fig.add_trace(bar(X, vals, line, color))\n", + "# Cumulative net line.\n", + "cum = pd.Series([HCASE[\"net\"][y] for y in YEARS]).cumsum()\n", + "fig.add_trace(cum_line(X, cum, \"Cumulative net\", color=INK))\n", + "for i, v in enumerate(cum):\n", + " fig.add_annotation(x=i, y=float(v), text=f\"{money(float(v))}\",\n", + " showarrow=False, yshift=16 if v >= 0 else -16,\n", + " font=dict(size=12, color=INK))\n", + "fig.update_layout(barmode=\"relative\")\n", + "_k = kpis(HCASE)\n", + "tei_layout(fig, f\"Virtual agents \\u2014 5-year case ({HEADLINE.title()})\",\n", + " f\"Benefits (up) vs cost lines (down) \\u00b7 5-yr net {money(_k['5-yr net'])} \\u00b7 \"\n", + " f\"NPV@{DISCOUNT_PRIMARY:.1%} {money(_k[f'NPV @ {DISCOUNT_PRIMARY:.1%}'])} \\u00b7 \"\n", + " f\"payback {_k['payback']}\", height=520)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "a3761a04", + "metadata": {}, + "source": [ + "## 4 · Cost & benefit detail" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8be664ad", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-13T19:59:14.988775Z", + "iopub.status.busy": "2026-07-13T19:59:14.988546Z", + "iopub.status.idle": "2026-07-13T19:59:15.014307Z", + "shell.execute_reply": "2026-07-13T19:59:15.013730Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
202620272028202920305-yr
Voice Bot deflection03,490,5409,075,40413,962,16013,962,16040,490,263
Agentic VA deflection0001,555,7833,889,4595,445,242
TOTAL benefit03,490,5409,075,40415,517,94317,851,61845,935,505
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 2029 2030 \\\n", + "Voice Bot deflection 0 3,490,540 9,075,404 13,962,160 13,962,160 \n", + "Agentic VA deflection 0 0 0 1,555,783 3,889,459 \n", + "TOTAL benefit 0 3,490,540 9,075,404 15,517,943 17,851,618 \n", + "\n", + " 5-yr \n", + "Voice Bot deflection 40,490,263 \n", + "Agentic VA deflection 5,445,242 \n", + "TOTAL benefit 45,935,505 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
202620272028202920305-yr
Platform licence (voice-attributable)1,600,0003,200,0003,200,0003,200,0003,200,00014,400,000
Implementation150,000450,000550,000300,000300,0001,750,000
Voice Bot tokens0243,060631,956972,240972,2402,819,496
Agentic VA tokens0001,473,2783,683,1965,156,474
TOTAL cost1,750,0003,893,0604,381,9565,945,5188,155,43624,125,970
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 2029 \\\n", + "Platform licence (voice-attributable) 1,600,000 3,200,000 3,200,000 3,200,000 \n", + "Implementation 150,000 450,000 550,000 300,000 \n", + "Voice Bot tokens 0 243,060 631,956 972,240 \n", + "Agentic VA tokens 0 0 0 1,473,278 \n", + "TOTAL cost 1,750,000 3,893,060 4,381,956 5,945,518 \n", + "\n", + " 2030 5-yr \n", + "Platform licence (voice-attributable) 3,200,000 14,400,000 \n", + "Implementation 300,000 1,750,000 \n", + "Voice Bot tokens 972,240 2,819,496 \n", + "Agentic VA tokens 3,683,196 5,156,474 \n", + "TOTAL cost 8,155,436 24,125,970 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
20262027202820292030cumulative net5-yr
benefit03,490,5409,075,40415,517,94317,851,6180NaN
cost1,750,0003,893,0604,381,9565,945,5188,155,4360NaN
net-1,750,000-402,5204,693,4489,572,4259,696,182021,809,535
\n", + "
" + ], + "text/plain": [ + " 2026 2027 2028 2029 2030 cumulative net \\\n", + "benefit 0 3,490,540 9,075,404 15,517,943 17,851,618 0 \n", + "cost 1,750,000 3,893,060 4,381,956 5,945,518 8,155,436 0 \n", + "net -1,750,000 -402,520 4,693,448 9,572,425 9,696,182 0 \n", + "\n", + " 5-yr \n", + "benefit NaN \n", + "cost NaN \n", + "net 21,809,535 " + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ben_df = pd.DataFrame(HCASE[\"benefit_lines\"]).T[YEARS]\n", + "ben_df.loc[\"TOTAL benefit\"] = ben_df.sum()\n", + "ben_df[\"5-yr\"] = ben_df.sum(axis=1)\n", + "display(ben_df)\n", + "\n", + "cost_df = pd.DataFrame(HCASE[\"cost_lines\"]).T[YEARS]\n", + "cost_df.loc[\"TOTAL cost\"] = cost_df.sum()\n", + "cost_df[\"5-yr\"] = cost_df.sum(axis=1)\n", + "display(cost_df)\n", + "\n", + "net_df = pd.DataFrame({\"benefit\": HCASE[\"benefit\"], \"cost\": HCASE[\"cost\"],\n", + " \"net\": HCASE[\"net\"]}).T[YEARS]\n", + "net_df[\"cumulative net\"] = 0\n", + "net_df.loc[\"net\", \"5-yr\"] = sum(HCASE[\"net\"].values())\n", + "_cum = pd.Series([HCASE[\"net\"][y] for y in YEARS]).cumsum().values\n", + "display(net_df)" + ] + }, + { + "cell_type": "markdown", + "id": "d8d3626c", + "metadata": {}, + "source": [ + "## 5 · Figure 2 — Floor vs Realistic, cumulative net\n", + "\n", + "Both defensible scenarios; anchor on Realistic. Even **Floor** is cumulatively net-positive by the\n", + "end of the window — the opposite of the Genesys feature-only case (net-negative on an absolute basis,\n", + "see `ctm_business_case_no_current_state.ipynb`)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "9d172a1d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-13T19:59:15.016591Z", + "iopub.status.busy": "2026-07-13T19:59:15.016396Z", + "iopub.status.idle": "2026-07-13T19:59:15.052357Z", + "shell.execute_reply": "2026-07-13T19:59:15.051192Z" + } + }, + "outputs": [ + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#eda100", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Floor", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": { + "bdata": "AAAAAPCzOsEcuZHjDLBKwf0zppm5kUHBvi85PXoPPEEU9lagLQJXQQ==", + "dtype": "f8" + } + }, + { + "hovertemplate": "%{fullData.name}: %{y:$,.0f}", + "line": { + "color": "#2a78d6", + "width": 2 + }, + "marker": { + "line": { + "color": "#fcfcfb", + "width": 2 + }, + "size": 8 + }, + "mode": "lines+markers", + "name": "Realistic", + "type": "scatter", + "x": [ + "2026", + "2027", + "2028", + "2029", + "2030" + ], + "y": { + "bdata": "AAAAAPCzOsHzAz8OJGxAwZW+tsy/YkNBHpPqB7kaZ0HdqJDol8x0QQ==", + "dtype": "f8" + } + } + ], + "layout": { + "annotations": [ + { + "font": { + "color": "#eda100", + "size": 12 + }, + "showarrow": false, + "text": "$6.0M", + "x": 4, + "xshift": 6, + "y": 6031542.505307693 + }, + { + "font": { + "color": "#2a78d6", + "size": 12 + }, + "showarrow": false, + "text": "$21.8M", + "x": 4, + "xshift": 6, + "y": 21809534.53531729 + } + ], + "bargap": 0.42, + "font": { + "color": "#52514e", + "family": "system-ui, -apple-system, \"Segoe UI\", sans-serif", + "size": 12 + }, + "height": 440, + "hovermode": "x unified", + "legend": { + "font": { + "color": "#52514e", + "size": 11 + }, + "orientation": "h", + "x": 0, + "y": -0.1, + "yanchor": "top" + }, + "margin": { + "b": 80, + "l": 70, + "r": 30, + "t": 70 + }, + "paper_bgcolor": "#fcfcfb", + "plot_bgcolor": "#fcfcfb", + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermap": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermap" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "title": { + "font": { + "color": "#0b0b0b", + "size": 16 + }, + "text": "Cumulative net — Floor vs Realistic (5-year)
Deflection carries the case · investment 2026-27, returns from 2028", + "x": 0.02, + "xanchor": "left" + }, + "xaxis": { + "linecolor": "#c3c2b7", + "showgrid": false, + "tickfont": { + "color": "#898781" + }, + "type": "category" + }, + "yaxis": { + "gridcolor": "#e1e0d9", + "tickfont": { + "color": "#898781" + }, + "tickformat": "$~s", + "zeroline": true, + "zerolinecolor": "#c3c2b7", + "zerolinewidth": 1.5 + } + } + } + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
FloorRealistic
5-yr benefits$25.4M$45.9M
5-yr cost$19.3M$24.1M
5-yr net$6.0M$21.8M
NPV @ 8.0%$3.7M$15.4M
NPV @ 13.5%$2.6M$12.3M
paybackJul 2029Jun 2028
\n", + "
" + ], + "text/plain": [ + " Floor Realistic\n", + "5-yr benefits $25.4M $45.9M\n", + "5-yr cost $19.3M $24.1M\n", + "5-yr net $6.0M $21.8M\n", + "NPV @ 8.0% $3.7M $15.4M\n", + "NPV @ 13.5% $2.6M $12.3M\n", + "payback Jul 2029 Jun 2028" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig = go.Figure()\n", + "for scen in (\"floor\", \"realistic\"):\n", + " cum = pd.Series([CASES[scen][\"net\"][y] for y in YEARS]).cumsum()\n", + " fig.add_trace(cum_line(X, cum, scen.title(), color=SCEN_COLOR[scen]))\n", + " fig.add_annotation(x=len(YEARS) - 1, y=float(cum.iloc[-1]),\n", + " text=f\"{money(float(cum.iloc[-1]))}\", showarrow=False,\n", + " xshift=6, font=dict(size=12, color=SCEN_COLOR[scen]))\n", + "fig.update_yaxes(zeroline=True)\n", + "tei_layout(fig, \"Cumulative net — Floor vs Realistic (5-year)\",\n", + " \"Deflection carries the case · investment 2026-27, returns from 2028\", height=440)\n", + "fig.show()\n", + "\n", + "kpi_df = pd.DataFrame({scen.title(): kpis(CASES[scen]) for scen in (\"floor\", \"realistic\")})\n", + "_disp = kpi_df.copy()\n", + "for r in kpi_df.index:\n", + " if r != \"payback\":\n", + " _disp.loc[r] = kpi_df.loc[r].map(money)\n", + "display(_disp)" + ] + }, + { + "cell_type": "markdown", + "id": "2425db98", + "metadata": {}, + "source": [ + "## 6 · Voice-Bot-only view — the robust core\n", + "\n", + "Voice Bot alone (no Agentic VA) is the conservative spine of the case: mature by 2029, a\n", + "**~14:1 benefit-to-token ratio** at Realistic, and it delivers the bulk of the 5-year net on its\n", + "own. Agentic VA is layered upside with its own runway — not a dependency the core case leans on." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6e07d6ac", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-13T19:59:15.054864Z", + "iopub.status.busy": "2026-07-13T19:59:15.054679Z", + "iopub.status.idle": "2026-07-13T19:59:15.066082Z", + "shell.execute_reply": "2026-07-13T19:59:15.065079Z" + } + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
VB 5-yr benefitVB 5-yr tokensbenefit : tokenVB-only 5-yr netpayback
scenario
Floor$23.1M$1.1M21.5:1$6.6MJun 2029
Realistic$40.5M$2.8M14.4:1$22.2MJun 2028
\n", + "
" + ], + "text/plain": [ + " VB 5-yr benefit VB 5-yr tokens benefit : token VB-only 5-yr net \\\n", + "scenario \n", + "Floor $23.1M $1.1M 21.5:1 $6.6M \n", + "Realistic $40.5M $2.8M 14.4:1 $22.2M \n", + "\n", + " payback \n", + "scenario \n", + "Floor Jun 2029 \n", + "Realistic Jun 2028 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "VB-only realistic 5-yr net $22.2M\n" + ] + } + ], + "source": [ + "vb_only = {scen: build_5yr(scen, include_va=False) for scen in (\"floor\", \"realistic\")}\n", + "rows = []\n", + "for scen in (\"floor\", \"realistic\"):\n", + " c = vb_only[scen]\n", + " vb_b, vb_t = c[\"mature\"][\"Voice Bot\"]\n", + " ben5 = sum(c[\"benefit\"].values())\n", + " tok5 = sum(c[\"cost_lines\"][\"Voice Bot tokens\"].values())\n", + " rows.append({\"scenario\": scen.title(),\n", + " \"VB 5-yr benefit\": money(ben5),\n", + " \"VB 5-yr tokens\": money(tok5),\n", + " \"benefit : token\": f\"{ben5 / tok5:,.1f}:1\",\n", + " \"VB-only 5-yr net\": money(sum(c[\"net\"].values())),\n", + " \"payback\": payback_label(c[\"net\"])})\n", + "display(pd.DataFrame(rows).set_index(\"scenario\"))\n", + "backstage(f\"VB-only realistic 5-yr net \"\n", + " f\"{money(sum(vb_only['realistic']['net'].values()))}\")" + ] + }, + { + "cell_type": "markdown", + "id": "d44dd47d", + "metadata": {}, + "source": [ + "## 7 · The strategic point (for the room)\n", + "\n", + "> *Contact-centre transformation isn't a 3-year decision. The Genesys case was built on a 3-year\n", + "> window because that's how vendor cases work — but that framing pulls every benefit forward and\n", + "> waves away every dependency. The real question is whether the transformation pencils over the\n", + "> natural 5-year lifecycle of the investment. On that basis, the case is very different.*\n", + "\n", + "**Three numbers to walk in with (Realistic):**\n", + "- **Voice Bot 5-year net: ~$22M** — the robust, ops-validatable core.\n", + "- **Payback: mid-2028** — ~24–30 months from platform go-live.\n", + "- **Benefit-to-token ratio: ~14:1** — token consumption is a rounding error against the labour avoided.\n", + "\n", + "**Caveats / next steps**\n", + "- Non-NAM site volumes & AHTs are `tokencalc` placeholders (🟡) — confirm with CTM data.\n", + "- Phasing ramps are the honest working assumption; validate go-live dates with the PM and CTM ops.\n", + "- Deflection targets should be validated against CTM's containable-intent analysis (sidebar overrides).\n", + "- Contracted token rates (vs $1.00 US list) not yet sourced — the ~14:1 ratio has enormous headroom." + ] + }, + { + "cell_type": "markdown", + "id": "eecec3d7", + "metadata": {}, + "source": [ + "## 8 · Machine-readable data appendix\n", + "\n", + "Renders backstage (JupyterLab / nbconvert); hidden on the Mercury stage." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "5c6bff66", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-13T19:59:15.068865Z", + "iopub.status.busy": "2026-07-13T19:59:15.068685Z", + "iopub.status.idle": "2026-07-13T19:59:15.201240Z", + "shell.execute_reply": "2026-07-13T19:59:15.200251Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "#### Mature-year anchors\n", + "\n", + "| | mature benefit / yr | mature tokens / yr | benefit : token |\n", + "|:----------------------------|----------------------:|---------------------:|:------------------|\n", + "| ('floor', 'Voice Bot') | 7,978,377 | 370,416 | 21.5:1 |\n", + "| ('floor', 'Agentic VA') | 1,595,675 | 1,511,064 | 1.1:1 |\n", + "| ('realistic', 'Voice Bot') | 13,962,160 | 972,240 | 14.4:1 |\n", + "| ('realistic', 'Agentic VA') | 3,889,459 | 3,683,196 | 1.1:1 |\n", + "\n", + "#### Phasing & cost inputs\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|:------------------------|----------:|----------:|----------:|----------:|----------:|\n", + "| Voice Bot benefit ramp | 0 | 0 | 1 | 1 | 1 |\n", + "| Agentic VA benefit ramp | 0 | 0 | 0 | 0 | 1 |\n", + "| VB implementation ($) | 150,000 | 450,000 | 150,000 | 150,000 | 150,000 |\n", + "| VA implementation ($) | 0 | 0 | 400,000 | 150,000 | 150,000 |\n", + "| Platform licence ($) | 1,600,000 | 3,200,000 | 3,200,000 | 3,200,000 | 3,200,000 |\n", + "\n", + "#### Floor — benefit by line × year\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|:----------------------|-------:|----------:|----------:|----------:|----------:|\n", + "| Voice Bot deflection | 0 | 1,994,594 | 5,185,945 | 7,978,377 | 7,978,377 |\n", + "| Agentic VA deflection | 0 | 0 | 0 | 638,270 | 1,595,675 |\n", + "\n", + "#### Floor — cost by line × year\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|:--------------------------------------|----------:|----------:|----------:|----------:|----------:|\n", + "| Platform licence (voice-attributable) | 1,600,000 | 3,200,000 | 3,200,000 | 3,200,000 | 3,200,000 |\n", + "| Implementation | 150,000 | 450,000 | 550,000 | 300,000 | 300,000 |\n", + "| Voice Bot tokens | 0 | 92,604 | 240,770 | 370,416 | 370,416 |\n", + "| Agentic VA tokens | 0 | 0 | 0 | 604,426 | 1,511,064 |\n", + "\n", + "#### Realistic — benefit by line × year\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|:----------------------|-------:|----------:|----------:|-----------:|-----------:|\n", + "| Voice Bot deflection | 0 | 3,490,540 | 9,075,404 | 13,962,160 | 13,962,160 |\n", + "| Agentic VA deflection | 0 | 0 | 0 | 1,555,783 | 3,889,459 |\n", + "\n", + "#### Realistic — cost by line × year\n", + "\n", + "| | 2026 | 2027 | 2028 | 2029 | 2030 |\n", + "|:--------------------------------------|----------:|----------:|----------:|----------:|----------:|\n", + "| Platform licence (voice-attributable) | 1,600,000 | 3,200,000 | 3,200,000 | 3,200,000 | 3,200,000 |\n", + "| Implementation | 150,000 | 450,000 | 550,000 | 300,000 | 300,000 |\n", + "| Voice Bot tokens | 0 | 243,060 | 631,956 | 972,240 | 972,240 |\n", + "| Agentic VA tokens | 0 | 0 | 0 | 1,473,278 | 3,683,196 |\n", + "\n", + "#### Model state (JSON)\n", + "\n", + "```json\n", + "{\n", + " \"window\": [\n", + " 2026,\n", + " 2027,\n", + " 2028,\n", + " 2029,\n", + " 2030\n", + " ],\n", + " \"headline_scenario\": \"realistic\",\n", + " \"discount_primary\": 0.08,\n", + " \"include_agentic_va\": true,\n", + " \"licence_annual\": 3200000.0,\n", + " \"phasing\": {\n", + " \"voice_bot\": {\n", + " \"2026\": 0.0,\n", + " \"2027\": 0.25,\n", + " \"2028\": 0.65,\n", + " \"2029\": 1.0,\n", + " \"2030\": 1.0\n", + " },\n", + " \"agentic_va\": {\n", + " \"2026\": 0.0,\n", + " \"2027\": 0.0,\n", + " \"2028\": 0.0,\n", + " \"2029\": 0.4,\n", + " \"2030\": 1.0\n", + " }\n", + " },\n", + " \"scenarios\": {\n", + " \"floor\": {\n", + " \"benefit_by_year\": {\n", + " \"2026\": 0,\n", + " \"2027\": 1994594,\n", + " \"2028\": 5185945,\n", + " \"2029\": 8616647,\n", + " \"2030\": 9574052\n", + " },\n", + " \"cost_by_year\": {\n", + " \"2026\": 1750000,\n", + " \"2027\": 3742604,\n", + " \"2028\": 3990770,\n", + " \"2029\": 4474842,\n", + " \"2030\": 5381480\n", + " },\n", + " \"net_by_year\": {\n", + " \"2026\": -1750000,\n", + " \"2027\": -1748010,\n", + " \"2028\": 1195175,\n", + " \"2029\": 4141805,\n", + " \"2030\": 4192572\n", + " },\n", + " \"net_5yr\": 6031543,\n", + " \"npv_primary\": 3727506,\n", + " \"npv_sensitivity\": 2640311,\n", + " \"payback\": \"Jul 2029\",\n", + " \"vb_only_net_5yr\": 6613087\n", + " },\n", + " \"realistic\": {\n", + " \"benefit_by_year\": {\n", + " \"2026\": 0,\n", + " \"2027\": 3490540,\n", + " \"2028\": 9075404,\n", + " \"2029\": 15517943,\n", + " \"2030\": 17851618\n", + " },\n", + " \"cost_by_year\": {\n", + " \"2026\": 1750000,\n", + " \"2027\": 3893060,\n", + " \"2028\": 4381956,\n", + " \"2029\": 5945518,\n", + " \"2030\": 8155436\n", + " },\n", + " \"net_by_year\": {\n", + " \"2026\": -1750000,\n", + " \"2027\": -402520,\n", + " \"2028\": 4693448,\n", + " \"2029\": 9572425,\n", + " \"2030\": 9696182\n", + " },\n", + " \"net_5yr\": 21809535,\n", + " \"npv_primary\": 15395420,\n", + " \"npv_sensitivity\": 12271658,\n", + " \"payback\": \"Jun 2028\",\n", + " \"vb_only_net_5yr\": 22220767\n", + " }\n", + " }\n", + "}\n", + "```\n" + ] + } + ], + "source": [ + "import json as _json\n", + "\n", + "\n", + "def _section(title, df):\n", + " backstage(f\"\\n#### {title}\\n\")\n", + " backstage(df.to_markdown(floatfmt=\",.0f\"))\n", + "\n", + "\n", + "_section(\"Mature-year anchors\", anchor)\n", + "_section(\"Phasing & cost inputs\", phasing)\n", + "for scen in (\"floor\", \"realistic\"):\n", + " c = CASES[scen]\n", + " _section(f\"{scen.title()} — benefit by line × year\",\n", + " pd.DataFrame(c[\"benefit_lines\"]).T[YEARS])\n", + " _section(f\"{scen.title()} — cost by line × year\",\n", + " pd.DataFrame(c[\"cost_lines\"]).T[YEARS])\n", + "\n", + "backstage(\"\\n#### Model state (JSON)\\n\")\n", + "backstage(\"```json\")\n", + "backstage(_json.dumps({\n", + " \"window\": YEARS,\n", + " \"headline_scenario\": HEADLINE,\n", + " \"discount_primary\": DISCOUNT_PRIMARY,\n", + " \"include_agentic_va\": INCLUDE_VA,\n", + " \"licence_annual\": LICENCE_ANNUAL,\n", + " \"phasing\": {\"voice_bot\": VB_RAMP, \"agentic_va\": VA_RAMP},\n", + " \"scenarios\": {\n", + " scen: {\n", + " \"benefit_by_year\": {str(y): round(CASES[scen][\"benefit\"][y]) for y in YEARS},\n", + " \"cost_by_year\": {str(y): round(CASES[scen][\"cost\"][y]) for y in YEARS},\n", + " \"net_by_year\": {str(y): round(CASES[scen][\"net\"][y]) for y in YEARS},\n", + " \"net_5yr\": round(sum(CASES[scen][\"net\"].values())),\n", + " \"npv_primary\": round(npv([CASES[scen][\"net\"][y] for y in YEARS], DISCOUNT_PRIMARY)),\n", + " \"npv_sensitivity\": round(npv([CASES[scen][\"net\"][y] for y in YEARS], DISCOUNT_SENS)),\n", + " \"payback\": payback_label(CASES[scen][\"net\"]),\n", + " \"vb_only_net_5yr\": round(sum(vb_only[scen][\"net\"].values())),\n", + " } for scen in (\"floor\", \"realistic\")},\n", + "}, indent=2))\n", + "backstage(\"```\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "1eb499ff637b460186eb8485dc2c79cd": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "floor", + "realistic" + ], + "disabled": false, + "hidden": false, + "label": "Headline scenario", + "layout": "IPY_MODEL_bf42f2d19c434de4926d58c3e7b48d2e", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "realistic" + } + }, + "2a96c544ee55490aa9e41d9c3cae7788": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3b8e09a0b1eb47c29cb03829fcf7363a": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.select.SelectWidget", + "_css": "\n .mljar-select-container {\n display: flex;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n }\n\n .mljar-select-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-select-control {\n position: relative;\n display: flex;\n align-items: center;\n cursor: default;\n }\n\n .mljar-select-widget-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 36px 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n line-height: 1.4;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n\n appearance: none !important;\n background-color: #ffffff !important;\n color: #0f172a !important;\n cursor: default;\n }\n\n .mljar-select-widget-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n cursor: text;\n }\n\n .mljar-select-caret {\n position: absolute;\n right: 12px;\n top: 50%;\n width: 8px;\n height: 8px;\n border-right: 1.5px solid #0f172a;\n border-bottom: 1.5px solid #0f172a;\n transform: translateY(-65%) rotate(45deg);\n pointer-events: none;\n opacity: 0.5;\n transition: transform 0.18s ease, opacity 0.18s ease;\n }\n\n .mljar-select-container.is-open .mljar-select-caret {\n opacity: 1;\n transform: translateY(-35%) rotate(225deg);\n }\n\n .mljar-select-dropdown {\n display: none;\n margin-top: 6px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);\n overflow: hidden;\n }\n\n .mljar-select-list {\n max-height: 260px;\n overflow-y: auto;\n }\n\n .mljar-select-option {\n display: block;\n width: 100%;\n padding: 9px 10px;\n border: 0;\n background: transparent;\n color: #0f172a;\n text-align: left;\n cursor: pointer;\n font: inherit;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-select-option:hover {\n background: #f3f3f4;\n }\n\n .mljar-select-option:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-option.is-selected {\n background: #e6f2ff;\n color: #007bff;\n font-weight: 600;\n }\n\n .mljar-select-option.is-selected:hover,\n .mljar-select-option.is-selected:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-select-empty {\n display: none;\n padding: 10px;\n color: #616673;\n font-size: 0.95em;\n }\n\n .mljar-select-widget-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-select-control.is-disabled .mljar-select-caret {\n opacity: 0.45;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const normalize = value => String(value ?? \"\").toLowerCase().trim();\n const getChoices = () =>\n Array.isArray(model.get(\"choices\")) ? [...model.get(\"choices\")] : [];\n const isDisabled = () => !!model.get(\"disabled\");\n const isHidden = () => !!model.get(\"hidden\");\n\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-select-container\");\n\n if (model.get(\"label\")) {\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-select-label\");\n topLabel.innerHTML = model.get(\"label\");\n container.appendChild(topLabel);\n }\n\n const control = document.createElement(\"div\");\n control.classList.add(\"mljar-select-control\");\n\n const input = document.createElement(\"input\");\n input.type = \"text\";\n input.classList.add(\"mljar-select-widget-input\");\n input.autocomplete = \"off\";\n input.spellcheck = false;\n\n const caret = document.createElement(\"div\");\n caret.classList.add(\"mljar-select-caret\");\n\n control.appendChild(input);\n control.appendChild(caret);\n\n const dropdown = document.createElement(\"div\");\n dropdown.classList.add(\"mljar-select-dropdown\");\n\n const list = document.createElement(\"div\");\n list.classList.add(\"mljar-select-list\");\n\n const emptyState = document.createElement(\"div\");\n emptyState.classList.add(\"mljar-select-empty\");\n emptyState.textContent = \"No matches\";\n\n dropdown.appendChild(list);\n dropdown.appendChild(emptyState);\n\n container.appendChild(control);\n container.appendChild(dropdown);\n el.appendChild(container);\n\n let isOpen = false;\n let filteredChoices = [];\n let lastCommittedValue = \"\";\n let isEditing = false;\n\n const setOpen = next => {\n if (isDisabled()) {\n isOpen = false;\n } else {\n isOpen = !!next;\n }\n container.classList.toggle(\"is-open\", isOpen);\n dropdown.style.display = isOpen ? \"block\" : \"none\";\n };\n\n const updateDisabledState = () => {\n const disabled = isDisabled();\n input.disabled = disabled;\n control.classList.toggle(\"is-disabled\", disabled);\n };\n\n const updateHiddenState = () => {\n container.style.display = isHidden() ? \"none\" : \"\";\n };\n\n const syncInputWithValue = () => {\n const value = model.get(\"value\") || \"\";\n lastCommittedValue = value;\n if (!isEditing) {\n input.value = value;\n }\n };\n\n const filterChoices = query => {\n const normalizedQuery = normalize(query);\n const allChoices = getChoices();\n if (!normalizedQuery) {\n return allChoices;\n }\n return allChoices.filter(choice =>\n normalize(choice).includes(normalizedQuery)\n );\n };\n\n const renderList = () => {\n list.innerHTML = \"\";\n filteredChoices.forEach(choice => {\n const option = document.createElement(\"button\");\n option.type = \"button\";\n option.classList.add(\"mljar-select-option\");\n if (choice === model.get(\"value\")) {\n option.classList.add(\"is-selected\");\n }\n option.textContent = choice;\n option.addEventListener(\"mousedown\", event => {\n event.preventDefault();\n event.stopPropagation();\n model.set(\"value\", choice);\n model.save_changes();\n isEditing = false;\n syncInputWithValue();\n renderList();\n setOpen(false);\n });\n list.appendChild(option);\n });\n\n const hasMatches = filteredChoices.length > 0;\n list.style.display = hasMatches ? \"block\" : \"none\";\n emptyState.style.display = hasMatches ? \"none\" : \"block\";\n };\n\n const refreshList = () => {\n filteredChoices = filterChoices(input.value);\n renderList();\n };\n\n const openWithCurrentQuery = () => {\n isEditing = true;\n input.value = \"\";\n refreshList();\n setOpen(true);\n };\n\n control.addEventListener(\"click\", event => {\n event.stopPropagation();\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n input.focus();\n });\n\n input.addEventListener(\"input\", () => {\n if (isDisabled()) {\n return;\n }\n refreshList();\n setOpen(true);\n });\n\n input.addEventListener(\"focus\", () => {\n if (isDisabled()) {\n return;\n }\n openWithCurrentQuery();\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n input.value = lastCommittedValue;\n });\n\n const handleDocumentClick = event => {\n if (!container.contains(event.target)) {\n isEditing = false;\n setOpen(false);\n input.value = lastCommittedValue;\n }\n };\n\n document.addEventListener(\"click\", handleDocumentClick);\n\n model.on(\"change:value\", () => {\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:choices\", () => {\n const choices = getChoices();\n if (!choices.includes(model.get(\"value\")) && choices.length > 0) {\n model.set(\"value\", choices[0]);\n model.save_changes();\n return;\n }\n syncInputWithValue();\n refreshList();\n });\n\n model.on(\"change:disabled\", () => {\n updateDisabledState();\n if (isDisabled()) {\n isEditing = false;\n setOpen(false);\n }\n });\n\n model.on(\"change:hidden\", () => {\n updateHiddenState();\n });\n\n updateDisabledState();\n updateHiddenState();\n syncInputWithValue();\n refreshList();\n setOpen(false);\n\n return () => {\n document.removeEventListener(\"click\", handleDocumentClick);\n };\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "choices": [ + "8.0% (CTM treasury)", + "13.5% (deck)" + ], + "disabled": false, + "hidden": false, + "label": "NPV discount rate (primary)", + "layout": "IPY_MODEL_df9473c074854514aa16850d83174b0a", + "layout_path": null, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "8.0% (CTM treasury)" + } + }, + "714e29afc099414f81adfbc789ddd589": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7d3e63e84499491ba71b4a2d3074036b": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Voice-attributable platform licence ($/yr)", + "layout": "IPY_MODEL_2a96c544ee55490aa9e41d9c3cae7788", + "layout_path": null, + "max": 6000000, + "min": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 100000, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3200000 + } + }, + "8c09da957880482e807df2152f65db4c": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Include Agentic VA layer", + "last_changed_at": "", + "layout": "IPY_MODEL_714e29afc099414f81adfbc789ddd589", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "bf42f2d19c434de4926d58c3e7b48d2e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "df9473c074854514aa16850d83174b0a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/studies/202607_CTM_GenesysCX/notebooks/ctm_migration_wfm.ipynb b/studies/202607_CTM_GenesysCX/notebooks/ctm_migration_wfm.ipynb index e38834d..6062f4f 100644 --- a/studies/202607_CTM_GenesysCX/notebooks/ctm_migration_wfm.ipynb +++ b/studies/202607_CTM_GenesysCX/notebooks/ctm_migration_wfm.ipynb @@ -39,10 +39,10 @@ "id": "38d0b7d9", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.299824Z", - "iopub.status.busy": "2026-07-08T15:15:47.299589Z", - "iopub.status.idle": "2026-07-08T15:15:47.712827Z", - "shell.execute_reply": "2026-07-08T15:15:47.712043Z" + "iopub.execute_input": "2026-07-13T19:34:22.503625Z", + "iopub.status.busy": "2026-07-13T19:34:22.503379Z", + "iopub.status.idle": "2026-07-13T19:34:22.942301Z", + "shell.execute_reply": "2026-07-13T19:34:22.941521Z" } }, "outputs": [ @@ -154,22 +154,22 @@ "id": "104afa86", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.715016Z", - "iopub.status.busy": "2026-07-08T15:15:47.714770Z", - "iopub.status.idle": "2026-07-08T15:15:47.725088Z", - "shell.execute_reply": "2026-07-08T15:15:47.724690Z" + "iopub.execute_input": "2026-07-13T19:34:22.944759Z", + "iopub.status.busy": "2026-07-13T19:34:22.944509Z", + "iopub.status.idle": "2026-07-13T19:34:22.956133Z", + "shell.execute_reply": "2026-07-13T19:34:22.955587Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "cf504293e15c4cd99f1a492d73d8e263", + "model_id": "6b6471d39e2544f58844cffb1d57afe9", "position": "sidebar", "widget": "MarkdownWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "cf504293e15c4cd99f1a492d73d8e263", + "model_id": "6b6471d39e2544f58844cffb1d57afe9", "version_major": 2, "version_minor": 0 }, @@ -243,27 +243,27 @@ "id": "f8863db7", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.727202Z", - "iopub.status.busy": "2026-07-08T15:15:47.727057Z", - "iopub.status.idle": "2026-07-08T15:15:47.757766Z", - "shell.execute_reply": "2026-07-08T15:15:47.757050Z" + "iopub.execute_input": "2026-07-13T19:34:22.958354Z", + "iopub.status.busy": "2026-07-13T19:34:22.958178Z", + "iopub.status.idle": "2026-07-13T19:34:22.994994Z", + "shell.execute_reply": "2026-07-13T19:34:22.994203Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "ddacc3befdc54c278abccd4a5b6b98b5", + "model_id": "f755a69ae50540b3aac94ceb249b1cc2", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "ddacc3befdc54c278abccd4a5b6b98b5", + "model_id": "f755a69ae50540b3aac94ceb249b1cc2", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -272,17 +272,17 @@ { "data": { "application/mercury+json": { - "model_id": "6d231d033d7b4f41b852046f069ebc3f", + "model_id": "a4de65fac18f44e086d105b7f75693fe", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "6d231d033d7b4f41b852046f069ebc3f", + "model_id": "a4de65fac18f44e086d105b7f75693fe", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -291,17 +291,17 @@ { "data": { "application/mercury+json": { - "model_id": "b2ec5338e0ba4055b77a57edd08b464c", + "model_id": "ff3441542e0b43db946e184e4b461b1e", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "b2ec5338e0ba4055b77a57edd08b464c", + "model_id": "ff3441542e0b43db946e184e4b461b1e", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -310,17 +310,17 @@ { "data": { "application/mercury+json": { - "model_id": "28eb2b2d2a2a41adaad830ac204d7b39", + "model_id": "401b35065476433d96aeed7fcad746e8", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "28eb2b2d2a2a41adaad830ac204d7b39", + "model_id": "401b35065476433d96aeed7fcad746e8", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -329,17 +329,17 @@ { "data": { "application/mercury+json": { - "model_id": "4d3b2b0c854b4bcab393ef4f89aee1b4", + "model_id": "0fff3f6c07fe4316a91344082245882c", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "4d3b2b0c854b4bcab393ef4f89aee1b4", + "model_id": "0fff3f6c07fe4316a91344082245882c", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -348,17 +348,17 @@ { "data": { "application/mercury+json": { - "model_id": "9a1300f4e5374fa6bb1c9717866f8ce1", + "model_id": "bab7c81ec7ae47deabd7fc74f205c38e", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "9a1300f4e5374fa6bb1c9717866f8ce1", + "model_id": "bab7c81ec7ae47deabd7fc74f205c38e", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -367,17 +367,17 @@ { "data": { "application/mercury+json": { - "model_id": "8e89c81717ff408d9e5cb1a949ab337c", + "model_id": "7e22e4c6e3dd44fc8eeb96c4f501a342", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "8e89c81717ff408d9e5cb1a949ab337c", + "model_id": "7e22e4c6e3dd44fc8eeb96c4f501a342", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -386,17 +386,17 @@ { "data": { "application/mercury+json": { - "model_id": "cdf8fe1626a54e7eb048e9e8d27cb2a4", + "model_id": "c2a141345720426b933108c66be94b8b", "position": "inline", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "cdf8fe1626a54e7eb048e9e8d27cb2a4", + "model_id": "c2a141345720426b933108c66be94b8b", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -405,17 +405,17 @@ { "data": { "application/mercury+json": { - "model_id": "68b411c315ff4357815d7ff9801f445a", + "model_id": "ec33300bf1e14da98f0dd7bfc01a2f44", "position": "inline", "widget": "DateInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "68b411c315ff4357815d7ff9801f445a", + "model_id": "ec33300bf1e14da98f0dd7bfc01a2f44", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -460,10 +460,10 @@ "id": "0bf61187", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.759694Z", - "iopub.status.busy": "2026-07-08T15:15:47.759486Z", - "iopub.status.idle": "2026-07-08T15:15:47.771664Z", - "shell.execute_reply": "2026-07-08T15:15:47.771155Z" + "iopub.execute_input": "2026-07-13T19:34:22.996863Z", + "iopub.status.busy": "2026-07-13T19:34:22.996642Z", + "iopub.status.idle": "2026-07-13T19:34:23.012452Z", + "shell.execute_reply": "2026-07-13T19:34:23.011782Z" } }, "outputs": [ @@ -622,27 +622,27 @@ "id": "8bafeb91", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.773242Z", - "iopub.status.busy": "2026-07-08T15:15:47.773111Z", - "iopub.status.idle": "2026-07-08T15:15:47.784646Z", - "shell.execute_reply": "2026-07-08T15:15:47.784118Z" + "iopub.execute_input": "2026-07-13T19:34:23.014180Z", + "iopub.status.busy": "2026-07-13T19:34:23.014015Z", + "iopub.status.idle": "2026-07-13T19:34:23.029326Z", + "shell.execute_reply": "2026-07-13T19:34:23.028514Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "cc5b28489de346d3b59e3ace05fb2ebb", + "model_id": "655850770ff34fb8bf1076de30bf2ddc", "position": "sidebar", "widget": "NumberInputWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "cc5b28489de346d3b59e3ace05fb2ebb", + "model_id": "655850770ff34fb8bf1076de30bf2ddc", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -651,17 +651,17 @@ { "data": { "application/mercury+json": { - "model_id": "2be8dde4d1644691add8dae88a36ac81", + "model_id": "8ffdd60ef54a4c829429b46b438c36db", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "2be8dde4d1644691add8dae88a36ac81", + "model_id": "8ffdd60ef54a4c829429b46b438c36db", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -670,17 +670,17 @@ { "data": { "application/mercury+json": { - "model_id": "cddbd4fd3deb4ed1a29dad5cf082939b", + "model_id": "d4d0cebd7a9345e2a88c5fa842d24337", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "cddbd4fd3deb4ed1a29dad5cf082939b", + "model_id": "d4d0cebd7a9345e2a88c5fa842d24337", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -689,17 +689,17 @@ { "data": { "application/mercury+json": { - "model_id": "1d4ddc81f2c249cf9be44e32e8213b85", + "model_id": "abd40a60e1974670886b2bd43aab35c2", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "1d4ddc81f2c249cf9be44e32e8213b85", + "model_id": "abd40a60e1974670886b2bd43aab35c2", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -708,17 +708,17 @@ { "data": { "application/mercury+json": { - "model_id": "5c47680e253c4f45bbbbfcc76612499b", + "model_id": "1138fba4608d4de484f84b11e1ae2862", "position": "sidebar", "widget": "CheckboxWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "5c47680e253c4f45bbbbfcc76612499b", + "model_id": "1138fba4608d4de484f84b11e1ae2862", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -747,10 +747,10 @@ "id": "6d971716", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.786019Z", - "iopub.status.busy": "2026-07-08T15:15:47.785869Z", - "iopub.status.idle": "2026-07-08T15:15:47.802381Z", - "shell.execute_reply": "2026-07-08T15:15:47.801425Z" + "iopub.execute_input": "2026-07-13T19:34:23.031153Z", + "iopub.status.busy": "2026-07-13T19:34:23.030906Z", + "iopub.status.idle": "2026-07-13T19:34:23.045403Z", + "shell.execute_reply": "2026-07-13T19:34:23.044707Z" } }, "outputs": [ @@ -897,10 +897,10 @@ "id": "12673034", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.804434Z", - "iopub.status.busy": "2026-07-08T15:15:47.804197Z", - "iopub.status.idle": "2026-07-08T15:15:47.852870Z", - "shell.execute_reply": "2026-07-08T15:15:47.852057Z" + "iopub.execute_input": "2026-07-13T19:34:23.047481Z", + "iopub.status.busy": "2026-07-13T19:34:23.047226Z", + "iopub.status.idle": "2026-07-13T19:34:23.082155Z", + "shell.execute_reply": "2026-07-13T19:34:23.081476Z" } }, "outputs": [ @@ -908,7 +908,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "WFM benefits by year: {2026: '$0K', 2027: '$108K', 2028: '$2.2M'} → 3-yr $2.3M\n" + "WFM benefits by year: {2026: '$0K', 2027: '$350K', 2028: '$2.0M'} → 3-yr $2.3M\n" ] }, { @@ -956,8 +956,8 @@ " \n", " ANZ\n", " 0\n", - " 107,692\n", - " 1,292,308\n", + " 350,000\n", + " 1,050,000\n", " 1,400,000\n", " \n", " \n", @@ -970,8 +970,8 @@ " \n", " TOTAL\n", " 0\n", - " 107,692\n", - " 2,206,308\n", + " 350,000\n", + " 1,964,000\n", " 2,314,000\n", " \n", " \n", @@ -982,9 +982,9 @@ "year 2026 2027 2028 TOTAL\n", "region \n", "NA 0 0 0 0\n", - "ANZ 0 107,692 1,292,308 1,400,000\n", + "ANZ 0 350,000 1,050,000 1,400,000\n", "ASIA 0 0 914,000 914,000\n", - "TOTAL 0 107,692 2,206,308 2,314,000" + "TOTAL 0 350,000 1,964,000 2,314,000" ] }, "metadata": {}, @@ -1014,10 +1014,10 @@ "id": "3cbefa13", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:47.854742Z", - "iopub.status.busy": "2026-07-08T15:15:47.854559Z", - "iopub.status.idle": "2026-07-08T15:15:48.983809Z", - "shell.execute_reply": "2026-07-08T15:15:48.980233Z" + "iopub.execute_input": "2026-07-13T19:34:23.084146Z", + "iopub.status.busy": "2026-07-13T19:34:23.083884Z", + "iopub.status.idle": "2026-07-13T19:34:24.380344Z", + "shell.execute_reply": "2026-07-13T19:34:24.379394Z" } }, "outputs": [ @@ -1068,8 +1068,8 @@ ], "y": [ 0.0, - 107692.30769230769, - 1292307.6923076923 + 350000.0, + 1050000.0 ] }, { @@ -1116,7 +1116,7 @@ "2028" ], "y": { - "bdata": "AAAAAAAAAADsxE7sxEr6QP////+Hp0FB", + "bdata": "AAAAAAAAAAAAAAAAwFwVQQAAAACIp0FB", "dtype": "f8" } } @@ -1140,9 +1140,9 @@ "size": 12 }, "showarrow": false, - "text": "$108K", + "text": "$350K", "x": 1, - "y": 107692.30769230769, + "y": 350000.0, "yshift": 12 }, { @@ -1151,9 +1151,9 @@ "size": 12 }, "showarrow": false, - "text": "$2.2M", + "text": "$2.0M", "x": 2, - "y": 2206307.692307692, + "y": 1964000.0, "yshift": 12 } ], @@ -2034,10 +2034,10 @@ "id": "ccde902f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:48.986149Z", - "iopub.status.busy": "2026-07-08T15:15:48.985913Z", - "iopub.status.idle": "2026-07-08T15:15:49.012029Z", - "shell.execute_reply": "2026-07-08T15:15:49.008783Z" + "iopub.execute_input": "2026-07-13T19:34:24.387579Z", + "iopub.status.busy": "2026-07-13T19:34:24.387133Z", + "iopub.status.idle": "2026-07-13T19:34:24.416055Z", + "shell.execute_reply": "2026-07-13T19:34:24.415071Z" } }, "outputs": [ @@ -2045,7 +2045,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Migration programme cost: {2026: '$10.2M', 2027: '$11.9M', 2028: '$3.6M'} → 3-yr $25.7M\n", + "Migration programme cost: {2026: '$10.1M', 2027: '$11.9M', 2028: '$3.6M'} → 3-yr $25.7M\n", "Do nothing: $7.3M/yr → 3-yr $21.9M\n" ] }, @@ -2093,10 +2093,10 @@ " \n", " \n", " NTT managed services (from MCX go-live)\n", - " 102,730\n", + " 68,486\n", " 410,918\n", " 410,918\n", - " 924,566\n", + " 890,323\n", " \n", " \n", " Existing platform (term-contract run-off)\n", @@ -2107,10 +2107,10 @@ " \n", " \n", " TOTAL\n", - " 10,182,454\n", + " 10,148,211\n", " 11,923,643\n", " 3,610,918\n", - " 25,717,015\n", + " 25,682,772\n", " \n", " \n", "\n", @@ -2120,16 +2120,16 @@ " 2026 2027 \\\n", "CCaaS platform licences (contracted, ramp-adjus... 1,600,000 3,200,000 \n", "NTT PS + training (SOW billing milestones) 1,179,723 1,012,723 \n", - "NTT managed services (from MCX go-live) 102,730 410,918 \n", + "NTT managed services (from MCX go-live) 68,486 410,918 \n", "Existing platform (term-contract run-off) 7,300,001 7,300,001 \n", - "TOTAL 10,182,454 11,923,643 \n", + "TOTAL 10,148,211 11,923,643 \n", "\n", " 2028 3-yr \n", "CCaaS platform licences (contracted, ramp-adjus... 3,200,000 8,000,000 \n", "NTT PS + training (SOW billing milestones) 0 2,192,446 \n", - "NTT managed services (from MCX go-live) 410,918 924,566 \n", + "NTT managed services (from MCX go-live) 410,918 890,323 \n", "Existing platform (term-contract run-off) 0 14,600,002 \n", - "TOTAL 3,610,918 25,717,015 " + "TOTAL 3,610,918 25,682,772 " ] }, "metadata": {}, @@ -2175,20 +2175,20 @@ " $607,633.94\n", " \n", " \n", - " Start of client UAT (first region)\n", - " 2026-09-30\n", + " Start of client UAT (first region, NA)\n", + " 2026-10-16\n", " 20%\n", " $405,089.30\n", " \n", " \n", - " Start of client UAT (last region)\n", - " 2027-06-30\n", + " Start of client UAT (last region, APAC)\n", + " 2027-10-01\n", " 30%\n", " $607,633.94\n", " \n", " \n", " Completion of last go-live migration\n", - " 2027-09-30\n", + " 2027-11-01\n", " 20%\n", " $405,089.30\n", " \n", @@ -2197,12 +2197,12 @@ "
" ], "text/plain": [ - " date share amount\n", - "SOW billing milestone \n", - "SOW Effective Date 2026-03-15 30% $607,633.94\n", - "Start of client UAT (first region) 2026-09-30 20% $405,089.30\n", - "Start of client UAT (last region) 2027-06-30 30% $607,633.94\n", - "Completion of last go-live migration 2027-09-30 20% $405,089.30" + " date share amount\n", + "SOW billing milestone \n", + "SOW Effective Date 2026-03-15 30% $607,633.94\n", + "Start of client UAT (first region, NA) 2026-10-16 20% $405,089.30\n", + "Start of client UAT (last region, APAC) 2027-10-01 30% $607,633.94\n", + "Completion of last go-live migration 2027-11-01 20% $405,089.30" ] }, "metadata": {}, @@ -2240,10 +2240,10 @@ "id": "c058e795", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.014884Z", - "iopub.status.busy": "2026-07-08T15:15:49.014409Z", - "iopub.status.idle": "2026-07-08T15:15:49.061734Z", - "shell.execute_reply": "2026-07-08T15:15:49.057052Z" + "iopub.execute_input": "2026-07-13T19:34:24.418363Z", + "iopub.status.busy": "2026-07-13T19:34:24.418119Z", + "iopub.status.idle": "2026-07-13T19:34:24.457096Z", + "shell.execute_reply": "2026-07-13T19:34:24.456122Z" } }, "outputs": [ @@ -2315,7 +2315,7 @@ "2028" ], "y": [ - 102729.60000000002, + 68486.40000000001, 410918.4000000001, 410918.4000000001 ] @@ -2364,7 +2364,7 @@ "2028" ], "y": { - "bdata": "rkfhuuZrY0F7FK4H/xR1QeF6FG6RhnhB", + "bdata": "SOF6VC5bY0FI4XrUogx1Qa5H4To1fnhB", "dtype": "f8" } }, @@ -2404,9 +2404,9 @@ "size": 12 }, "showarrow": false, - "text": "$10.2M", + "text": "$10.1M", "x": 0, - "y": 10182453.84, + "y": 10148210.64, "yshift": 12 }, { @@ -2440,7 +2440,7 @@ "text": "3-yr $25.7M — $3.8M above doing nothing", "x": 2, "xshift": -70, - "y": 25717014.88, + "y": 25682771.68, "yshift": 18 } ], @@ -3337,27 +3337,27 @@ "id": "d8293152", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.063837Z", - "iopub.status.busy": "2026-07-08T15:15:49.063671Z", - "iopub.status.idle": "2026-07-08T15:15:49.069028Z", - "shell.execute_reply": "2026-07-08T15:15:49.068435Z" + "iopub.execute_input": "2026-07-13T19:34:24.460722Z", + "iopub.status.busy": "2026-07-13T19:34:24.460346Z", + "iopub.status.idle": "2026-07-13T19:34:24.469686Z", + "shell.execute_reply": "2026-07-13T19:34:24.468764Z" } }, "outputs": [ { "data": { "application/mercury+json": { - "model_id": "720f723fcedb42efa4441ea0f5e2aae2", + "model_id": "fa2b443aef4f4d2ab8e12371c8da9a52", "position": "sidebar", "widget": "SelectWidget" }, "application/vnd.jupyter.widget-view+json": { - "model_id": "720f723fcedb42efa4441ea0f5e2aae2", + "model_id": "fa2b443aef4f4d2ab8e12371c8da9a52", "version_major": 2, "version_minor": 1 }, "text/plain": [ - "" + "" ] }, "metadata": {}, @@ -3378,10 +3378,10 @@ "id": "c89973af", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.070865Z", - "iopub.status.busy": "2026-07-08T15:15:49.070716Z", - "iopub.status.idle": "2026-07-08T15:15:49.076308Z", - "shell.execute_reply": "2026-07-08T15:15:49.075521Z" + "iopub.execute_input": "2026-07-13T19:34:24.472312Z", + "iopub.status.busy": "2026-07-13T19:34:24.471959Z", + "iopub.status.idle": "2026-07-13T19:34:24.480220Z", + "shell.execute_reply": "2026-07-13T19:34:24.479435Z" } }, "outputs": [ @@ -3389,7 +3389,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "net by year: {2026: '-$2.9M', 2027: '-$4.5M', 2028: '$5.9M'} → 3-yr -$1.5M\n", + "net by year: {2026: '-$2.8M', 2027: '-$4.3M', 2028: '$5.7M'} → 3-yr -$1.5M\n", "steady-state run-rate saving $6.6M/yr ($4.1M licences − $411K managed services + $2.9M WFM annuals) → breakeven 39 months (~Mar 2029, extrapolated)\n" ] } @@ -3427,10 +3427,10 @@ "id": "9d135a19", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.077976Z", - "iopub.status.busy": "2026-07-08T15:15:49.077813Z", - "iopub.status.idle": "2026-07-08T15:15:49.102760Z", - "shell.execute_reply": "2026-07-08T15:15:49.102141Z" + "iopub.execute_input": "2026-07-13T19:34:24.482509Z", + "iopub.status.busy": "2026-07-13T19:34:24.482260Z", + "iopub.status.idle": "2026-07-13T19:34:24.515278Z", + "shell.execute_reply": "2026-07-13T19:34:24.514283Z" } }, "outputs": [ @@ -3459,8 +3459,8 @@ ], "y": [ 0.0, - 107692.30769230769, - 2206307.692307692 + 350000.0, + 1964000.0 ] }, { @@ -3480,7 +3480,7 @@ "2028" ], "y": [ - -2882453.84, + -2848210.6400000006, -4623642.640000001, 3689081.6 ] @@ -3507,7 +3507,7 @@ "2028" ], "y": { - "bdata": "uB6F68r9RcHYFgcLATlcwRiuR+Em7zbB", + "bdata": "IIXrUem6RcEghetR7ypbweh6FK5jaTbB", "dtype": "f8" } } @@ -3520,9 +3520,9 @@ "size": 12 }, "showarrow": false, - "text": "-$2.9M", + "text": "-$2.8M", "x": 0, - "y": -2882453.84, + "y": -2848210.6400000006, "yshift": -14 }, { @@ -3531,9 +3531,9 @@ "size": 12 }, "showarrow": false, - "text": "-$7.4M", + "text": "-$7.1M", "x": 1, - "y": -7398404.1723076925, + "y": -7121853.280000001, "yshift": -14 }, { @@ -3544,7 +3544,7 @@ "showarrow": false, "text": "-$1.5M", "x": 2, - "y": -1503014.8800000008, + "y": -1468771.6800000016, "yshift": -14 }, { @@ -4433,10 +4433,10 @@ "id": "7ca576f1", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.105099Z", - "iopub.status.busy": "2026-07-08T15:15:49.104937Z", - "iopub.status.idle": "2026-07-08T15:15:49.115327Z", - "shell.execute_reply": "2026-07-08T15:15:49.114734Z" + "iopub.execute_input": "2026-07-13T19:34:24.517991Z", + "iopub.status.busy": "2026-07-13T19:34:24.517696Z", + "iopub.status.idle": "2026-07-13T19:34:24.533900Z", + "shell.execute_reply": "2026-07-13T19:34:24.532905Z" } }, "outputs": [ @@ -4501,7 +4501,7 @@ " \n", " NPV @ 8.0% (CTM treasury)\n", " -$3.8M\n", - " -$1.9M\n", + " -$1.8M\n", " \n", " \n", " Payback (2026–28 window)\n", @@ -4540,7 +4540,7 @@ "3-yr net -$1.5M \n", "ROI (net ÷ incremental cost) -39% \n", "NPV @ 13.5% (deck rate) -$2.0M \n", - "NPV @ 8.0% (CTM treasury) -$1.9M \n", + "NPV @ 8.0% (CTM treasury) -$1.8M \n", "Payback (2026–28 window) beyond 2028 \n", "Steady-state run-rate saving $6.6M/yr \n", "Breakeven 39 months (~Mar 2029, extrapolated) " @@ -4612,10 +4612,10 @@ "id": "4ab17e24", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.118883Z", - "iopub.status.busy": "2026-07-08T15:15:49.118721Z", - "iopub.status.idle": "2026-07-08T15:15:49.156840Z", - "shell.execute_reply": "2026-07-08T15:15:49.156126Z" + "iopub.execute_input": "2026-07-13T19:34:24.536467Z", + "iopub.status.busy": "2026-07-13T19:34:24.536203Z", + "iopub.status.idle": "2026-07-13T19:34:24.584567Z", + "shell.execute_reply": "2026-07-13T19:34:24.583618Z" } }, "outputs": [ @@ -4660,29 +4660,29 @@ "ygap": 2, "z": [ [ - -3103014.880000001, - -4903014.880000001, - -6403014.880000001 + -3068771.6800000016, + -4868771.680000002, + -6368771.680000002 ], [ - -1503014.8800000008, - -3003014.880000001, - -4253014.880000001 + -1468771.6800000016, + -2968771.6800000016, + -4218771.680000002 ], [ - 96985.11999999918, - -1103014.8800000008, - -2103014.880000001 + 131228.31999999844, + -1068771.6800000016, + -2068771.6800000016 ], [ - 1696985.1199999987, - 796985.1199999987, - 46985.119999998715 + 1731228.3199999984, + 831228.3199999984, + 81228.31999999844 ], [ - 3296985.119999999, - 2696985.119999999, - 2196985.119999999 + 3331228.3199999984, + 2731228.3199999984, + 2231228.3199999984 ] ], "zmid": 0 @@ -4746,7 +4746,7 @@ "size": 12 }, "showarrow": false, - "text": "-$4.3M", + "text": "-$4.2M", "x": 2, "y": 1 }, @@ -4756,7 +4756,7 @@ "size": 12 }, "showarrow": false, - "text": "$97K", + "text": "$131K", "x": 0, "y": 2 }, @@ -4796,7 +4796,7 @@ "size": 12 }, "showarrow": false, - "text": "$797K", + "text": "$831K", "x": 1, "y": 3 }, @@ -4806,7 +4806,7 @@ "size": 12 }, "showarrow": false, - "text": "$47K", + "text": "$81K", "x": 2, "y": 3 }, @@ -5679,7 +5679,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Contracted corner (6-mo ramp × $3.2M/yr): -$1.5M · deck-rate corner (6-mo × $4.3M/yr): -$4.3M — the licence correction recovers $2.8M but the 3-yr window stays negative; breakeven rides the post-2028 run-rate.\n" + "Contracted corner (6-mo ramp × $3.2M/yr): -$1.5M · deck-rate corner (6-mo × $4.3M/yr): -$4.2M — the licence correction recovers $2.8M but the 3-yr window stays negative; breakeven rides the post-2028 run-rate.\n" ] } ], @@ -5747,10 +5747,10 @@ "id": "939417e4", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.160025Z", - "iopub.status.busy": "2026-07-08T15:15:49.159836Z", - "iopub.status.idle": "2026-07-08T15:15:49.177363Z", - "shell.execute_reply": "2026-07-08T15:15:49.176452Z" + "iopub.execute_input": "2026-07-13T19:34:24.587372Z", + "iopub.status.busy": "2026-07-13T19:34:24.587093Z", + "iopub.status.idle": "2026-07-13T19:34:24.611911Z", + "shell.execute_reply": "2026-07-13T19:34:24.610957Z" } }, "outputs": [ @@ -5791,7 +5791,7 @@ "_approx(_ps_c[2026], 607_633.94 + 405_089.30 + 167_000, tol=0.01) # 30% + 20% + training\n", "_approx(_ps_c[2027], 607_633.94 + 405_089.30, tol=0.01) # 30% + 20%\n", "_ms_c = managed_services_by_year()\n", - "_approx(_ms_c[2026], MANAGED_SERVICES_ANNUAL * 3 / 12, tol=0.01) # Oct–Dec after go-live\n", + "_approx(_ms_c[2026], MANAGED_SERVICES_ANNUAL * 2 / 12, tol=0.01) # Nov–Dec after go-live\n", "_approx(_ms_c[2027], MANAGED_SERVICES_ANNUAL, tol=0.01)\n", "\n", "# Sections 2-3 — WFM scope & phasing at engine defaults\n", @@ -5801,7 +5801,7 @@ "_approx(_wfm_d[\"benefit\"].sum(), 2_314_000) # $0 NA + $1.4M ANZ + $914K ASIA\n", "_by_d = _wfm_d.groupby(\"year\")[\"benefit\"].sum()\n", "_approx(_by_d[2026], 0)\n", - "_approx(_by_d[2027], 1_400_000 / 13, tol=1) # ANZ: 1 of 13 live months in 2027\n", + "_approx(_by_d[2027], 1_400_000 * 4 / 16, tol=1) # ANZ: 4 of 16 live months in 2027 (PM timeline)\n", "_approx(wfm_annual_runrate(), 2_900_000)\n", "_approx(runrate_saving_annual(), 5_900_000) # deck frame: no managed services\n", "\n", @@ -5825,7 +5825,7 @@ " \"44 months (~Aug 2029, extrapolated)\"\n", "_tot_c = {y: _cur_d[y] + _lc[y] + _ps_c[y] + _ms_c[y] for y in YEARS}\n", "_inc_c, _net_c = case_flows(_tot_c, _ben_d)\n", - "_approx(sum(_net_c.values()), -1_503_013, tol=1) # contracted frame — still negative\n", + "_approx(sum(_net_c.values()), -1_468_770, tol=1) # contracted frame — still negative\n", "assert runrate_breakeven_label(\n", " _net_c, runrate_saving_annual(tco(\"ccaas_annual\"),\n", " managed_annual=MANAGED_SERVICES_ANNUAL)) == \\\n", @@ -5907,10 +5907,10 @@ "id": "32ebed57", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T15:15:49.180044Z", - "iopub.status.busy": "2026-07-08T15:15:49.179772Z", - "iopub.status.idle": "2026-07-08T15:15:49.204642Z", - "shell.execute_reply": "2026-07-08T15:15:49.203924Z" + "iopub.execute_input": "2026-07-13T19:34:24.615086Z", + "iopub.status.busy": "2026-07-13T19:34:24.614717Z", + "iopub.status.idle": "2026-07-13T19:34:24.652647Z", + "shell.execute_reply": "2026-07-13T19:34:24.651541Z" } }, "outputs": [ @@ -5941,7 +5941,7 @@ "\n", "| region | 2026 | 2027 | 2028 |\n", "|:---------|-------:|--------:|----------:|\n", - "| ANZ | 0 | 107,692 | 1,292,308 |\n", + "| ANZ | 0 | 350,000 | 1,050,000 |\n", "| ASIA | 0 | 0 | 914,000 |\n", "| NA | 0 | 0 | 0 |\n", "\n", @@ -5951,25 +5951,19 @@ "|:----------------------------------------------------|----------:|----------:|----------:|-----------:|\n", "| CCaaS platform licences (contracted, ramp-adjusted) | 1,600,000 | 3,200,000 | 3,200,000 | 8,000,000 |\n", "| NTT PS + training (SOW billing milestones) | 1,179,723 | 1,012,723 | 0 | 2,192,446 |\n", - "| NTT managed services (from MCX go-live) | 102,730 | 410,918 | 410,918 | 924,566 |\n", + "| NTT managed services (from MCX go-live) | 68,486 | 410,918 | 410,918 | 890,323 |\n", "| Existing platform (term-contract run-off) | 7,300,001 | 7,300,001 | 0 | 14,600,002 |\n", "\n", "#### Business-case flows vs do-nothing baseline ($)\n", "\n", "| | 2026 | 2027 | 2028 |\n", "|:-----------------|-----------:|-----------:|-----------:|\n", - "| programme cost | 10,182,454 | 11,923,643 | 3,610,918 |\n", - "| incremental cost | 2,882,454 | 4,623,643 | -3,689,082 |\n", - "| net | -2,882,454 | -4,515,950 | 5,895,389 |\n", + "| programme cost | 10,148,211 | 11,923,643 | 3,610,918 |\n", + "| incremental cost | 2,848,211 | 4,623,643 | -3,689,082 |\n", + "| net | -2,848,211 | -4,273,643 | 5,653,082 |\n", "\n", "#### KPIs — migration + WFM (no AI)\n", - "\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ + "\n", "| | Deck frame ($4.3M/yr, deck PS) | Contracted ($3.2M/yr + SOW) |\n", "|:-------------------------------------|:------------------------------------|:------------------------------------|\n", "| 3-yr WFM benefits (verbatim, scoped) | $2.3M | $2.3M |\n", @@ -5977,7 +5971,7 @@ "| 3-yr net | -$3.7M | -$1.5M |\n", "| ROI (net ÷ incremental cost) | -62% | -39% |\n", "| NPV @ 13.5% (deck rate) | -$3.8M | -$2.0M |\n", - "| NPV @ 8.0% (CTM treasury) | -$3.8M | -$1.9M |\n", + "| NPV @ 8.0% (CTM treasury) | -$3.8M | -$1.8M |\n", "| Payback (2026–28 window) | beyond 2028 | beyond 2028 |\n", "| Steady-state run-rate saving | $5.9M/yr | $6.6M/yr |\n", "| Breakeven | 44 months (~Aug 2029, extrapolated) | 39 months (~Mar 2029, extrapolated) |\n", @@ -5989,25 +5983,25 @@ " \"scenario\": \"Genesys Cloud CX migration + WFM (NA migrate, APAC implement) \\u2014 no AI\",\n", " \"wfm_benefits_by_year\": {\n", " \"2026\": 0,\n", - " \"2027\": 107692,\n", - " \"2028\": 2206308\n", + " \"2027\": 350000,\n", + " \"2028\": 1964000\n", " },\n", " \"cost_by_year\": {\n", - " \"2026\": 10182454,\n", + " \"2026\": 10148211,\n", " \"2027\": 11923643,\n", " \"2028\": 3610918\n", " },\n", " \"net_by_year\": {\n", - " \"2026\": -2882454,\n", - " \"2027\": -4515950,\n", - " \"2028\": 5895389\n", + " \"2026\": -2848211,\n", + " \"2027\": -4273643,\n", + " \"2028\": 5653082\n", " },\n", " \"kpis\": {\n", " \"benefits_3yr\": 2314000,\n", - " \"incremental_cost_3yr\": 3817015,\n", - " \"net_3yr\": -1503015,\n", - " \"roi\": -0.3938,\n", - " \"npv\": -2013126,\n", + " \"incremental_cost_3yr\": 3782772,\n", + " \"net_3yr\": -1468772,\n", + " \"roi\": -0.3883,\n", + " \"npv\": -1960584,\n", " \"discount_rate\": 0.135,\n", " \"payback\": \"beyond 2028\"\n", " },\n", @@ -6016,7 +6010,7 @@ " \"incremental_cost_3yr\": 6017002,\n", " \"net_3yr\": -3703002,\n", " \"roi\": -0.6154,\n", - " \"npv\": -3849521,\n", + " \"npv\": -3827149,\n", " \"discount_rate\": 0.135,\n", " \"payback\": \"beyond 2028\"\n", " },\n", @@ -6034,23 +6028,23 @@ " \"amount\": 607633.94\n", " },\n", " {\n", - " \"name\": \"Start of client UAT (first region)\",\n", - " \"date\": \"2026-09-30\",\n", + " \"name\": \"Start of client UAT (first region, NA)\",\n", + " \"date\": \"2026-10-16\",\n", " \"amount\": 405089.3\n", " },\n", " {\n", - " \"name\": \"Start of client UAT (last region)\",\n", - " \"date\": \"2027-06-30\",\n", + " \"name\": \"Start of client UAT (last region, APAC)\",\n", + " \"date\": \"2027-10-01\",\n", " \"amount\": 607633.94\n", " },\n", " {\n", " \"name\": \"Completion of last go-live migration\",\n", - " \"date\": \"2027-09-30\",\n", + " \"date\": \"2027-11-01\",\n", " \"amount\": 405089.3\n", " }\n", " ],\n", " \"managed_services_annual\": 410918.4,\n", - " \"mcx_go_live\": \"2026-09-30\",\n", + " \"mcx_go_live\": \"2026-10-16\",\n", " \"licence_includes_wfm_skus\": true,\n", " \"wfm_regions\": [\n", " \"NA\",\n", @@ -6172,7 +6166,7 @@ "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { - "098f9852a68245118ba3cb197da7c299": { + "038fdeed63e244b7b3ee5a36c876039e": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -6225,427 +6219,7 @@ "width": null } }, - "0d71f3541aea48d29335b75aabae1a05": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1d4ddc81f2c249cf9be44e32e8213b85": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "WFM — ASIA (implement)", - "last_changed_at": "", - "layout": "IPY_MODEL_b50136dc74fb4036b657d83d7b10c5e6", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": true - } - }, - "217923ff9f174ff0b1e46c612cb7cd44": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "28eb2b2d2a2a41adaad830ac204d7b39": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "ANZ — current platform cost ($/yr)", - "layout": "IPY_MODEL_343cc107bc7b4369a4e654d5a2a888b1", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 677320.0 - } - }, - "2be8dde4d1644691add8dae88a36ac81": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "WFM — NA (migrate existing users)", - "last_changed_at": "", - "layout": "IPY_MODEL_d9bccb3d954a4ad88eca96950703a866", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": true - } - }, - "343cc107bc7b4369a4e654d5a2a888b1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "36fd828f151b4a7ba019f601b7a2099e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3d24b7abdffd4cb580cf7a42348d1cd0": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "423a009e5b26436c9ccdb69e031722a4": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4d3b2b0c854b4bcab393ef4f89aee1b4": { + "0fff3f6c07fe4316a91344082245882c": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -6665,7 +6239,7 @@ "disabled": false, "hidden": false, "label": "ANZ — contract termination", - "layout": "IPY_MODEL_3d24b7abdffd4cb580cf7a42348d1cd0", + "layout": "IPY_MODEL_9de21a8805534b84a0d44b60c998113a", "layout_path": null, "max": "", "min": "", @@ -6678,60 +6252,7 @@ "value": "2027-12-31" } }, - "58dab83fd1e24bd8b609d9fe3fd8420c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "5c47680e253c4f45bbbbfcc76612499b": { + "1138fba4608d4de484f84b11e1ae2862": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -6753,7 +6274,7 @@ "hidden": false, "label": "WFM — EMEA (upside option)", "last_changed_at": "", - "layout": "IPY_MODEL_217923ff9f174ff0b1e46c612cb7cd44", + "layout": "IPY_MODEL_d263a846169b4fcf8493e58a0a277820", "layout_path": null, "n_toggles": 0, "position": "sidebar", @@ -6765,40 +6286,7 @@ "value": false } }, - "68b411c315ff4357815d7ff9801f445a": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.date.DateInputWidget", - "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "ASIA — contract termination", - "layout": "IPY_MODEL_36fd828f151b4a7ba019f601b7a2099e", - "layout_path": null, - "max": "", - "min": "", - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "2027-12-31" - } - }, - "6bc9b75143f244bbbb3a29cdbcb95e53": { + "362dfb796cfc4881a89dd7e566db84d6": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -6851,7 +6339,541 @@ "width": null } }, - "6d231d033d7b4f41b852046f069ebc3f": { + "401b35065476433d96aeed7fcad746e8": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ANZ — current platform cost ($/yr)", + "layout": "IPY_MODEL_ba02c7051fbc4fd0a304e49019f0e69b", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 677320.0 + } + }, + "4b5a5306392949a9a6c9f1414e28e166": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "655850770ff34fb8bf1076de30bf2ddc": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Genesys licence run-rate ($/yr)", + "layout": "IPY_MODEL_9871a3fecaec4886b2da0dfdfa495ad9", + "layout_path": null, + "max": 10000000.0, + "min": 0.0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "step": 100000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 3200000.0 + } + }, + "6b6471d39e2544f58844cffb1d57afe9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HTMLView", + "cell_id": "", + "description": "", + "description_allow_html": false, + "layout": "IPY_MODEL_aa069fb9941e456191f05ffe96e143be", + "layout_path": null, + "placeholder": "​", + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "style": "IPY_MODEL_d7fc62778a6f48fa8e03760f8e67f869", + "tabbable": null, + "tooltip": null, + "value": "" + } + }, + "7e22e4c6e3dd44fc8eeb96c4f501a342": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "EMEA — contract termination", + "layout": "IPY_MODEL_4b5a5306392949a9a6c9f1414e28e166", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "837fa7d9dfef4562b952d2a7a9ef2b99": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8ffdd60ef54a4c829429b46b438c36db": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "WFM — NA (migrate existing users)", + "last_changed_at": "", + "layout": "IPY_MODEL_f69fbf92b5684033896094b83d45cfd6", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "9045ff71291a455abb6f1d9e3c2cc471": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9871a3fecaec4886b2da0dfdfa495ad9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9d2e70e1ffab471692880e7ce645219d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9de21a8805534b84a0d44b60c998113a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9eeb15eeffa249929fca65f62246b45b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a4de65fac18f44e086d105b7f75693fe": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -6871,7 +6893,7 @@ "disabled": false, "hidden": false, "label": "NA — current platform cost ($/yr)", - "layout": "IPY_MODEL_58dab83fd1e24bd8b609d9fe3fd8420c", + "layout": "IPY_MODEL_b1b5ab2787cd409bb2552372f9ace4b4", "layout_path": null, "max": 8000000.0, "min": 0.0, @@ -6885,7 +6907,599 @@ "value": 3348969.0 } }, - "720f723fcedb42efa4441ea0f5e2aae2": { + "aa069fb9941e456191f05ffe96e143be": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "abd40a60e1974670886b2bd43aab35c2": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "WFM — ASIA (implement)", + "last_changed_at": "", + "layout": "IPY_MODEL_9d2e70e1ffab471692880e7ce645219d", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "b1b5ab2787cd409bb2552372f9ace4b4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b26784cff5864afb8f4760fd7bb8aa1a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b549f3626d8e46f2bf7e849cf91e381f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ba02c7051fbc4fd0a304e49019f0e69b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "bab7c81ec7ae47deabd7fc74f205c38e": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "EMEA — current platform cost ($/yr)", + "layout": "IPY_MODEL_038fdeed63e244b7b3ee5a36c876039e", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 1204124.0 + } + }, + "c2a141345720426b933108c66be94b8b": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ASIA — current platform cost ($/yr)", + "layout": "IPY_MODEL_b26784cff5864afb8f4760fd7bb8aa1a", + "layout_path": null, + "max": 8000000.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 10000.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 2069588.0 + } + }, + "d263a846169b4fcf8493e58a0a277820": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d4d0cebd7a9345e2a88c5fa842d24337": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.checkbox.CheckboxWidget", + "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "appearance": "toggle", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "WFM — ANZ (implement)", + "last_changed_at": "", + "layout": "IPY_MODEL_9eeb15eeffa249929fca65f62246b45b", + "layout_path": null, + "n_toggles": 0, + "position": "sidebar", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": true + } + }, + "d7fc62778a6f48fa8e03760f8e67f869": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HTMLStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HTMLStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "background": null, + "description_width": "", + "font_size": null, + "text_color": null + } + }, + "ec33300bf1e14da98f0dd7bfc01a2f44": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.date.DateInputWidget", + "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "ASIA — contract termination", + "layout": "IPY_MODEL_362dfb796cfc4881a89dd7e566db84d6", + "layout_path": null, + "max": "", + "min": "", + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": "2027-12-31" + } + }, + "f69fbf92b5684033896094b83d45cfd6": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f755a69ae50540b3aac94ceb249b1cc2": { + "model_module": "anywidget", + "model_module_version": "~0.11.*", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "mercury.number.NumberInputWidget", + "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", + "_dom_classes": [], + "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", + "_model_module": "anywidget", + "_model_module_version": "~0.11.*", + "_model_name": "AnyModel", + "_view_count": null, + "_view_module": "anywidget", + "_view_module_version": "~0.11.*", + "_view_name": "AnyView", + "cell_id": "", + "disabled": false, + "hidden": false, + "label": "Genesys ramp — licence-free months", + "layout": "IPY_MODEL_9045ff71291a455abb6f1d9e3c2cc471", + "layout_path": null, + "max": 24.0, + "min": 0.0, + "position": "inline", + "render_slot_id": null, + "source_cell_id": null, + "step": 1.0, + "tabbable": null, + "tooltip": null, + "url_key": "", + "value": 6.0 + } + }, + "fa2b443aef4f4d2ab8e12371c8da9a52": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -6909,7 +7523,7 @@ "disabled": false, "hidden": false, "label": "NPV discount rate", - "layout": "IPY_MODEL_0d71f3541aea48d29335b75aabae1a05", + "layout": "IPY_MODEL_837fa7d9dfef4562b952d2a7a9ef2b99", "layout_path": null, "position": "sidebar", "render_slot_id": null, @@ -6920,180 +7534,7 @@ "value": "13.5% (deck)" } }, - "74d727dbe17e46cb9c8da2283a3acb50": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "800dd2c22a2c4172ae2a846f620f97d9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8e89c81717ff408d9e5cb1a949ab337c": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.date.DateInputWidget", - "_css": "\n .mljar-date-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-date-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-date-input {\n width: 100%;\n min-height: 40px;\n padding: 9px 10px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n color: #0f172a;\n box-sizing: border-box;\n line-height: 1.4;\n }\n\n .mljar-date-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-date-input:focus {\n outline: none;\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n @media (max-width: 768px) {\n .mljar-date-input {\n min-height: 44px;\n padding: 10px 12px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-date-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-date-label\");\n\n const input = document.createElement(\"input\");\n input.type = \"date\";\n input.classList.add(\"mljar-date-input\");\n\n container.appendChild(topLabel);\n container.appendChild(input);\n el.appendChild(container);\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Date\";\n input.value = model.get(\"value\") || \"\";\n\n const min = model.get(\"min\") || \"\";\n const max = model.get(\"max\") || \"\";\n if (min) input.min = min; else input.removeAttribute(\"min\");\n if (max) input.max = max; else input.removeAttribute(\"max\");\n\n input.disabled = !!model.get(\"disabled\");\n container.style.display = model.get(\"hidden\") ? \"none\" : \"flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n model.set(\"value\", input.value);\n model.save_changes();\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "EMEA — contract termination", - "layout": "IPY_MODEL_098f9852a68245118ba3cb197da7c299", - "layout_path": null, - "max": "", - "min": "", - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": "2027-12-31" - } - }, - "9a1300f4e5374fa6bb1c9717866f8ce1": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "EMEA — current platform cost ($/yr)", - "layout": "IPY_MODEL_423a009e5b26436c9ccdb69e031722a4", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 1204124.0 - } - }, - "b2ec5338e0ba4055b77a57edd08b464c": { + "ff3441542e0b43db946e184e4b461b1e": { "model_module": "anywidget", "model_module_version": "~0.11.*", "model_name": "AnyModel", @@ -7113,7 +7554,7 @@ "disabled": false, "hidden": false, "label": "NA — contract termination", - "layout": "IPY_MODEL_e7597a4eb1c14475a440ac83ee50ce1c", + "layout": "IPY_MODEL_b549f3626d8e46f2bf7e849cf91e381f", "layout_path": null, "max": "", "min": "", @@ -7125,453 +7566,6 @@ "url_key": "", "value": "2027-12-31" } - }, - "b50136dc74fb4036b657d83d7b10c5e6": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "cc5b28489de346d3b59e3ace05fb2ebb": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Genesys licence run-rate ($/yr)", - "layout": "IPY_MODEL_800dd2c22a2c4172ae2a846f620f97d9", - "layout_path": null, - "max": 10000000.0, - "min": 0.0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "step": 100000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 3200000.0 - } - }, - "cddbd4fd3deb4ed1a29dad5cf082939b": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.checkbox.CheckboxWidget", - "_css": "\n .mljar-checkbox-container {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n cursor: pointer;\n user-select: none;\n -webkit-tap-highlight-color: transparent;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n color: #0f172a;\n padding-left: 5px;\n }\n .mljar-checkbox-container.is-disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .mljar-checkbox-input {\n position: absolute;\n opacity: 0;\n width: 0;\n height: 0;\n }\n\n .mljar-checkbox-input:focus-visible + .mljar-checkbox-control {\n box-shadow: inset 0 0 0 2px #007bff;\n }\n\n .mljar-checkbox-label {\n font-size: 14px;\n line-height: 1.2;\n padding-top: 2px;\n }\n\n /* --- Toggle style (15% smaller) --- */\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control {\n position: relative;\n width: 34px;\n height: 19px;\n border-radius: 999px;\n background: #f1f1f2;\n border: 1px solid #cfd1d5;\n transition: background 150ms ease, border-color 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control {\n background: #007bff;\n border-color: #007bff;\n }\n .mljar-checkbox-container.is-toggle .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 15px;\n height: 15px;\n border-radius: 50%;\n background: #ffffff;\n box-shadow: 0 1px 2px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04);\n transition: transform 150ms ease;\n }\n .mljar-checkbox-container.is-toggle.is-checked .mljar-checkbox-control::after {\n transform: translateX(15px);\n }\n\n /* --- Classic box style --- */\n .mljar-checkbox-container.is-box .mljar-checkbox-control {\n width: 16px;\n height: 16px;\n border-radius: 4px;\n border: 1px solid #cfd1d5;\n background: #ffffff;\n display: inline-block;\n position: relative;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control {\n border-color: #007bff;\n background: #007bff;\n }\n .mljar-checkbox-container.is-box.is-checked .mljar-checkbox-control::after {\n content: \"\";\n position: absolute;\n left: 4px;\n top: 0px;\n width: 5px;\n height: 10px;\n border: solid #fff;\n border-width: 0 2px 2px 0;\n transform: rotate(45deg);\n }\n\n .mljar-checkbox-container:not(.is-disabled):hover .mljar-checkbox-control {\n border-color: #007bff;\n background: #f3f3f4;\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"label\");\n container.classList.add(\"mljar-checkbox-container\");\n\n const input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.classList.add(\"mljar-checkbox-input\");\n\n const control = document.createElement(\"span\");\n control.classList.add(\"mljar-checkbox-control\");\n\n const text = document.createElement(\"span\");\n text.classList.add(\"mljar-checkbox-label\");\n\n container.appendChild(input);\n container.appendChild(control);\n container.appendChild(text);\n el.appendChild(container);\n\n function syncFromModel() {\n // appearance\n const a = model.get(\"appearance\") || \"toggle\";\n container.classList.remove(\"is-toggle\",\"is-box\");\n container.classList.add(`is-${a}`);\n input.setAttribute(\"role\", a === \"toggle\" ? \"switch\" : \"checkbox\");\n\n // value\n const v = !!model.get(\"value\");\n if (input.checked !== v) {\n input.checked = v;\n input.setAttribute(\"aria-checked\", String(v));\n }\n container.classList.toggle(\"is-checked\", v);\n\n // disabled\n const d = !!model.get(\"disabled\");\n input.disabled = d;\n container.classList.toggle(\"is-disabled\", d);\n\n // label\n text.textContent = model.get(\"label\") || \"\";\n\n // hidden (exists but not visible)\n container.style.display = model.get(\"hidden\") ? \"none\" : \"inline-flex\";\n }\n\n input.addEventListener(\"change\", () => {\n if (model.get(\"disabled\")) return;\n\n const v = input.checked;\n if (model.get(\"value\") !== v) {\n model.set(\"value\", v);\n model.set(\"last_changed_at\", new Date().toISOString());\n model.set(\"n_toggles\", (model.get(\"n_toggles\") || 0) + 1);\n model.save_changes();\n // model.send({ type: \"changed\", value: v });\n }\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:appearance\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n \n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }\n */\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "appearance": "toggle", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "WFM — ANZ (implement)", - "last_changed_at": "", - "layout": "IPY_MODEL_de9d84197e314233938eb4ad7b19ad0b", - "layout_path": null, - "n_toggles": 0, - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": true - } - }, - "cdf8fe1626a54e7eb048e9e8d27cb2a4": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "ASIA — current platform cost ($/yr)", - "layout": "IPY_MODEL_74d727dbe17e46cb9c8da2283a3acb50", - "layout_path": null, - "max": 8000000.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 10000.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 2069588.0 - } - }, - "cf504293e15c4cd99f1a492d73d8e263": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HTMLView", - "cell_id": "", - "description": "", - "description_allow_html": false, - "layout": "IPY_MODEL_6bc9b75143f244bbbb3a29cdbcb95e53", - "layout_path": null, - "placeholder": "​", - "position": "sidebar", - "render_slot_id": null, - "source_cell_id": null, - "style": "IPY_MODEL_f1dd0e56a7224aafa77c1c3c23e9fdb0", - "tabbable": null, - "tooltip": null, - "value": "" - } - }, - "d9afd02316174736b31a7964fe071605": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d9bccb3d954a4ad88eca96950703a866": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "ddacc3befdc54c278abccd4a5b6b98b5": { - "model_module": "anywidget", - "model_module_version": "~0.11.*", - "model_name": "AnyModel", - "state": { - "_anywidget_id": "mercury.number.NumberInputWidget", - "_css": "\n .mljar-number-container {\n display: flex;\n flex-direction: column;\n width: 100%;\n font-family: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;\n font-size: 14px;\n color: #0f172a;\n margin-bottom: 8px;\n padding-left: 4px;\n padding-right: 4px;\n box-sizing: border-box;\n }\n\n .mljar-number-label {\n margin-bottom: 4px;\n font-weight: 600;\n }\n\n .mljar-number-field-row {\n display: flex;\n align-items: stretch;\n width: 100%;\n min-height: 40px;\n border: 1px solid #cfd1d5;\n border-radius: 6px;\n background: #ffffff;\n box-sizing: border-box;\n overflow: hidden;\n }\n\n .mljar-number-input {\n flex: 1 1 auto;\n min-width: 0;\n min-height: 100%;\n padding: 7px 10px;\n border: 0;\n border-radius: 0;\n background: #ffffff;\n box-sizing: border-box;\n background-color: #ffffff !important;\n color: #0f172a !important;\n font: inherit;\n line-height: 1.2;\n -moz-appearance: textfield;\n }\n\n .mljar-number-input::-webkit-outer-spin-button,\n .mljar-number-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n }\n\n .mljar-number-input:disabled {\n background: #f5f5f5;\n color: #888;\n cursor: not-allowed;\n }\n\n .mljar-number-input:focus {\n outline: none;\n }\n\n .mljar-number-field-row:focus-within {\n border-color: #007bff;\n border-width: 2px;\n box-shadow: none;\n }\n\n .mljar-number-field-row:focus-within .mljar-number-controls {\n border-left-color: #007bff;\n }\n\n .mljar-number-controls {\n display: flex;\n align-items: stretch;\n flex: 0 0 auto;\n border-left: 1px solid #cfd1d5;\n background: #f1f1f2;\n }\n\n .mljar-number-step-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 38px;\n min-width: 38px;\n min-height: 100%;\n border: 0;\n border-radius: 0;\n background: transparent;\n color: #0f172a;\n font: inherit;\n font-size: 18px;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n padding: 0;\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n transition: background-color 0.14s ease, color 0.14s ease;\n }\n\n .mljar-number-step-up {\n border-left: 1px solid #cfd1d5;\n }\n\n .mljar-number-step-btn:hover {\n background: #f3f3f4;\n }\n\n .mljar-number-step-btn:active {\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:focus-visible {\n outline: none;\n background: #e6f2ff;\n color: #007bff;\n }\n\n .mljar-number-step-btn:disabled {\n background: #f5f5f5;\n color: #aaa;\n cursor: not-allowed;\n }\n\n @media (max-width: 768px) {\n .mljar-number-field-row {\n min-height: 44px;\n }\n\n .mljar-number-input {\n min-height: 44px;\n padding: 8px 12px;\n }\n\n .mljar-number-step-btn {\n font-size: 19px;\n width: 44px;\n min-width: 44px;\n }\n }\n ", - "_dom_classes": [], - "_esm": "\n function render({ model, el }) {\n const container = document.createElement(\"div\");\n container.classList.add(\"mljar-number-container\");\n\n const topLabel = document.createElement(\"div\");\n topLabel.classList.add(\"mljar-number-label\");\n\n const fieldRow = document.createElement(\"div\");\n fieldRow.classList.add(\"mljar-number-field-row\");\n\n const input = document.createElement(\"input\");\n input.type = \"number\";\n input.classList.add(\"mljar-number-input\");\n\n const decrementBtn = document.createElement(\"button\");\n decrementBtn.type = \"button\";\n decrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-down\");\n decrementBtn.textContent = \"-\";\n decrementBtn.setAttribute(\"aria-label\", \"Decrease value\");\n\n const incrementBtn = document.createElement(\"button\");\n incrementBtn.type = \"button\";\n incrementBtn.classList.add(\"mljar-number-step-btn\", \"mljar-number-step-up\");\n incrementBtn.textContent = \"+\";\n incrementBtn.setAttribute(\"aria-label\", \"Increase value\");\n\n const controls = document.createElement(\"div\");\n controls.classList.add(\"mljar-number-controls\");\n\n controls.appendChild(decrementBtn);\n controls.appendChild(incrementBtn);\n fieldRow.appendChild(input);\n fieldRow.appendChild(controls);\n\n container.appendChild(topLabel);\n container.appendChild(fieldRow);\n el.appendChild(container);\n\n function clamp(val, min, max) {\n if (Number.isFinite(min) && val < min) return min;\n if (Number.isFinite(max) && val > max) return max;\n return val;\n }\n\n function normalizeStep(step) {\n return Number.isFinite(step) && step > 0 ? step : 1;\n }\n\n function snapToStep(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = Math.round((value - base) / safeStep);\n const snapped = base + steps * safeStep;\n const precision = Math.max(\n 0,\n (String(safeStep).split(\".\")[1] || \"\").length\n );\n\n return Number(snapped.toFixed(precision + 2));\n }\n\n function isOnStepGrid(value, min, step) {\n const safeStep = normalizeStep(step);\n const base = Number.isFinite(min) ? min : 0;\n const steps = (value - base) / safeStep;\n const nearest = Math.round(steps);\n const epsilon = Math.max(1e-9, safeStep * 1e-9);\n\n return Math.abs(steps - nearest) <= epsilon;\n }\n\n function getCurrentBounds() {\n return {\n min: Number(model.get(\"min\")),\n max: Number(model.get(\"max\")),\n };\n }\n\n function isTransientDraft(raw) {\n return raw === \"\" || raw === \"-\" || raw === \".\" || raw === \"-.\";\n }\n\n let isEditing = false;\n const INPUT_COMMIT_DEBOUNCE_MS = 400;\n\n function clearPendingDraftCommit() {\n if (debounceTimer) clearTimeout(debounceTimer);\n pendingDraftValue = null;\n }\n\n function parseDraftValue(rawValue) {\n const raw = String(rawValue).trim();\n if (isTransientDraft(raw)) {\n return { kind: \"transient\" };\n }\n\n const value = Number(raw);\n if (!Number.isFinite(value)) {\n return { kind: \"invalid\" };\n }\n\n return { kind: \"number\", value };\n }\n\n function commitValue(nextValue, saveNow = true) {\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n const parsed = parseDraftValue(nextValue);\n if (parsed.kind !== \"number\") {\n syncFromModel();\n return;\n }\n\n let v = parsed.value;\n v = clamp(v, min, max);\n v = snapToStep(v, min, step);\n v = clamp(v, min, max);\n input.value = String(v);\n model.set(\"value\", v);\n\n if (saveNow) {\n model.save_changes();\n }\n }\n\n function syncFromModel() {\n topLabel.innerHTML = model.get(\"label\") || \"Enter number\";\n\n const min = Number(model.get(\"min\"));\n const max = Number(model.get(\"max\"));\n const step = Number(model.get(\"step\"));\n\n if (Number.isFinite(min)) input.min = String(min); else input.removeAttribute(\"min\");\n if (Number.isFinite(max)) input.max = String(max); else input.removeAttribute(\"max\");\n if (Number.isFinite(step)) input.step = String(step); else input.removeAttribute(\"step\");\n\n const v = Number(model.get(\"value\"));\n if (!isEditing) {\n input.value = Number.isFinite(v) ? String(v) : \"\";\n }\n\n const disabled = !!model.get(\"disabled\");\n input.disabled = disabled;\n incrementBtn.disabled = disabled;\n decrementBtn.disabled = disabled;\n\n const hidden = !!model.get(\"hidden\");\n container.style.display = hidden ? \"none\" : \"flex\";\n }\n\n let debounceTimer = null;\n let pendingDraftValue = null;\n input.addEventListener(\"focus\", () => {\n isEditing = true;\n });\n\n input.addEventListener(\"input\", () => {\n if (model.get(\"disabled\")) return;\n\n const parsed = parseDraftValue(input.value);\n if (parsed.kind !== \"number\") {\n clearPendingDraftCommit();\n return;\n }\n\n const v = parsed.value;\n const { min, max } = getCurrentBounds();\n const step = Number(model.get(\"step\"));\n if (Number.isFinite(min) && v < min) {\n clearPendingDraftCommit();\n return;\n }\n if (Number.isFinite(max) && v > max) {\n clearPendingDraftCommit();\n return;\n }\n if (!isOnStepGrid(v, min, step)) {\n clearPendingDraftCommit();\n return;\n }\n\n pendingDraftValue = v;\n if (debounceTimer) clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n if (pendingDraftValue === null) return;\n model.set(\"value\", pendingDraftValue);\n model.save_changes();\n pendingDraftValue = null;\n }, INPUT_COMMIT_DEBOUNCE_MS);\n });\n\n input.addEventListener(\"blur\", () => {\n isEditing = false;\n clearPendingDraftCommit();\n commitValue(input.value, true);\n });\n\n input.addEventListener(\"keydown\", event => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n input.blur();\n }\n });\n\n incrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base + step, min, step));\n });\n\n decrementBtn.addEventListener(\"click\", () => {\n if (model.get(\"disabled\")) return;\n const current = Number(model.get(\"value\"));\n const step = normalizeStep(Number(model.get(\"step\")));\n const min = Number(model.get(\"min\"));\n const base = Number.isFinite(current) ? current : 0;\n commitValue(snapToStep(base - step, min, step));\n });\n\n model.on(\"change:value\", syncFromModel);\n model.on(\"change:min\", syncFromModel);\n model.on(\"change:max\", syncFromModel);\n model.on(\"change:step\", syncFromModel);\n model.on(\"change:label\", syncFromModel);\n model.on(\"change:disabled\", syncFromModel);\n model.on(\"change:hidden\", syncFromModel);\n\n syncFromModel();\n\n // ---- read cell id (no DOM modifications) ----\n /*\n const ID_ATTR = \"data-cell-id\";\n const hostWithId = el.closest(`[${ID_ATTR}]`);\n const cellId = hostWithId ? hostWithId.getAttribute(ID_ATTR) : null;\n\n if (cellId) {\n model.set(\"cell_id\", cellId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: cellId });\n } else {\n const mo = new MutationObserver(() => {\n const host = el.closest(`[${ID_ATTR}]`);\n const newId = host?.getAttribute(ID_ATTR);\n if (newId) {\n model.set(\"cell_id\", newId);\n model.save_changes();\n model.send({ type: \"cell_id_detected\", value: newId });\n mo.disconnect();\n }\n });\n mo.observe(document.body, { attributes: true, subtree: true, attributeFilter: [ID_ATTR] });\n }*/\n }\n export default { render };\n ", - "_model_module": "anywidget", - "_model_module_version": "~0.11.*", - "_model_name": "AnyModel", - "_view_count": null, - "_view_module": "anywidget", - "_view_module_version": "~0.11.*", - "_view_name": "AnyView", - "cell_id": "", - "disabled": false, - "hidden": false, - "label": "Genesys ramp — licence-free months", - "layout": "IPY_MODEL_d9afd02316174736b31a7964fe071605", - "layout_path": null, - "max": 24.0, - "min": 0.0, - "position": "inline", - "render_slot_id": null, - "source_cell_id": null, - "step": 1.0, - "tabbable": null, - "tooltip": null, - "url_key": "", - "value": 6.0 - } - }, - "de9d84197e314233938eb4ad7b19ad0b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e7597a4eb1c14475a440ac83ee50ce1c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f1dd0e56a7224aafa77c1c3c23e9fdb0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HTMLStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HTMLStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "background": null, - "description_width": "", - "font_size": null, - "text_color": null - } } }, "version_major": 2, diff --git a/studies/202607_CTM_GenesysCX/notebooks/ctm_token_calculator.ipynb b/studies/202607_CTM_GenesysCX/notebooks/ctm_token_calculator.ipynb index c9bdd2e..b557a9c 100644 --- a/studies/202607_CTM_GenesysCX/notebooks/ctm_token_calculator.ipynb +++ b/studies/202607_CTM_GenesysCX/notebooks/ctm_token_calculator.ipynb @@ -26,10 +26,10 @@ "id": "57efb206", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.333726Z", - "iopub.status.busy": "2026-07-08T14:26:20.333472Z", - "iopub.status.idle": "2026-07-08T14:26:20.859435Z", - "shell.execute_reply": "2026-07-08T14:26:20.858762Z" + "iopub.execute_input": "2026-07-13T19:32:57.711516Z", + "iopub.status.busy": "2026-07-13T19:32:57.711205Z", + "iopub.status.idle": "2026-07-13T19:32:58.206644Z", + "shell.execute_reply": "2026-07-13T19:32:58.205709Z" } }, "outputs": [ @@ -81,10 +81,10 @@ "id": "361bde24", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.863561Z", - "iopub.status.busy": "2026-07-08T14:26:20.863323Z", - "iopub.status.idle": "2026-07-08T14:26:20.893620Z", - "shell.execute_reply": "2026-07-08T14:26:20.892775Z" + "iopub.execute_input": "2026-07-13T19:32:58.210328Z", + "iopub.status.busy": "2026-07-13T19:32:58.209921Z", + "iopub.status.idle": "2026-07-13T19:32:58.234502Z", + "shell.execute_reply": "2026-07-13T19:32:58.233799Z" } }, "outputs": [ @@ -478,10 +478,10 @@ "id": "72311955", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.898251Z", - "iopub.status.busy": "2026-07-08T14:26:20.897714Z", - "iopub.status.idle": "2026-07-08T14:26:20.912681Z", - "shell.execute_reply": "2026-07-08T14:26:20.911767Z" + "iopub.execute_input": "2026-07-13T19:32:58.236321Z", + "iopub.status.busy": "2026-07-13T19:32:58.236148Z", + "iopub.status.idle": "2026-07-13T19:32:58.246527Z", + "shell.execute_reply": "2026-07-13T19:32:58.245798Z" } }, "outputs": [ @@ -793,10 +793,10 @@ "id": "8b24676f", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.916526Z", - "iopub.status.busy": "2026-07-08T14:26:20.916164Z", - "iopub.status.idle": "2026-07-08T14:26:20.921252Z", - "shell.execute_reply": "2026-07-08T14:26:20.920512Z" + "iopub.execute_input": "2026-07-13T19:32:58.248518Z", + "iopub.status.busy": "2026-07-13T19:32:58.248332Z", + "iopub.status.idle": "2026-07-13T19:32:58.252173Z", + "shell.execute_reply": "2026-07-13T19:32:58.251375Z" } }, "outputs": [ @@ -825,17 +825,17 @@ "id": "810e9dae", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.923553Z", - "iopub.status.busy": "2026-07-08T14:26:20.923302Z", - "iopub.status.idle": "2026-07-08T14:26:20.986453Z", - "shell.execute_reply": "2026-07-08T14:26:20.985763Z" + "iopub.execute_input": "2026-07-13T19:32:58.253831Z", + "iopub.status.busy": "2026-07-13T19:32:58.253676Z", + "iopub.status.idle": "2026-07-13T19:32:58.302237Z", + "shell.execute_reply": "2026-07-13T19:32:58.301248Z" } }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "638076a7ebde4826978fd461fd23d9c3", + "model_id": "69f65b8fab04496fa3f3b45116f421e5", "version_major": 2, "version_minor": 0 }, @@ -849,7 +849,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "12e64cff7b2a49c9ab1285c5655f4457", + "model_id": "2b35efbfa83f4dfb994080acef7b28cc", "version_major": 2, "version_minor": 0 }, @@ -931,10 +931,10 @@ "id": "65773422", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.989712Z", - "iopub.status.busy": "2026-07-08T14:26:20.989541Z", - "iopub.status.idle": "2026-07-08T14:26:20.995164Z", - "shell.execute_reply": "2026-07-08T14:26:20.994448Z" + "iopub.execute_input": "2026-07-13T19:32:58.304432Z", + "iopub.status.busy": "2026-07-13T19:32:58.304177Z", + "iopub.status.idle": "2026-07-13T19:32:58.312707Z", + "shell.execute_reply": "2026-07-13T19:32:58.311008Z" } }, "outputs": [ @@ -1019,17 +1019,17 @@ "id": "872af216", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:20.997810Z", - "iopub.status.busy": "2026-07-08T14:26:20.997439Z", - "iopub.status.idle": "2026-07-08T14:26:21.019924Z", - "shell.execute_reply": "2026-07-08T14:26:21.018568Z" + "iopub.execute_input": "2026-07-13T19:32:58.315429Z", + "iopub.status.busy": "2026-07-13T19:32:58.315136Z", + "iopub.status.idle": "2026-07-13T19:32:58.329667Z", + "shell.execute_reply": "2026-07-13T19:32:58.328977Z" } }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f1eaf046c0694901a1630c26d9bfbe85", + "model_id": "cf854dec89944ceca7119f9f177abfd5", "version_major": 2, "version_minor": 0 }, @@ -1096,10 +1096,10 @@ "id": "07ed3a9e", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:21.025205Z", - "iopub.status.busy": "2026-07-08T14:26:21.024604Z", - "iopub.status.idle": "2026-07-08T14:26:21.953313Z", - "shell.execute_reply": "2026-07-08T14:26:21.952528Z" + "iopub.execute_input": "2026-07-13T19:32:58.334260Z", + "iopub.status.busy": "2026-07-13T19:32:58.334037Z", + "iopub.status.idle": "2026-07-13T19:32:59.377489Z", + "shell.execute_reply": "2026-07-13T19:32:59.376614Z" } }, "outputs": [ @@ -2124,10 +2124,10 @@ "id": "e7fc3a73", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:21.955295Z", - "iopub.status.busy": "2026-07-08T14:26:21.955169Z", - "iopub.status.idle": "2026-07-08T14:26:21.969100Z", - "shell.execute_reply": "2026-07-08T14:26:21.968473Z" + "iopub.execute_input": "2026-07-13T19:32:59.380017Z", + "iopub.status.busy": "2026-07-13T19:32:59.379684Z", + "iopub.status.idle": "2026-07-13T19:32:59.407404Z", + "shell.execute_reply": "2026-07-13T19:32:59.406552Z" } }, "outputs": [ @@ -2375,10 +2375,10 @@ "id": "3ac4fee1", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:21.971514Z", - "iopub.status.busy": "2026-07-08T14:26:21.971380Z", - "iopub.status.idle": "2026-07-08T14:26:21.987749Z", - "shell.execute_reply": "2026-07-08T14:26:21.987124Z" + "iopub.execute_input": "2026-07-13T19:32:59.410682Z", + "iopub.status.busy": "2026-07-13T19:32:59.410375Z", + "iopub.status.idle": "2026-07-13T19:32:59.435201Z", + "shell.execute_reply": "2026-07-13T19:32:59.432035Z" } }, "outputs": [ @@ -2518,10 +2518,10 @@ "id": "fa4c9cdc", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:21.989793Z", - "iopub.status.busy": "2026-07-08T14:26:21.989658Z", - "iopub.status.idle": "2026-07-08T14:26:22.467383Z", - "shell.execute_reply": "2026-07-08T14:26:22.466582Z" + "iopub.execute_input": "2026-07-13T19:32:59.440342Z", + "iopub.status.busy": "2026-07-13T19:32:59.440009Z", + "iopub.status.idle": "2026-07-13T19:32:59.903308Z", + "shell.execute_reply": "2026-07-13T19:32:59.902627Z" } }, "outputs": [ @@ -3583,10 +3583,10 @@ "id": "380914e0", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:22.469658Z", - "iopub.status.busy": "2026-07-08T14:26:22.469486Z", - "iopub.status.idle": "2026-07-08T14:26:22.491013Z", - "shell.execute_reply": "2026-07-08T14:26:22.490362Z" + "iopub.execute_input": "2026-07-13T19:32:59.906155Z", + "iopub.status.busy": "2026-07-13T19:32:59.905981Z", + "iopub.status.idle": "2026-07-13T19:32:59.928045Z", + "shell.execute_reply": "2026-07-13T19:32:59.926937Z" } }, "outputs": [ @@ -3697,10 +3697,10 @@ "id": "ae5fb48b", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:22.493375Z", - "iopub.status.busy": "2026-07-08T14:26:22.493214Z", - "iopub.status.idle": "2026-07-08T14:26:22.601521Z", - "shell.execute_reply": "2026-07-08T14:26:22.600862Z" + "iopub.execute_input": "2026-07-13T19:32:59.930242Z", + "iopub.status.busy": "2026-07-13T19:32:59.930066Z", + "iopub.status.idle": "2026-07-13T19:33:00.022614Z", + "shell.execute_reply": "2026-07-13T19:33:00.022032Z" } }, "outputs": [ @@ -5549,10 +5549,10 @@ "id": "7bd6456e", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:22.604241Z", - "iopub.status.busy": "2026-07-08T14:26:22.603951Z", - "iopub.status.idle": "2026-07-08T14:26:22.670128Z", - "shell.execute_reply": "2026-07-08T14:26:22.669363Z" + "iopub.execute_input": "2026-07-13T19:33:00.025440Z", + "iopub.status.busy": "2026-07-13T19:33:00.025068Z", + "iopub.status.idle": "2026-07-13T19:33:00.089596Z", + "shell.execute_reply": "2026-07-13T19:33:00.088897Z" } }, "outputs": [ @@ -5843,10 +5843,10 @@ "id": "c869422e", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:22.672308Z", - "iopub.status.busy": "2026-07-08T14:26:22.672148Z", - "iopub.status.idle": "2026-07-08T14:26:22.841046Z", - "shell.execute_reply": "2026-07-08T14:26:22.840390Z" + "iopub.execute_input": "2026-07-13T19:33:00.092683Z", + "iopub.status.busy": "2026-07-13T19:33:00.092484Z", + "iopub.status.idle": "2026-07-13T19:33:00.264469Z", + "shell.execute_reply": "2026-07-13T19:33:00.263571Z" } }, "outputs": [ @@ -6719,10 +6719,10 @@ "id": "bab4c657", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:22.843681Z", - "iopub.status.busy": "2026-07-08T14:26:22.843516Z", - "iopub.status.idle": "2026-07-08T14:26:23.481559Z", - "shell.execute_reply": "2026-07-08T14:26:23.480845Z" + "iopub.execute_input": "2026-07-13T19:33:00.266887Z", + "iopub.status.busy": "2026-07-13T19:33:00.266647Z", + "iopub.status.idle": "2026-07-13T19:33:00.944991Z", + "shell.execute_reply": "2026-07-13T19:33:00.943841Z" } }, "outputs": [ @@ -7595,10 +7595,10 @@ "id": "2b3dee90", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:23.485264Z", - "iopub.status.busy": "2026-07-08T14:26:23.485095Z", - "iopub.status.idle": "2026-07-08T14:26:24.814498Z", - "shell.execute_reply": "2026-07-08T14:26:24.813929Z" + "iopub.execute_input": "2026-07-13T19:33:00.947480Z", + "iopub.status.busy": "2026-07-13T19:33:00.947233Z", + "iopub.status.idle": "2026-07-13T19:33:02.288725Z", + "shell.execute_reply": "2026-07-13T19:33:02.288084Z" } }, "outputs": [ @@ -8485,10 +8485,10 @@ "id": "57611721", "metadata": { "execution": { - "iopub.execute_input": "2026-07-08T14:26:24.819382Z", - "iopub.status.busy": "2026-07-08T14:26:24.819221Z", - "iopub.status.idle": "2026-07-08T14:26:25.044813Z", - "shell.execute_reply": "2026-07-08T14:26:25.043869Z" + "iopub.execute_input": "2026-07-13T19:33:02.293716Z", + "iopub.status.busy": "2026-07-13T19:33:02.293526Z", + "iopub.status.idle": "2026-07-13T19:33:02.541639Z", + "shell.execute_reply": "2026-07-13T19:33:02.541002Z" } }, "outputs": [ @@ -8496,8 +8496,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "Excel → /home/robert/git/palladium/studies/202512_GenesysCX/ctm-token-calculator/exports/ctm_token_calculator_realistic.xlsx\n", - "JSON → /home/robert/git/palladium/studies/202512_GenesysCX/ctm-token-calculator/exports/ctm_scenario_realistic.json\n" + "Excel → /home/robert/git/palladium/studies/202607_CTM_GenesysCX/exports/ctm_token_calculator_realistic.xlsx\n", + "JSON → /home/robert/git/palladium/studies/202607_CTM_GenesysCX/exports/ctm_scenario_realistic.json\n" ] } ], @@ -8568,55 +8568,7 @@ "widgets": { "application/vnd.jupyter.widget-state+json": { "state": { - "06907bbe86db48ccbc956fd27da8a519": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "ToggleButtonsStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "ToggleButtonsStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "button_width": "", - "description_width": "", - "font_weight": "" - } - }, - "0cd5200fee1443ab8bf177dcda697022": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "FloatSliderModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "FloatSliderModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "FloatSliderView", - "behavior": "drag-tap", - "continuous_update": true, - "description": "VA %", - "description_allow_html": false, - "disabled": false, - "layout": "IPY_MODEL_df8a7801a2f748248ce4c6a19ae22015", - "max": 0.5, - "min": 0.0, - "orientation": "horizontal", - "readout": true, - "readout_format": ".0%", - "step": 0.01, - "style": "IPY_MODEL_997e3595822a430c929fdfb2080dd1bb", - "tabbable": null, - "tooltip": null, - "value": 0.15 - } - }, - "12617a53edb749218e7216a9a740cef8": { + "06a1bded636445f6bf53fded75148290": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -8669,7 +8621,7 @@ "width": null } }, - "12e64cff7b2a49c9ab1285c5655f4457": { + "2b35efbfa83f4dfb994080acef7b28cc": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "ToggleButtonsModel", @@ -8693,69 +8645,14 @@ "disabled": false, "icons": [], "index": 0, - "layout": "IPY_MODEL_746c833373da4dce892bf4db54864f28", - "style": "IPY_MODEL_4d7601df2bdd4653ae4c732ed6e7fd0a", + "layout": "IPY_MODEL_06a1bded636445f6bf53fded75148290", + "style": "IPY_MODEL_f9f37238bba4439dbc32d0ed649e17f9", "tabbable": null, "tooltip": null, "tooltips": [] } }, - "12f81e44138e45b08c3def7a64399b41": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "FloatSliderModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "FloatSliderModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "FloatSliderView", - "behavior": "drag-tap", - "continuous_update": true, - "description": "Bot %", - "description_allow_html": false, - "disabled": false, - "layout": "IPY_MODEL_a118aede53134fd8b46a76f20e507670", - "max": 0.7, - "min": 0.0, - "orientation": "horizontal", - "readout": true, - "readout_format": ".0%", - "step": 0.01, - "style": "IPY_MODEL_4fb09530f11240a393f0fc5b9abf0493", - "tabbable": null, - "tooltip": null, - "value": 0.35 - } - }, - "164cc23f3e5c402999bbcf1927410042": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_12f81e44138e45b08c3def7a64399b41", - "IPY_MODEL_b86814e4dbf14956b98cf2b2a69efaa0", - "IPY_MODEL_0cd5200fee1443ab8bf177dcda697022" - ], - "layout": "IPY_MODEL_b4622b7d885a4db08b3c06f2287aceae", - "tabbable": null, - "tooltip": null - } - }, - "2124cc42f17e412695d938edaffa91c8": { + "3462a88bfec94f01b6aea7530a431884": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -8808,7 +8705,113 @@ "width": null } }, - "21ce7908fb3d4185afeb53af2ca52a1f": { + "3dab48323eae42f4b02df79e20773531": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4c9514e9b96142e9a269035bdd0714c4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "577793de6703445bbac50d16994744b8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "SliderStyleModel", @@ -8824,7 +8827,38 @@ "handle_color": null } }, - "2883e60da5d444a8b3b9f1fd2b4dc5a3": { + "60707879e2d748d381f46fc74f0afe83": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatSliderModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatSliderModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "FloatSliderView", + "behavior": "drag-tap", + "continuous_update": true, + "description": "VA %", + "description_allow_html": false, + "disabled": false, + "layout": "IPY_MODEL_d68795b0944341bf8354500e127e4e59", + "max": 0.5, + "min": 0.0, + "orientation": "horizontal", + "readout": true, + "readout_format": ".0%", + "step": 0.01, + "style": "IPY_MODEL_7e58dd9a55e44efd8c5b0d566118304c", + "tabbable": null, + "tooltip": null, + "value": 0.15 + } + }, + "6482ebca307b4d04996420d94f2aa0ac": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "SliderStyleModel", @@ -8840,24 +8874,7 @@ "handle_color": null } }, - "4d7601df2bdd4653ae4c732ed6e7fd0a": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "ToggleButtonsStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "ToggleButtonsStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "button_width": "", - "description_width": "", - "font_weight": "" - } - }, - "4fb09530f11240a393f0fc5b9abf0493": { + "65b0678176734553be6e24141b82f58c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "SliderStyleModel", @@ -8873,7 +8890,7 @@ "handle_color": null } }, - "638076a7ebde4826978fd461fd23d9c3": { + "69f65b8fab04496fa3f3b45116f421e5": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "ToggleButtonsModel", @@ -8897,556 +8914,14 @@ "disabled": false, "icons": [], "index": 1, - "layout": "IPY_MODEL_2124cc42f17e412695d938edaffa91c8", - "style": "IPY_MODEL_06907bbe86db48ccbc956fd27da8a519", + "layout": "IPY_MODEL_8afe69103c0948ec805cb3fc5c831d4a", + "style": "IPY_MODEL_cc3fc8777cd34c539aa36ea9b125f2a4", "tabbable": null, "tooltip": null, "tooltips": [] } }, - "638411fa1d2f4a56848f845b33a6cd2e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "70e5f83eee964da2acfbac46a79ac10e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "FloatSliderModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "FloatSliderModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "FloatSliderView", - "behavior": "drag-tap", - "continuous_update": true, - "description": "Completion", - "description_allow_html": false, - "disabled": false, - "layout": "IPY_MODEL_a2e49111d5364f9aafcb7280bd689664", - "max": 1.0, - "min": 0.3, - "orientation": "horizontal", - "readout": true, - "readout_format": ".0%", - "step": 0.01, - "style": "IPY_MODEL_7dc8918d4e0f49b1b73a5777ca6e2a0c", - "tabbable": null, - "tooltip": null, - "value": 0.7 - } - }, - "73ce8fea4d9444999e165a019f0a68ed": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "746c833373da4dce892bf4db54864f28": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7dc8918d4e0f49b1b73a5777ca6e2a0c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "SliderStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "SliderStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "description_width": "", - "handle_color": null - } - }, - "8df8182142a542baac5e799c7cdecedf": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "95d9a518fdef4741835a899418270cce": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_70e5f83eee964da2acfbac46a79ac10e", - "IPY_MODEL_cf9e8cb142324efda5fd5fc23ea86c62", - "IPY_MODEL_a28d1ef5ddee4259a160036809f1a2b2" - ], - "layout": "IPY_MODEL_638411fa1d2f4a56848f845b33a6cd2e", - "tabbable": null, - "tooltip": null - } - }, - "997e3595822a430c929fdfb2080dd1bb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "SliderStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "SliderStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "StyleView", - "description_width": "", - "handle_color": null - } - }, - "a118aede53134fd8b46a76f20e507670": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a28d1ef5ddee4259a160036809f1a2b2": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "2.0.0", - "model_name": "FloatSliderModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "2.0.0", - "_model_name": "FloatSliderModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "2.0.0", - "_view_name": "FloatSliderView", - "behavior": "drag-tap", - "continuous_update": true, - "description": "Callback", - "description_allow_html": false, - "disabled": false, - "layout": "IPY_MODEL_12617a53edb749218e7216a9a740cef8", - "max": 0.3, - "min": 0.0, - "orientation": "horizontal", - "readout": true, - "readout_format": ".0%", - "step": 0.01, - "style": "IPY_MODEL_d1fe3ddf07dd4de7993bb9678116f9cc", - "tabbable": null, - "tooltip": null, - "value": 0.05 - } - }, - "a2e49111d5364f9aafcb7280bd689664": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a68f48677402477c92e96916c5201f83": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b4622b7d885a4db08b3c06f2287aceae": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "2.0.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "2.0.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "2.0.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border_bottom": null, - "border_left": null, - "border_right": null, - "border_top": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b86814e4dbf14956b98cf2b2a69efaa0": { + "6ce00d0669b94edcbc7574f9e690bb46": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "FloatSliderModel", @@ -9464,51 +8939,68 @@ "description": "Bot min", "description_allow_html": false, "disabled": false, - "layout": "IPY_MODEL_8df8182142a542baac5e799c7cdecedf", + "layout": "IPY_MODEL_3462a88bfec94f01b6aea7530a431884", "max": 5.0, "min": 0.5, "orientation": "horizontal", "readout": true, "readout_format": ".2f", "step": 0.1, - "style": "IPY_MODEL_21ce7908fb3d4185afeb53af2ca52a1f", + "style": "IPY_MODEL_da443aae09c44a919b74f57076cd36f9", "tabbable": null, "tooltip": null, "value": 1.5 } }, - "cf9e8cb142324efda5fd5fc23ea86c62": { + "712b05a4eb6a4dacb70abd6df8a7dfbf": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", - "model_name": "FloatSliderModel", + "model_name": "HBoxModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "2.0.0", - "_model_name": "FloatSliderModel", + "_model_name": "HBoxModel", "_view_count": null, "_view_module": "@jupyter-widgets/controls", "_view_module_version": "2.0.0", - "_view_name": "FloatSliderView", - "behavior": "drag-tap", - "continuous_update": true, - "description": "Labour real.", - "description_allow_html": false, - "disabled": false, - "layout": "IPY_MODEL_73ce8fea4d9444999e165a019f0a68ed", - "max": 1.0, - "min": 0.3, - "orientation": "horizontal", - "readout": true, - "readout_format": ".0%", - "step": 0.01, - "style": "IPY_MODEL_2883e60da5d444a8b3b9f1fd2b4dc5a3", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_94ae237e32054fbebbab3a3b05563fe1", + "IPY_MODEL_6ce00d0669b94edcbc7574f9e690bb46", + "IPY_MODEL_60707879e2d748d381f46fc74f0afe83" + ], + "layout": "IPY_MODEL_3dab48323eae42f4b02df79e20773531", "tabbable": null, - "tooltip": null, - "value": 0.8 + "tooltip": null } }, - "d1fe3ddf07dd4de7993bb9678116f9cc": { + "7cd6b5152e354e418c8a9ee1a738208e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_d4545b6a131b4490a6b546d9e2b5f1f5", + "IPY_MODEL_f4843600b298491eb47c6e21eb2e66f3", + "IPY_MODEL_fa90689f37c64ad59800e48ecc3e095b" + ], + "layout": "IPY_MODEL_fd382d2b7de247f79150b34908bbd6bd", + "tabbable": null, + "tooltip": null + } + }, + "7e58dd9a55e44efd8c5b0d566118304c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "SliderStyleModel", @@ -9524,7 +9016,7 @@ "handle_color": null } }, - "df8a7801a2f748248ce4c6a19ae22015": { + "8afe69103c0948ec805cb3fc5c831d4a": { "model_module": "@jupyter-widgets/base", "model_module_version": "2.0.0", "model_name": "LayoutModel", @@ -9577,7 +9069,230 @@ "width": null } }, - "f1eaf046c0694901a1630c26d9bfbe85": { + "8d83e7fb02114d57adff5f3021cadd24": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "SliderStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "SliderStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "description_width": "", + "handle_color": null + } + }, + "94ae237e32054fbebbab3a3b05563fe1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatSliderModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatSliderModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "FloatSliderView", + "behavior": "drag-tap", + "continuous_update": true, + "description": "Bot %", + "description_allow_html": false, + "disabled": false, + "layout": "IPY_MODEL_9fe19d91747e4865a104e65b658b7c8e", + "max": 0.7, + "min": 0.0, + "orientation": "horizontal", + "readout": true, + "readout_format": ".0%", + "step": 0.01, + "style": "IPY_MODEL_65b0678176734553be6e24141b82f58c", + "tabbable": null, + "tooltip": null, + "value": 0.35 + } + }, + "995b326fefa942da94ce18e92254860e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9fe19d91747e4865a104e65b658b7c8e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a8101d75b1794ba48f5b3ee90c545005": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cc3fc8777cd34c539aa36ea9b125f2a4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ToggleButtonsStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ToggleButtonsStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "button_width": "", + "description_width": "", + "font_weight": "" + } + }, + "cf854dec89944ceca7119f9f177abfd5": { "model_module": "@jupyter-widgets/controls", "model_module_version": "2.0.0", "model_name": "VBoxModel", @@ -9592,13 +9307,298 @@ "_view_name": "VBoxView", "box_style": "", "children": [ - "IPY_MODEL_164cc23f3e5c402999bbcf1927410042", - "IPY_MODEL_95d9a518fdef4741835a899418270cce" + "IPY_MODEL_712b05a4eb6a4dacb70abd6df8a7dfbf", + "IPY_MODEL_7cd6b5152e354e418c8a9ee1a738208e" ], - "layout": "IPY_MODEL_a68f48677402477c92e96916c5201f83", + "layout": "IPY_MODEL_a8101d75b1794ba48f5b3ee90c545005", "tabbable": null, "tooltip": null } + }, + "d4545b6a131b4490a6b546d9e2b5f1f5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatSliderModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatSliderModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "FloatSliderView", + "behavior": "drag-tap", + "continuous_update": true, + "description": "Completion", + "description_allow_html": false, + "disabled": false, + "layout": "IPY_MODEL_fd591354d1214f4b9b7d8850be81a511", + "max": 1.0, + "min": 0.3, + "orientation": "horizontal", + "readout": true, + "readout_format": ".0%", + "step": 0.01, + "style": "IPY_MODEL_577793de6703445bbac50d16994744b8", + "tabbable": null, + "tooltip": null, + "value": 0.7 + } + }, + "d68795b0944341bf8354500e127e4e59": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "da443aae09c44a919b74f57076cd36f9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "SliderStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "SliderStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "description_width": "", + "handle_color": null + } + }, + "f4843600b298491eb47c6e21eb2e66f3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatSliderModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatSliderModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "FloatSliderView", + "behavior": "drag-tap", + "continuous_update": true, + "description": "Labour real.", + "description_allow_html": false, + "disabled": false, + "layout": "IPY_MODEL_4c9514e9b96142e9a269035bdd0714c4", + "max": 1.0, + "min": 0.3, + "orientation": "horizontal", + "readout": true, + "readout_format": ".0%", + "step": 0.01, + "style": "IPY_MODEL_8d83e7fb02114d57adff5f3021cadd24", + "tabbable": null, + "tooltip": null, + "value": 0.8 + } + }, + "f9f37238bba4439dbc32d0ed649e17f9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "ToggleButtonsStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "ToggleButtonsStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "StyleView", + "button_width": "", + "description_width": "", + "font_weight": "" + } + }, + "fa90689f37c64ad59800e48ecc3e095b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "2.0.0", + "model_name": "FloatSliderModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "2.0.0", + "_model_name": "FloatSliderModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "2.0.0", + "_view_name": "FloatSliderView", + "behavior": "drag-tap", + "continuous_update": true, + "description": "Callback", + "description_allow_html": false, + "disabled": false, + "layout": "IPY_MODEL_995b326fefa942da94ce18e92254860e", + "max": 0.3, + "min": 0.0, + "orientation": "horizontal", + "readout": true, + "readout_format": ".0%", + "step": 0.01, + "style": "IPY_MODEL_6482ebca307b4d04996420d94f2aa0ac", + "tabbable": null, + "tooltip": null, + "value": 0.05 + } + }, + "fd382d2b7de247f79150b34908bbd6bd": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "fd591354d1214f4b9b7d8850be81a511": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "2.0.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "2.0.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border_bottom": null, + "border_left": null, + "border_right": null, + "border_top": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } } }, "version_major": 2, diff --git a/studies/202607_CTM_GenesysCX/tests/test_appendix4.py b/studies/202607_CTM_GenesysCX/tests/test_appendix4.py index a33f684..41ada45 100644 --- a/studies/202607_CTM_GenesysCX/tests/test_appendix4.py +++ b/studies/202607_CTM_GenesysCX/tests/test_appendix4.py @@ -25,11 +25,12 @@ def test_verbatim_crossfoots_to_slide_totals(): a4.crossfoot_tolerance(a4.SLIDE_TOTALS["total_3yr"]) -def test_benefits_phase_on_the_deck_schedule(): +def test_benefits_phase_on_the_pm_schedule(): _, _, benefit_rollout = a4.build_rollouts(SITES) long = a4.benefits_by_year(benefit_rollout) by_year = long.groupby("year")["benefit"].sum() - assert by_year[2026] == 0.0, "2026 must be $0 under Genesys's own schedule" + # Earliest region (NA) realizes Jan 2027 → 2026 still $0. + assert by_year[2026] == 0.0, "2026 must be $0 (NA realizes Jan 2027)" # Scaling at the finest grain reproduces every verbatim 3-yr value exactly. for (region, cap), (_, three_yr) in a4.VERBATIM_BENEFITS.items(): got = long.query("region == @region and capability == @cap")["benefit"].sum() @@ -65,16 +66,18 @@ def test_token_hand_checks(): long = a4.token_costs_by_year(SITES, meters, DEFAULT_PRICING, a4.claim_scenario(0.255), core, email, token_ro, email_ro) - # STA 2028: NAM/AUZ/EMEA × 12 months + ASIA × 10 months, by hand. + # STA 2028: every site live all 12 months under the compressed PM + # timeline (APAC live from Nov 2027) → full-year STA for all sites. sta = long.query("cost_line == 'Speech & Text Analytics [named]'") - assert sta.query("year == 2028")["annual_cost"].sum() == pytest.approx(715_800) + assert sta.query("year == 2028")["annual_cost"].sum() == pytest.approx(751_680) # Agent Copilot 2028 (ASIA off): 1,490 users × 40 tokens × 12 months. cp = long.query("cost_line == 'Agent Copilot [named]' and year == 2028") assert cp["annual_cost"].sum() == pytest.approx(1_490 * 40 * 12) # Rule 1: Copilot covers AI Summary at Copilot sites. assert (long.query("cost_line == 'AI Summary & Insights'")["annual_cost"] == 0).all() - # Nothing is live in 2026. - assert long.query("year == 2026")["annual_cost"].sum() == 0 + # Under the PM timeline NA goes live Oct 2026, so tokens DO accrue in + # 2026 (NA only — three live months); no longer a $0 year. + assert long.query("year == 2026")["annual_cost"].sum() == pytest.approx(424_350) # PR NAM steady-month tokens. assert math.ceil( 1_214_358 * DEFAULT_METERS["Predictive Routing"].tokens_per_unit) == 71_433 @@ -130,8 +133,8 @@ def test_sow_milestones_and_managed_services(): assert ps[2028] == 0.0 # The deck's verbatim year-1 lump stays intact for the as-pitched frame. assert a4.ps_costs_by_year() == {2026: 2_567_000, 2027: 0.0, 2028: 0.0} - # Managed services bill from the month after MCX go-live (Sep 30 → Oct). + # Managed services bill from the month after MCX go-live (Oct 16 → Nov). ms = a4.managed_services_by_year() - assert ms[2026] == pytest.approx(410_918.40 * 3 / 12) + assert ms[2026] == pytest.approx(410_918.40 * 2 / 12) assert ms[2027] == pytest.approx(410_918.40) assert ms[2028] == pytest.approx(410_918.40) diff --git a/studies/202607_CTM_GenesysCX/tests/test_migration_wfm.py b/studies/202607_CTM_GenesysCX/tests/test_migration_wfm.py index 25dcc4e..6f865df 100644 --- a/studies/202607_CTM_GenesysCX/tests/test_migration_wfm.py +++ b/studies/202607_CTM_GenesysCX/tests/test_migration_wfm.py @@ -25,13 +25,14 @@ def test_wfm_scope_and_verbatim_total(): assert ben["benefit"].sum() == pytest.approx(2_314_000) -def test_wfm_phasing_on_deck_schedule(): +def test_wfm_phasing_on_pm_schedule(): ben = mw.wfm_benefits_by_year(_default_benefit_rollout()) by_year = ben.groupby("year")["benefit"].sum() assert by_year[2026] == 0.0 - # ANZ realizes Dec 2027 (1 of 13 live months lands in 2027). - assert by_year[2027] == pytest.approx(1_400_000 / 13) - assert by_year[2028] == pytest.approx(2_314_000 - 1_400_000 / 13) + # PM timeline: ANZ realizes Sep 2027 → 4 of 16 live months in 2027 + # (Sep-Dec); ASIA realizes Jan 2028 → all in 2028. + assert by_year[2027] == pytest.approx(1_400_000 * 4 / 16) + assert by_year[2028] == pytest.approx(2_314_000 - 1_400_000 * 4 / 16) def test_runrate_saving_annual(): @@ -76,7 +77,7 @@ def test_contracted_frame_with_sow_and_managed_services(): man = a4.managed_services_by_year() total = {y: cur[y] + lic[y] + ps[y] + man[y] for y in a4.YEARS} inc, net = a4.case_flows(total, _default_wfm_benefits_by_year()) - assert sum(net.values()) == pytest.approx(-1_503_013, abs=1_000) + assert sum(net.values()) == pytest.approx(-1_468_770, abs=1_000) runrate = mw.runrate_saving_annual( a4.tco("ccaas_annual"), managed_annual=a4.MANAGED_SERVICES_ANNUAL) label = mw.runrate_breakeven_label(net, runrate) diff --git a/studies/202607_CTM_GenesysCX/tokencalc/appendix4.py b/studies/202607_CTM_GenesysCX/tokencalc/appendix4.py index d72da02..4a35493 100644 --- a/studies/202607_CTM_GenesysCX/tokencalc/appendix4.py +++ b/studies/202607_CTM_GenesysCX/tokencalc/appendix4.py @@ -113,11 +113,11 @@ TCO_CONTRACTED: dict[str, float] = { PS_MILESTONES: list[dict] = [ {"name": "SOW Effective Date", "date": dt.date(2026, 3, 15), "share": 0.30, "amount": 607_633.94}, - {"name": "Start of client UAT (first region)", "date": dt.date(2026, 9, 30), + {"name": "Start of client UAT (first region, NA)", "date": dt.date(2026, 10, 16), "share": 0.20, "amount": 405_089.30}, - {"name": "Start of client UAT (last region)", "date": dt.date(2027, 6, 30), + {"name": "Start of client UAT (last region, APAC)", "date": dt.date(2027, 10, 1), "share": 0.30, "amount": 607_633.94}, - {"name": "Completion of last go-live migration", "date": dt.date(2027, 9, 30), + {"name": "Completion of last go-live migration", "date": dt.date(2027, 11, 1), "share": 0.20, "amount": 405_089.30}, ] PS_CONTRACTED_TOTAL = sum(m["amount"] for m in PS_MILESTONES) @@ -125,20 +125,23 @@ PS_CONTRACTED_TOTAL = sum(m["amount"] for m in PS_MILESTONES) #: NTT managed services — commences billing at MCX go-live (🟢 contractual). #: Not in the deck's TCO at all; an ongoing run-rate cost thereafter. MANAGED_SERVICES_ANNUAL = 410_918.40 -MCX_GO_LIVE = dt.date(2026, 9, 30) +MCX_GO_LIVE = dt.date(2026, 10, 16) def tco(key: str) -> float: """Contracted value where one exists, else the deck's verbatim anchor.""" return TCO_CONTRACTED.get(key, TCO_VERBATIM[key]) -#: Genesys/Broadreach deployment schedule (slides 17-21), months from -#: Jan 2026 inclusive. Benefits realize IMPL + 3 months. -IMPL_MONTH = {"NA": 18, "ANZ": 21, "EMEA": 24, "ASIA": 27} +#: Deployment schedule per the current PM delivery timeline (Jul 2026), +#: months from Jan 2026 inclusive. UAT start is the implementation anchor: +#: NA Oct 2026 (m10), EMEA Feb 2027 (m14), ANZ Jun 2027 (m18), +#: APAC Oct 2027 (m22). Benefits realize IMPL + 3 months. +IMPL_MONTH = {"NA": 10, "EMEA": 14, "ANZ": 18, "ASIA": 22} BENEFIT_LAG_MONTHS = 3 REALIZE_MONTH = {r: m + BENEFIT_LAG_MONTHS for r, m in IMPL_MONTH.items()} -#: NA Gantt exception: Email implemented Jan 2027, realizes Apr 2027. -NA_EMAIL_IMPL_MONTH = 13 +#: NA-Email-early exception retired under the compressed timeline +#: (NA implements Oct 2026; Email cannot realize before NA itself). +NA_EMAIL_IMPL_MONTH = IMPL_MONTH["NA"] DEFAULT_RAMP_MONTHS = 6 # Genesys ramp programme (🟢 order form) DEFAULT_TERMINATION = dt.date(2027, 12, 31) # current-platform term contracts @@ -321,7 +324,7 @@ def ps_milestones_dataframe() -> pd.DataFrame: def managed_services_by_year( annual: float = MANAGED_SERVICES_ANNUAL, start: dt.date = MCX_GO_LIVE ) -> dict[int, float]: - """Managed services bill from the month after go-live (Sep 30 → Oct), + """Managed services bill from the month after go-live (Oct 16 → Nov), then run at the full annual rate — an ongoing cost with no end date inside the model window.""" def _months(y: int) -> int: