diff --git a/package-lock.json b/package-lock.json
index e0a3961186631738dbb50a3ce01b8100f2bcde32..7493aec7b1496c5c64df9f62a50e9d20cc8660e0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -60,7 +60,7 @@
         "sanitize-filename": "^1.6.3",
         "scroll-into-view-if-needed": "^3.0.10",
         "slug": "^10.0.0",
-        "svelte": "^5.9.0",
+        "svelte": "^5.17.3",
         "svelte-check": "^4.0.1",
         "svelte-dnd-action": "^0.9.8",
         "svelte-floating-ui": "^1.5.3",
@@ -3116,9 +3116,9 @@
       }
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.5.79",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.79.tgz",
-      "integrity": "sha512-nYOxJNxQ9Om4EC88BE4pPoNI8xwSFf8pU/BAeOl4Hh/b/i6V4biTAzwV7pXi3ARKeoYO5JZKMIXTryXSVer5RA==",
+      "version": "1.5.80",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.80.tgz",
+      "integrity": "sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==",
       "dev": true,
       "license": "ISC"
     },
@@ -3490,9 +3490,9 @@
       }
     },
     "node_modules/esrap": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz",
-      "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==",
+      "version": "1.4.2",
+      "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.4.2.tgz",
+      "integrity": "sha512-FhVlJzvTw7ZLxYZ7RyHwQCFE64dkkpzGNNnphaGCLwjqGk1SQcqzbgdx9FowPCktx6NOSHkzvcZ3vsvdH54YXA==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -6527,9 +6527,9 @@
       }
     },
     "node_modules/svelte": {
-      "version": "5.17.1",
-      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.17.1.tgz",
-      "integrity": "sha512-HitqD0XhU9OEytPuux/XYzxle4+7D8+fIb1tHbwMzOtBzDZZO+ESEuwMbahJ/3JoklfmRPB/Gzp74L87Qrxfpw==",
+      "version": "5.17.3",
+      "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.17.3.tgz",
+      "integrity": "sha512-eLgtpR2JiTgeuNQRCDcLx35Z7Lu9Qe09GPOz+gvtR9nmIZu5xgFd6oFiLGQlxLD0/u7xVyF5AUkjDVyFHe6Bvw==",
       "dev": true,
       "license": "MIT",
       "dependencies": {
@@ -7017,9 +7017,9 @@
       }
     },
     "node_modules/tough-cookie": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
-      "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz",
+      "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==",
       "dev": true,
       "license": "BSD-3-Clause",
       "dependencies": {
@@ -7237,9 +7237,9 @@
       "license": "MIT"
     },
     "node_modules/uuid": {
-      "version": "11.0.4",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.4.tgz",
-      "integrity": "sha512-IzL6VtTTYcAhA/oghbFJ1Dkmqev+FpQWnCBaKq/gUluLxliWvO8DPFWfIviRmYbtaavtSQe4WBL++rFjdcGWEg==",
+      "version": "11.0.5",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz",
+      "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==",
       "dev": true,
       "funding": [
         "https://github.com/sponsors/broofa",
diff --git a/package.json b/package.json
index e6150509714571c6cdc8082813ac929fc5188e09..ad53dac59d98dea5e669c3eb070de4aa0025d28b 100644
--- a/package.json
+++ b/package.json
@@ -65,7 +65,7 @@
     "sanitize-filename": "^1.6.3",
     "scroll-into-view-if-needed": "^3.0.10",
     "slug": "^10.0.0",
-    "svelte": "^5.9.0",
+    "svelte": "^5.17.3",
     "svelte-check": "^4.0.1",
     "svelte-dnd-action": "^0.9.8",
     "svelte-floating-ui": "^1.5.3",
diff --git a/src/lib/components/WaterfallView.svelte b/src/lib/components/WaterfallView.svelte
index 174c43f1c11fb203efa0bd9d5858f4e857fe7e83..8155632d5d63897d1eb0266ef41cf5277c2f56ed 100644
--- a/src/lib/components/WaterfallView.svelte
+++ b/src/lib/components/WaterfallView.svelte
@@ -186,11 +186,19 @@
   }
 
   function removeSituationSlider() {
-    situation = { ...situation }
-    delete situation.slider
+    if (shared.savedSituation !== undefined) {
+      situation = shared.savedSituation
+      delete shared.savedSituation
+      if (shared.savedSituationIndex !== undefined)
+        shared.testCases[shared.savedSituationIndex] = situation
+      delete shared.savedSituationIndex
+    }
   }
 
   function requestAxesCalculation() {
+    shared.savedSituation = situation
+    situation = structuredClone($state.snapshot(situation))
+    shared.testCases[situationIndex] = situation
     if (
       situation.sliders?.length === undefined ||
       situation.sliders?.length <= 0
diff --git a/src/lib/components/piece_of_cake/DragSelect.svelte b/src/lib/components/piece_of_cake/DragSelect.svelte
index 61bc573c367a36f03d457ce5dd2839c35dad6f72..577c633364487c06a3b64d8b13125dfb2cd7ba08 100644
--- a/src/lib/components/piece_of_cake/DragSelect.svelte
+++ b/src/lib/components/piece_of_cake/DragSelect.svelte
@@ -1,16 +1,14 @@
 <script lang="ts">
-  import { createEventDispatcher } from "svelte"
-
   import type { CurveModel } from "$lib/components/piece_of_cake/model"
+  import type { GraphDomain } from "$lib/components/piece_of_cake/types"
 
   interface Props {
-    modelGroups: CurveModel[][]
     children?: import("svelte").Snippet<[any]>
+    modelGroups: CurveModel[][]
+    onZoom?: (domain: GraphDomain) => void
   }
 
-  let { modelGroups, children }: Props = $props()
-
-  const dispatch = createEventDispatcher()
+  let { children, modelGroups, onZoom }: Props = $props()
 
   let clientWidth: number = $state()
   let clientHeight: number = $state()
@@ -60,20 +58,20 @@
       Math.round((start[0] / clientWidth) * xRange) + xScale.domain()[0]
     const xEnd =
       Math.round((end[0] / clientWidth) * xRange) + xScale.domain()[0]
-    const x = !xInverted ? [xStart, xEnd] : [xEnd, xStart]
+    const x: AxisDomain = !xInverted ? [xStart, xEnd] : [xEnd, xStart]
 
     const yRange = yScale.domain()[1] - yScale.domain()[0]
     const yStart =
       Math.round((1 - start[1] / clientHeight) * yRange) + yScale.domain()[0]
     const yEnd =
       Math.round((1 - end[1] / clientHeight) * yRange) + yScale.domain()[0]
-    const y = !yInverted ? [yEnd, yStart] : [yStart, yEnd]
+    const y: AxisDomain = !yInverted ? [yEnd, yStart] : [yStart, yEnd]
 
-    const domain = {
+    const domain: GraphDomain = {
       x,
       y,
     }
-    dispatch("zoom", domain)
+    onZoom?.(domain)
     start = undefined
     end = undefined
   }
diff --git a/src/lib/components/piece_of_cake/types.ts b/src/lib/components/piece_of_cake/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4e31f09dc21b57b1f36939d6b20b23a6ca991919
--- /dev/null
+++ b/src/lib/components/piece_of_cake/types.ts
@@ -0,0 +1,6 @@
+export type AxisDomain = [number, number] // [min, max]
+
+export interface GraphDomain {
+  x: AxisDomain
+  y: AxisDomain
+}
diff --git a/src/lib/components/test_cases/TestCaseGraph.svelte b/src/lib/components/test_cases/TestCaseGraph.svelte
index 08f9f6c2b73c205e7898b9f83141d3524ad8eb64..16367f96a22eb639a5bc9ab6a9141a9bd9c8d815 100644
--- a/src/lib/components/test_cases/TestCaseGraph.svelte
+++ b/src/lib/components/test_cases/TestCaseGraph.svelte
@@ -30,6 +30,7 @@
   import PieceOfCake from "$lib/components/piece_of_cake/PieceOfCake.svelte"
   import SharedTooltip from "$lib/components/piece_of_cake/SharedTooltip.svelte"
   import Svg from "$lib/components/piece_of_cake/Svg.svelte"
+  import type { GraphDomain } from "$lib/components/piece_of_cake/types"
   import TestCaseGraphXlsxExport from "$lib/components/test_cases/TestCaseGraphXlsxExport.svelte"
   import Tooltip from "$lib/components/Tooltip.svelte"
   import ValueChangeGraph from "$lib/components/ValueChangeGraph.svelte"
@@ -97,6 +98,11 @@
     year,
   }: Props = $props()
 
+  let domain: GraphDomain = $state({
+    x: [situation.slider?.min ?? 0, situation.slider?.max ?? 100000],
+    y: [situation.slider?.yMin ?? 0, situation.slider?.yMax ?? 100000],
+  })
+
   const familyEntity = entityByKey[familyEntityKey]
   const formatCurrency = valueFormatter(0, "currency-EUR", false)
   const formatLongOrdinalSup = (n: number) => {
@@ -113,6 +119,7 @@
     ["other", "ème"],
     ["one", "er"],
   ])
+  let maxVariableValue: number | undefined = $state()
   const ordinalPluralRules = new Intl.PluralRules("fr-FR", { type: "ordinal" })
   const personEntity = entityByKey[personEntityKey]
   const shortOrdinalSuffixes = new Map([
@@ -122,7 +129,6 @@
   let svgPadding = { bottom: 20, left: 8, right: 20, top: 20 }
 
   let grapheExplanationOpen = $state(false)
-  let maxVariableValue: number | undefined = $state()
   let variableCustomizations = $state(variableCustomizationsBase)
   let variableValues: VariableGraph[][] = $state([])
   let variableGroups: {
@@ -133,451 +139,82 @@
     variables: VariableGraph[]
   }[] = $state()
 
-  function calculateDeciles(decilesNiveauDeVie, niveauDeVie) {
-    if (niveauDeVie === undefined || niveauDeVie?.length <= 1) {
-      return []
-    }
+  let visibleDecompositionsGraph: VisibleDecompositionForGraph[] | undefined =
+    $state(undefined)
 
-    const niveauDeVieStart = niveauDeVie[0]
-    const niveauDeVieEnd = niveauDeVie[niveauDeVie.length - 1]
+  $effect(() => {
+    visibleDecompositionsGraph =
+      situation.slider !== undefined
+        ? buildVisibleDecompositionsForGraph(
+            shared.decompositionByName,
+            entityByKey,
+            evaluationByName,
+            situation,
+            variableSummaryByName,
+            waterfall,
+            false,
+            useRevaluationInsteadOfLaw,
+            vectorLength,
+            year,
+          )
+        : undefined
+  })
 
-    let sumPreviousWidthPercentages = 0
+  $effect(() => {
+    if (
+      situation.slider !== undefined &&
+      evaluationByNameArray !== undefined &&
+      visibleDecompositionsGraph !== undefined
+    ) {
+      untrack(() => {
+        const ressourcesBrutes = [
+          "remuneration_brute",
+          "chomage_brut",
+          "retraite_brute",
+        ]
 
-    return [
-      ...decilesNiveauDeVie,
-      { rate: { value: 10 }, threshold: { value: niveauDeVieEnd } },
-    ].reduce(
-      (
-        result: [number, number, number, number, boolean, boolean][],
-        current: RateBracket,
-        index,
-        array: RateBracketAtInstant[],
-      ) => {
-        const rate = current.rate.value as number
-        const start = index > 0 ? array[index - 1].threshold.value + 1 : 0
-        const end = parseInt(`${current.threshold.value}`)
-        if (end - niveauDeVieStart >= 0 && start <= niveauDeVieEnd) {
-          const nextDecileIndex = niveauDeVie.findIndex((val) => val > end)
-          const widthPercentage =
-            (nextDecileIndex === -1 ? 101 : nextDecileIndex) -
-            1 -
-            sumPreviousWidthPercentages
-          const cutStart =
-            start < niveauDeVieStart && (domain.x.min > 0 || index > 0)
-          const cutEnd = niveauDeVieEnd < end || rate === 10
-          result.push([rate, end, widthPercentage, cutStart, cutEnd])
-          sumPreviousWidthPercentages += widthPercentage
-        }
-        return result
-      },
-      [],
-    )
-  }
+        if (waterfall.name === "brut_to_disponible") {
+          const decompositions = visibleDecompositionsGraph.filter(
+            (decomposition) =>
+              !decomposition.trunk ||
+              ressourcesBrutes.includes(decomposition.decomposition.name) ||
+              decomposition.decomposition.name === "revenu_disponible",
+          )
 
-  function generateValues(
-    decompositions: VisibleDecompositionForGraph[],
-    reverse: boolean = false,
-  ): VariableGraph[] {
-    if (reverse) {
-      decompositions.reverse()
-    }
-    const values = decompositions.map((decomposition) => {
-      const open = decomposition.decomposition.open ?? false
-      const trunk = decomposition.trunk
-      return {
-        depth: decomposition.depth,
-        label:
-          decomposition.decomposition.short_label ??
-          decomposition.decomposition.label,
-        name: decomposition.decomposition.name,
-        open,
-        parent: decomposition.parent,
-        rows: decomposition.rows.map((row) => ({
-          ...row,
-          delta: (row.delta ?? []).map((val) => (isNaN(val) ? 0 : val)),
-          ignore:
-            (open && !trunk) ||
-            variableCustomizations[
-              getVariableCustomizationName(
-                decomposition.decomposition.name,
-                row.calculationName,
-              )
-            ]?.selected === "false",
-        })),
-        trunk,
-      }
-    })
-    const sums: VariableGraph[] = []
-    for (let i = 0; i < values.length; i++) {
-      const actualArr = sums.filter(({ rows }) =>
-        rows.some((row) => !row.ignore),
-      )
-      const decomposition = values[i]
+          const versementBrutIndex = decompositions.findIndex((decomposition) =>
+            ressourcesBrutes.includes(decomposition.decomposition.name),
+          )
+          const prestationsIndex = decompositions.findIndex(
+            (decomposition) =>
+              decomposition.decomposition.name === "prestations_sociales",
+          )
+          const revenuIndex = decompositions.findIndex(
+            (decomposition) =>
+              decomposition.decomposition.name === "revenu_disponible",
+          )
 
-      // If we're not doing the sums in reverse, it all works normally.
+          const prelevementsDecompositions = [
+            ...decompositions.slice(0, versementBrutIndex),
+            ...decompositions
+              .slice(versementBrutIndex, prestationsIndex)
+              .filter(
+                (decomposition) =>
+                  ressourcesBrutes.includes(decomposition.decomposition.name) ||
+                  getLatestVisibleCalculation(decomposition.rows)?.isNegative,
+              ),
+          ]
 
-      // If we're calculating in reverse, this means that the negative
-      // values won't be substracted from the previous ones, but from the
-      // upcoming ones. This means we have to iterate twice to calculate them.
+          const complementsDeRessourcesDecompositions = decompositions
+            .slice(versementBrutIndex, prestationsIndex)
+            .filter(
+              (decomposition) =>
+                !ressourcesBrutes.includes(decomposition.decomposition.name) &&
+                !getLatestVisibleCalculation(decomposition.rows)?.isNegative,
+            )
 
-      if (!reverse) {
-        sums.push({
-          ...decomposition,
-          rows: decomposition.rows.map((row) => ({
-            ...row,
-            summedValues:
-              actualArr.length > 0 && !decomposition.trunk
-                ? getLatestVisibleCalculation(
-                    actualArr[actualArr.length - 1].rows,
-                  )!.summedValues.map(
-                    (value, index) => value + row.delta[index],
-                  )
-                : row.delta,
-          })),
-        })
-      } else {
-        // In reverse:
-        if (getLatestVisibleCalculation(decomposition.rows)?.isNegative) {
-          // If the variable is negative, just push it as is for now,
-          // we'll iterate over it again later.
-          sums.push({
-            ...decomposition,
-            rows: decomposition.rows.map((row) => ({
-              ...row,
-              summedValues: row.delta,
-            })),
-          })
-        } else {
-          // If the variable is positive, look for negative values that substract from it.
-          // 1) Add the latest positive value to it:
-          const latestPositive = actualArr.findLast(
-            (d) => !getLatestVisibleCalculation(d.rows)?.isNegative,
-          )
-          sums.push({
-            ...decomposition,
-            rows: decomposition.rows.map((row) => ({
-              ...row,
-              summedValues:
-                latestPositive !== undefined && !decomposition.trunk
-                  ? getLatestVisibleCalculation(
-                      latestPositive.rows,
-                    )!.summedValues.map(
-                      (value, index) => value + row.delta[index],
-                    )
-                  : row.delta,
-            })),
-          })
-          // 2) If the previous variable is negative, add it (and others if there are many):
-          if (
-            sums.length > 1 &&
-            getLatestVisibleCalculation(sums[i - 1].rows)?.isNegative
-          ) {
-            let currentSummedValues = getLatestVisibleCalculation(sums[i].rows)
-            let lastPositiveIndex = sums
-              .slice(0, i)
-              .findLastIndex(
-                (d) => !getLatestVisibleCalculation(d.rows)?.isNegative,
-              )
-            if (lastPositiveIndex === -1) {
-              lastPositiveIndex = 0
-            }
-            for (let j = i - 1; j > lastPositiveIndex; j--) {
-              sums[j].rows = sums[j].rows.map((row) => ({
-                ...row,
-                summedValues:
-                  latestPositive !== undefined &&
-                  !decomposition.trunk &&
-                  currentSummedValues !== undefined
-                    ? currentSummedValues.summedValues.map(
-                        (value, index) => value + row.delta[index],
-                      )
-                    : row.delta,
-              }))
-              currentSummedValues = getLatestVisibleCalculation(sums[j].rows)
-            }
-          }
-        }
-      }
-    }
-    if (reverse) {
-      return sums.reverse()
-    } else {
-      return sums
-    }
-  }
-
-  function getLatestCalculation<T extends { calculationName: CalculationName }>(
-    list: T[],
-  ): T {
-    for (const calculationName of [
-      "amendment",
-      "bill",
-      "revaluation",
-      "law",
-    ] as CalculationName[]) {
-      const calculation = list?.find(
-        (item) => item.calculationName === calculationName,
-      )
-      if (calculation !== undefined) {
-        return calculation
-      }
-    }
-  }
-
-  function getLatestVisibleCalculation<
-    T extends {
-      calculationName: CalculationName
-      ignore?: boolean
-    },
-  >(list: T[]): T | undefined {
-    for (const calculationName of [
-      "amendment",
-      "bill",
-      "revaluation",
-      "law",
-    ] as CalculationName[]) {
-      const calculation = list?.find(
-        (item) =>
-          item.calculationName === calculationName && (!item.ignore ?? true),
-      )
-      if (calculation !== undefined) {
-        return calculation
-      }
-    }
-  }
-
-  function getVariableCustomizationName(name: string, calculationName: string) {
-    return Object.hasOwn(variableCustomizations, `${name}_${calculationName}`)
-      ? `${name}_${calculationName}`
-      : name
-  }
-
-  function getVariableValue(
-    situation: Situation,
-    variableName: string,
-    populationId: string,
-  ): VariableValue | undefined {
-    const variable = variableSummaryByName[variableName]
-    if (variable === undefined) {
-      return undefined
-    }
-    return getSituationVariableValue(situation, variable, populationId, year)
-  }
-
-  function requestAxesCalculation() {
-    if (
-      situation.sliders?.length === undefined ||
-      situation.sliders?.length <= 0
-    ) {
-      console.error(
-        "requestAxesCalculation",
-        "Situation sliders list is undefined",
-      )
-      return
-    }
-
-    // Get situation
-    const variable = variableSummaryByName[situation.sliders[0].name]
-    const slider = situation.sliders.find(
-      (slider) =>
-        slider.entity === variable.entity && slider.name === variable.name,
-    ) as ActiveSlider | undefined
-
-    if (slider === undefined) {
-      console.error("requestAxesCalculation", "Slider is undefined")
-      return
-    }
-
-    const updatedMin = domain.x.min ?? slider.min
-    const updatedMax = domain.x.max ?? slider.max
-    const updatedStepValue = (updatedMax - updatedMin) / 100 ?? slider.stepValue
-
-    const value = getSituationVariableValue(
-      situation,
-      variable,
-      slider.id,
-      year,
-    ) as number
-
-    const vectorIndex = Math.max(
-      0,
-      Math.min(100, Math.round(value / updatedStepValue)),
-    )
-
-    // Update situation
-    setSituationVariableValue(
-      entityByKey,
-      situation,
-      variable,
-      slider.id,
-      year,
-      Math.round(updatedStepValue * vectorIndex),
-    )
-    situation.slider = {
-      ...slider,
-      min: updatedMin,
-      max: updatedMax,
-      stepValue: updatedStepValue,
-      vectorIndex: vectorIndex,
-    }
-    if (domain.y?.min !== undefined) {
-      situation.slider.yMin = domain.y?.min
-    }
-    if (domain.y?.max !== undefined) {
-      situation.slider.yMax = domain.y?.max
-    }
-  }
-
-  function stackValues(...groups: VariableGraph[][]) {
-    const newGroups: VariableGraph[][] = []
-    for (const group of groups.toReversed()) {
-      if (newGroups.length === 0) {
-        newGroups.push(group)
-      } else {
-        if (
-          !group.some(
-            ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
-          )
-        ) {
-          newGroups.push(group)
-        } else {
-          const previousGroup = newGroups.findLast((group) =>
-            group.some(
-              ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
-            ),
-          )
-          if (previousGroup === undefined) {
-            return groups
-          }
-          const previousGroupTopVariable = previousGroup.find(
-            ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
-          )
-          if (previousGroupTopVariable === undefined) {
-            return groups
-          }
-          const previousGroupTopVariableLatestVisibleCalculation =
-            getLatestVisibleCalculation(
-              previousGroupTopVariable.rows,
-            ) as VariableGraphRow
-          newGroups.push(
-            group.map((value) => ({
-              ...value,
-              rows: value.rows.map((row) => ({
-                ...row,
-                summedValues: row.summedValues.map(
-                  (value, index) =>
-                    value +
-                    previousGroupTopVariableLatestVisibleCalculation
-                      .summedValues[index],
-                ),
-              })),
-            })),
-          )
-        }
-      }
-    }
-    return newGroups.toReversed()
-  }
-
-  function updateDomain({ target }: Event, axis: "x" | "y", minMax: string) {
-    let { value }: { value: VariableValue } = target as
-      | HTMLInputElement
-      | HTMLSelectElement
-    const numberValue = value.replace(/\D/g, "")
-    if (numberValue !== "") {
-      domain[axis] = {
-        ...domain?.[axis],
-        [minMax]: Number(numberValue),
-      }
-      requestAxesCalculation()
-    }
-  }
-
-  function updateAllDomain(newDomain) {
-    domain.x = {
-      min: newDomain.x[0],
-      max: newDomain.x[1],
-    }
-    domain.y = {
-      min: newDomain.y[0],
-      max: newDomain.y[1],
-    }
-    requestAxesCalculation()
-  }
-  let visibleDecompositionsGraph: VisibleDecompositionForGraph[] | undefined =
-    $state(undefined)
-  $effect(() => {
-    visibleDecompositionsGraph =
-      situation.slider !== undefined
-        ? buildVisibleDecompositionsForGraph(
-            shared.decompositionByName,
-            entityByKey,
-            evaluationByName,
-            situation,
-            variableSummaryByName,
-            waterfall,
-            false,
-            useRevaluationInsteadOfLaw,
-            vectorLength,
-            year,
-          )
-        : undefined
-  })
-  $effect(() => {
-    if (
-      situation.slider !== undefined &&
-      evaluationByNameArray !== undefined &&
-      visibleDecompositionsGraph !== undefined
-    ) {
-      untrack(() => {
-        const ressourcesBrutes = [
-          "remuneration_brute",
-          "chomage_brut",
-          "retraite_brute",
-        ]
-
-        if (waterfall.name === "brut_to_disponible") {
-          const decompositions = visibleDecompositionsGraph.filter(
-            (decomposition) =>
-              !decomposition.trunk ||
-              ressourcesBrutes.includes(decomposition.decomposition.name) ||
-              decomposition.decomposition.name === "revenu_disponible",
-          )
-
-          const versementBrutIndex = decompositions.findIndex((decomposition) =>
-            ressourcesBrutes.includes(decomposition.decomposition.name),
-          )
-          const prestationsIndex = decompositions.findIndex(
-            (decomposition) =>
-              decomposition.decomposition.name === "prestations_sociales",
-          )
-          const revenuIndex = decompositions.findIndex(
-            (decomposition) =>
-              decomposition.decomposition.name === "revenu_disponible",
-          )
-
-          const prelevementsDecompositions = [
-            ...decompositions.slice(0, versementBrutIndex),
-            ...decompositions
-              .slice(versementBrutIndex, prestationsIndex)
-              .filter(
-                (decomposition) =>
-                  ressourcesBrutes.includes(decomposition.decomposition.name) ||
-                  getLatestVisibleCalculation(decomposition.rows)?.isNegative,
-              ),
-          ]
-
-          const complementsDeRessourcesDecompositions = decompositions
-            .slice(versementBrutIndex, prestationsIndex)
-            .filter(
-              (decomposition) =>
-                !ressourcesBrutes.includes(decomposition.decomposition.name) &&
-                !getLatestVisibleCalculation(decomposition.rows)?.isNegative,
-            )
-
-          const prestationsDecompositions = decompositions.slice(
-            prestationsIndex,
-            revenuIndex,
+          const prestationsDecompositions = decompositions.slice(
+            prestationsIndex,
+            revenuIndex,
           )
 
           const revenuDisponibleDecomposition = decompositions[revenuIndex]
@@ -782,17 +419,7 @@
       })
     }
   })
-  let domain = $state({
-    x: {
-      min: situation.slider?.min,
-      max: situation.slider?.max,
-    },
-    y: {
-      min: situation.slider?.yMin ?? 0,
-      max: situation.slider?.yMax ?? (() => maxVariableValue)() ?? 100000,
-    },
-  })
-  updateAllDomain((() => domain)())
+
   // Note: A reform parameters tree is always more complete than a parameters tree before reform.
   // And the children of a reform node parameter always contain the children of the node parameter
   // before reform (albeit with some different value parameters).
@@ -892,6 +519,367 @@
   )
   let smicLatestInstantValueCouple = $derived(smicInstantValueCouplesArray[0])
   let smicValue = $derived(smicLatestInstantValueCouple?.[1] as NumberValue)
+
+  function calculateDeciles(decilesNiveauDeVie, niveauDeVie) {
+    if (niveauDeVie === undefined || niveauDeVie?.length <= 1) {
+      return []
+    }
+
+    const niveauDeVieStart = niveauDeVie[0]
+    const niveauDeVieEnd = niveauDeVie[niveauDeVie.length - 1]
+
+    let sumPreviousWidthPercentages = 0
+
+    return [
+      ...decilesNiveauDeVie,
+      { rate: { value: 10 }, threshold: { value: niveauDeVieEnd } },
+    ].reduce(
+      (
+        result: [number, number, number, number, boolean, boolean][],
+        current: RateBracket,
+        index,
+        array: RateBracketAtInstant[],
+      ) => {
+        const rate = current.rate.value as number
+        const start = index > 0 ? array[index - 1].threshold.value + 1 : 0
+        const end = parseInt(`${current.threshold.value}`)
+        if (end - niveauDeVieStart >= 0 && start <= niveauDeVieEnd) {
+          const nextDecileIndex = niveauDeVie.findIndex((val) => val > end)
+          const widthPercentage =
+            (nextDecileIndex === -1 ? 101 : nextDecileIndex) -
+            1 -
+            sumPreviousWidthPercentages
+          const cutStart =
+            start < niveauDeVieStart && (domain.x[0] > 0 || index > 0)
+          const cutEnd = niveauDeVieEnd < end || rate === 10
+          result.push([rate, end, widthPercentage, cutStart, cutEnd])
+          sumPreviousWidthPercentages += widthPercentage
+        }
+        return result
+      },
+      [],
+    )
+  }
+
+  function generateValues(
+    decompositions: VisibleDecompositionForGraph[],
+    reverse: boolean = false,
+  ): VariableGraph[] {
+    if (reverse) {
+      decompositions.reverse()
+    }
+    const values = decompositions.map((decomposition) => {
+      const open = decomposition.decomposition.open ?? false
+      const trunk = decomposition.trunk
+      return {
+        depth: decomposition.depth,
+        label:
+          decomposition.decomposition.short_label ??
+          decomposition.decomposition.label,
+        name: decomposition.decomposition.name,
+        open,
+        parent: decomposition.parent,
+        rows: decomposition.rows.map((row) => ({
+          ...row,
+          delta: (row.delta ?? []).map((val) => (isNaN(val) ? 0 : val)),
+          ignore:
+            (open && !trunk) ||
+            variableCustomizations[
+              getVariableCustomizationName(
+                decomposition.decomposition.name,
+                row.calculationName,
+              )
+            ]?.selected === "false",
+        })),
+        trunk,
+      }
+    })
+    const sums: VariableGraph[] = []
+    for (let i = 0; i < values.length; i++) {
+      const actualArr = sums.filter(({ rows }) =>
+        rows.some((row) => !row.ignore),
+      )
+      const decomposition = values[i]
+
+      // If we're not doing the sums in reverse, it all works normally.
+
+      // If we're calculating in reverse, this means that the negative
+      // values won't be substracted from the previous ones, but from the
+      // upcoming ones. This means we have to iterate twice to calculate them.
+
+      if (!reverse) {
+        sums.push({
+          ...decomposition,
+          rows: decomposition.rows.map((row) => ({
+            ...row,
+            summedValues:
+              actualArr.length > 0 && !decomposition.trunk
+                ? getLatestVisibleCalculation(
+                    actualArr[actualArr.length - 1].rows,
+                  )!.summedValues.map(
+                    (value, index) => value + row.delta[index],
+                  )
+                : row.delta,
+          })),
+        })
+      } else {
+        // In reverse:
+        if (getLatestVisibleCalculation(decomposition.rows)?.isNegative) {
+          // If the variable is negative, just push it as is for now,
+          // we'll iterate over it again later.
+          sums.push({
+            ...decomposition,
+            rows: decomposition.rows.map((row) => ({
+              ...row,
+              summedValues: row.delta,
+            })),
+          })
+        } else {
+          // If the variable is positive, look for negative values that substract from it.
+          // 1) Add the latest positive value to it:
+          const latestPositive = actualArr.findLast(
+            (d) => !getLatestVisibleCalculation(d.rows)?.isNegative,
+          )
+          sums.push({
+            ...decomposition,
+            rows: decomposition.rows.map((row) => ({
+              ...row,
+              summedValues:
+                latestPositive !== undefined && !decomposition.trunk
+                  ? getLatestVisibleCalculation(
+                      latestPositive.rows,
+                    )!.summedValues.map(
+                      (value, index) => value + row.delta[index],
+                    )
+                  : row.delta,
+            })),
+          })
+          // 2) If the previous variable is negative, add it (and others if there are many):
+          if (
+            sums.length > 1 &&
+            getLatestVisibleCalculation(sums[i - 1].rows)?.isNegative
+          ) {
+            let currentSummedValues = getLatestVisibleCalculation(sums[i].rows)
+            let lastPositiveIndex = sums
+              .slice(0, i)
+              .findLastIndex(
+                (d) => !getLatestVisibleCalculation(d.rows)?.isNegative,
+              )
+            if (lastPositiveIndex === -1) {
+              lastPositiveIndex = 0
+            }
+            for (let j = i - 1; j > lastPositiveIndex; j--) {
+              sums[j].rows = sums[j].rows.map((row) => ({
+                ...row,
+                summedValues:
+                  latestPositive !== undefined &&
+                  !decomposition.trunk &&
+                  currentSummedValues !== undefined
+                    ? currentSummedValues.summedValues.map(
+                        (value, index) => value + row.delta[index],
+                      )
+                    : row.delta,
+              }))
+              currentSummedValues = getLatestVisibleCalculation(sums[j].rows)
+            }
+          }
+        }
+      }
+    }
+    if (reverse) {
+      return sums.reverse()
+    } else {
+      return sums
+    }
+  }
+
+  function getLatestCalculation<T extends { calculationName: CalculationName }>(
+    list: T[],
+  ): T {
+    for (const calculationName of [
+      "amendment",
+      "bill",
+      "revaluation",
+      "law",
+    ] as CalculationName[]) {
+      const calculation = list?.find(
+        (item) => item.calculationName === calculationName,
+      )
+      if (calculation !== undefined) {
+        return calculation
+      }
+    }
+  }
+
+  function getLatestVisibleCalculation<
+    T extends {
+      calculationName: CalculationName
+      ignore?: boolean
+    },
+  >(list: T[]): T | undefined {
+    for (const calculationName of [
+      "amendment",
+      "bill",
+      "revaluation",
+      "law",
+    ] as CalculationName[]) {
+      const calculation = list?.find(
+        (item) =>
+          item.calculationName === calculationName && (!item.ignore ?? true),
+      )
+      if (calculation !== undefined) {
+        return calculation
+      }
+    }
+  }
+
+  function getVariableCustomizationName(name: string, calculationName: string) {
+    return Object.hasOwn(variableCustomizations, `${name}_${calculationName}`)
+      ? `${name}_${calculationName}`
+      : name
+  }
+
+  function getVariableValue(
+    situation: Situation,
+    variableName: string,
+    populationId: string,
+  ): VariableValue | undefined {
+    const variable = variableSummaryByName[variableName]
+    if (variable === undefined) {
+      return undefined
+    }
+    return getSituationVariableValue(situation, variable, populationId, year)
+  }
+
+  function requestAxesCalculation() {
+    if (
+      situation.sliders?.length === undefined ||
+      situation.sliders?.length <= 0
+    ) {
+      console.error(
+        "requestAxesCalculation",
+        "Situation sliders list is undefined",
+      )
+      return
+    }
+
+    // Get situation
+    const variable = variableSummaryByName[situation.sliders[0].name]
+    const slider = situation.sliders.find(
+      (slider) =>
+        slider.entity === variable.entity && slider.name === variable.name,
+    ) as ActiveSlider | undefined
+
+    if (slider === undefined) {
+      console.error("requestAxesCalculation", "Slider is undefined")
+      return
+    }
+
+    const updatedMin = domain.x[0] ?? slider.min
+    const updatedMax = domain.x[1] ?? slider.max
+    const updatedStepValue = (updatedMax - updatedMin) / 100
+
+    const value = getSituationVariableValue(
+      situation,
+      variable,
+      slider.id,
+      year,
+    ) as number
+
+    const vectorIndex = Math.max(
+      0,
+      Math.min(100, Math.round(value / updatedStepValue)),
+    )
+
+    // Update situation
+    setSituationVariableValue(
+      entityByKey,
+      situation,
+      variable,
+      slider.id,
+      year,
+      Math.round(updatedStepValue * vectorIndex),
+    )
+    situation.slider = {
+      ...slider,
+      min: updatedMin,
+      max: updatedMax,
+      stepValue: updatedStepValue,
+      vectorIndex: vectorIndex,
+    }
+    if (domain.y[0] !== undefined) {
+      situation.slider.yMin = domain.y[0]
+    }
+    if (domain.y[1] !== undefined) {
+      situation.slider.yMax = domain.y[1]
+    }
+  }
+
+  function stackValues(...groups: VariableGraph[][]) {
+    const newGroups: VariableGraph[][] = []
+    for (const group of groups.toReversed()) {
+      if (newGroups.length === 0) {
+        newGroups.push(group)
+      } else {
+        if (
+          !group.some(
+            ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
+          )
+        ) {
+          newGroups.push(group)
+        } else {
+          const previousGroup = newGroups.findLast((group) =>
+            group.some(
+              ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
+            ),
+          )
+          if (previousGroup === undefined) {
+            return groups
+          }
+          const previousGroupTopVariable = previousGroup.find(
+            ({ rows }) => getLatestVisibleCalculation(rows) !== undefined,
+          )
+          if (previousGroupTopVariable === undefined) {
+            return groups
+          }
+          const previousGroupTopVariableLatestVisibleCalculation =
+            getLatestVisibleCalculation(
+              previousGroupTopVariable.rows,
+            ) as VariableGraphRow
+          newGroups.push(
+            group.map((value) => ({
+              ...value,
+              rows: value.rows.map((row) => ({
+                ...row,
+                summedValues: row.summedValues.map(
+                  (value, index) =>
+                    value +
+                    previousGroupTopVariableLatestVisibleCalculation
+                      .summedValues[index],
+                ),
+              })),
+            })),
+          )
+        }
+      }
+    }
+    return newGroups.toReversed()
+  }
+
+  function updateDomainAxis(
+    { target }: Event,
+    axis: "x" | "y",
+    minMax: string,
+  ) {
+    let { value }: { value: VariableValue } = target as
+      | HTMLInputElement
+      | HTMLSelectElement
+    const numberValue = value.replace(/\D/g, "")
+    if (numberValue !== "") {
+      domain[axis][minMax === "min" ? 0 : 1] = Number(numberValue)
+      requestAxesCalculation()
+    }
+  }
 </script>
 
 {#if situation.slider !== undefined && visibleDecompositionsGraph !== undefined}
@@ -903,7 +891,7 @@
             <div class="flex flex-col gap-2">
               <div class="h-4 w-24 bg-neutral-300"></div>
               <div class="flex gap-4">
-                {#each Array(row) as _}
+                {#each Array(row)}
                   <div
                     class="flex h-9 items-center overflow-hidden rounded-full border bg-white text-neutral-600"
                   >
@@ -929,7 +917,7 @@
                 <div class="h-9 w-32 bg-neutral-300"></div>
               </div>
               <div class="flex gap-2">
-                {#each Array(10) as _}
+                {#each Array(10)}
                   <div class="h-1 w-full bg-neutral-300"></div>
                 {/each}
               </div>
@@ -1142,10 +1130,10 @@
               class="w-24 rounded-t border-b-2 border-black bg-neutral-200 px-3 outline-none md:w-28"
               min="0"
               max={maxVariableValue}
-              onchange={(event) => updateDomain(event, "y", "max")}
+              onchange={(event) => updateDomainAxis(event, "y", "max")}
               step="100"
               type="text"
-              value={domain.y?.max ?? maxVariableValue}
+              value={domain.y[1] ?? maxVariableValue}
             />
             <span
               class="pointer-events-none absolute right-1 bg-neutral-200 px-2"
@@ -1159,10 +1147,10 @@
               class="w-28 rounded-t border-b-2 border-black bg-neutral-200 px-3 outline-none"
               min="0"
               max={maxVariableValue}
-              onchange={(event) => updateDomain(event, "y", "min")}
+              onchange={(event) => updateDomainAxis(event, "y", "min")}
               step="100"
               type="text"
-              value={situation.slider.yMin ?? domain.y?.min ?? 0}
+              value={situation.slider.yMin ?? domain.y[0] ?? 0}
             />
             <span
               class="pointer-events-none absolute right-1 bg-neutral-200 px-2"
@@ -1205,8 +1193,8 @@
                           trunk,
                           xDomain: [situation.slider.min, situation.slider.max],
                           yDomain: [
-                            domain?.y?.min ?? 0,
-                            domain?.y?.max ?? maxVariableValue,
+                            domain?.y[0] ?? 0,
+                            domain?.y[1] ?? maxVariableValue,
                           ],
                         },
                       ),
@@ -1240,8 +1228,10 @@
                   {#snippet children({ modelGroups })}
                     <DragSelect
                       {modelGroups}
-                      on:zoom={({ detail }) => {
-                        updateAllDomain(detail)
+                      onZoom={(newDomain: GraphDomain) => {
+                        domain.x = [newDomain.x[0], newDomain.x[1]]
+                        domain.y = [newDomain.y[0], newDomain.y[1]]
+                        requestAxesCalculation()
                       }}
                     >
                       {#snippet children({ modelGroups })}
@@ -1494,7 +1484,7 @@
                 class="w-24 rounded-t border-b-2 border-black bg-neutral-200 px-3 outline-none md:w-28"
                 min="0"
                 max="100000"
-                onchange={(event) => updateDomain(event, "x", "min")}
+                onchange={(event) => updateDomainAxis(event, "x", "min")}
                 step="100"
                 type="text"
                 value={situation.slider.min}
@@ -1546,7 +1536,7 @@
                 class="w-24 rounded-t border-b-2 border-black bg-neutral-200 px-3 outline-none md:w-28"
                 min="0"
                 max="100000"
-                onchange={(event) => updateDomain(event, "x", "max")}
+                onchange={(event) => updateDomainAxis(event, "x", "max")}
                 step="100"
                 type="text"
                 value={situation.slider.max}
diff --git a/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte b/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte
index b9bc42bb3c103300b5a070296c5522452e8245ea..f541e5a079a2c7a02a39237dabf3203ec84084cc 100644
--- a/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte
+++ b/src/lib/components/test_cases/TestCaseGraphXlsxExport.svelte
@@ -12,6 +12,7 @@
   import XLSX from "xlsx-js-style"
 
   import { page } from "$app/stores"
+  import type { GraphDomain } from "$lib/components/piece_of_cake/types"
   import { decompositionCoreByName } from "$lib/decompositions"
   import type { DisplayMode } from "$lib/displays"
   import { entityByKey } from "$lib/entities"
@@ -43,10 +44,7 @@
 
   interface Props {
     displayMode: DisplayMode
-    domain: {
-      x: { min?: number; max?: number }
-      y: { min: number; max: number }
-    }
+    domain: GraphDomain
     situation: Situation
     situationIndex: number
     variableSummaryByName: VariableByName
@@ -157,7 +155,7 @@
         [
           undefined,
           "Fourchette de l'axe des X :",
-          `${domain.x.min}-${domain.x.max}`,
+          `${domain.x[0]}-${domain.x[1]}`,
         ],
         [undefined, "Date d'export :", dateFormatter(new Date())],
         [
diff --git a/src/lib/shared.svelte.ts b/src/lib/shared.svelte.ts
index 43d007bedb0c69ee5d9812d205d9fa8326d44890..153b30f7584f91606d39ffc45641b0d01e008b22 100644
--- a/src/lib/shared.svelte.ts
+++ b/src/lib/shared.svelte.ts
@@ -41,6 +41,8 @@ export interface Shared {
   requestedSimulationEmail?: string // Budget simulation requests
   requestedSimulationSent: boolean // Budget simulation requests
   requestedVariablesNameToCalculate?: Set<string> // Budget simulation requests
+  savedSituation?: Situation
+  savedSituationIndex?: number
   searchActive: boolean // Search
   searchVariableName?: string // Search
   showNulls: boolean