{
  "$comment": "Machine-readable companion to https://valem.run/calculators/turkey/holiday-entitlement. Formula, inputs, outputs, engine-verified test vectors, cited sources and the re-runnable Valem ModelSpec behind the page.",
  "url": "https://valem.run/calculators/turkey/holiday-entitlement",
  "dataUrl": "https://valem.run/calculators/turkey/holiday-entitlement.json",
  "license": {
    "name": "CC BY 4.0",
    "url": "https://creativecommons.org/licenses/by/4.0/",
    "attribution": "Türkiye yıllık ücretli i̇zin model by Valem (https://valem.run/calculators/turkey/holiday-entitlement), CC BY 4.0.",
    "note": "Covers the model — the expression of each rule and the arrangement of the catalog. The rates and thresholds themselves are law and are not licensed by anyone; the cited sources are where they come from."
  },
  "verificationUrl": "https://valem.run/calculators/turkey/holiday-entitlement/verify",
  "country": "turkey",
  "countryName": "Türkiye",
  "formula": "holiday-entitlement",
  "formulaName": "Yıllık Ücretli İzin",
  "title": "Yıllık Ücretli İzin Hesaplama 2026 — 14, 20 Veya 26 Gün",
  "answer": "Article 53 of the İş Kanunu gives annual paid leave only to workers who have completed at least one year of service, probation included — so the statutory entitlement in the first year is zero. After that it is at least 14 days for one to five years of service, at least 20 for more than five and under fifteen, and at least 26 from fifteen years. Workers aged 18 and under, and 50 and over, get at least 20 days whatever their service.",
  "lang": "en",
  "currency": "TRY",
  "taxYear": "2026",
  "verification": {
    "claim": "internal-consistency",
    "statement": "The Valem engine reproduces this model's stated formula through 16 self-test cases (below, and re-runnable against the spec). That asserts internal consistency, NOT that the formula matches current law — the cited sources are the evidence for that, and carry the date each was last checked.",
    "selfTestCases": 16,
    "ratesAreIllustrative": false,
    "sourcesCheckedOn": "2026-10-11"
  },
  "sources": [
    {
      "label": "İş Kanunu (4857) madde 53 — mevzuat.gov.tr (the one-year qualifying period; bands of 14, 20 and 26 days; the 20-day floor for workers 18 and under and 50 and over; four extra days for underground work)",
      "url": "https://www.mevzuat.gov.tr/MevzuatMetin/1.5.4857.pdf",
      "checked": "2026-10-11"
    }
  ],
  "terminalOutput": "daysRemaining",
  "headlineOutput": "daysEntitled",
  "inputs": [
    {
      "name": "serviceYears",
      "type": "number",
      "label": "Years of service with this employer",
      "minimum": 0,
      "maximum": 60
    },
    {
      "name": "age",
      "type": "number",
      "label": "Your age",
      "minimum": 15,
      "maximum": 100
    },
    {
      "name": "worksUnderground",
      "type": "boolean",
      "label": "You work in underground work (yer altı işleri)"
    },
    {
      "name": "daysTaken",
      "type": "number",
      "label": "Days already taken",
      "minimum": 0
    }
  ],
  "outputs": [
    {
      "name": "bandDays",
      "type": "number",
      "formula": "($s := $n(serviceYears); $s < $const.qualifyingYears ? 0 : ($s <= $const.bandYears5 ? $const.bandDaysTo5Years : ($s < $const.bandYears15 ? $const.bandDaysTo15Years : $const.bandDaysFrom15Years)))"
    },
    {
      "name": "daysEntitled",
      "type": "number",
      "formula": "($s := $n(serviceYears); $s < $const.qualifyingYears ? 0 : ($base := bandDays + (worksUnderground = true ? $const.undergroundExtraDays : 0); $floored := ($n(age) <= $const.youngAgeThreshold or $n(age) >= $const.olderAgeThreshold) ? ($base > $const.ageFloorDays ? $base : $const.ageFloorDays) : $base; $floored))"
    },
    {
      "name": "daysRemaining",
      "type": "number",
      "formula": "($r := daysEntitled - $n(daysTaken); $r < 0 ? 0 : $round($r, 1))"
    }
  ],
  "testCases": [
    {
      "description": "N1 under a year there is no statutory entitlement at all",
      "given": {
        "serviceYears": 0,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "bandDays": 0,
        "daysEntitled": 0
      }
    },
    {
      "description": "N2 at one year the first band opens at 14",
      "given": {
        "serviceYears": 1,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "bandDays": 14,
        "daysEntitled": 14
      }
    },
    {
      "description": "N3 five years is still band a, because the article includes five",
      "given": {
        "serviceYears": 5,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 14
      }
    },
    {
      "description": "N4 just over five years moves to 20",
      "given": {
        "serviceYears": 6,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N5 fourteen years is still band b, because band c starts AT fifteen",
      "given": {
        "serviceYears": 14,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N6 fifteen years reaches 26, and that is the top band",
      "given": {
        "serviceYears": 15,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 26
      }
    },
    {
      "description": "N7 a 17-year-old with two years' service gets the 20-day floor, not 14",
      "given": {
        "serviceYears": 2,
        "age": 17,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "bandDays": 14,
        "daysEntitled": 20
      }
    },
    {
      "description": "N8 so does a 55-year-old with two years' service — the floor is two-sided",
      "given": {
        "serviceYears": 2,
        "age": 55,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N9 the young threshold is inclusive at 18, so 18 still floors to 20",
      "given": {
        "serviceYears": 2,
        "age": 18,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N10 nineteen is above the young threshold and below 50, so back to the band",
      "given": {
        "serviceYears": 2,
        "age": 19,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 14
      }
    },
    {
      "description": "N11 the older threshold is inclusive at 50",
      "given": {
        "serviceYears": 2,
        "age": 50,
        "worksUnderground": false,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N12 underground work adds four days to a band",
      "given": {
        "serviceYears": 2,
        "age": 35,
        "worksUnderground": true,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 18
      }
    },
    {
      "description": "N13 the floor applies to the total, so an underground 55-year-old on band a sees 20",
      "given": {
        "serviceYears": 2,
        "age": 55,
        "worksUnderground": true,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 20
      }
    },
    {
      "description": "N14 underground work earns nothing before the qualifying year either",
      "given": {
        "serviceYears": 0,
        "age": 55,
        "worksUnderground": true,
        "daysTaken": 0
      },
      "expect": {
        "daysEntitled": 0
      }
    },
    {
      "description": "N15 days already taken come off the remainder",
      "given": {
        "serviceYears": 6,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 8
      },
      "expect": {
        "daysRemaining": 12
      }
    },
    {
      "description": "N16 taking more than the entitlement never goes negative",
      "given": {
        "serviceYears": 2,
        "age": 35,
        "worksUnderground": false,
        "daysTaken": 40
      },
      "expect": {
        "daysRemaining": 0
      }
    }
  ],
  "spec": {
    "id": "holiday-entitlement-turkey",
    "version": "1.0.0",
    "schema": {
      "type": "object",
      "properties": {
        "serviceYears": {
          "type": "number",
          "maximum": 60,
          "minimum": 0
        },
        "age": {
          "type": "number",
          "maximum": 100,
          "minimum": 15
        },
        "worksUnderground": {
          "type": "boolean"
        },
        "daysTaken": {
          "type": "number",
          "minimum": 0
        },
        "bandDays": {
          "type": "number",
          "readOnly": true
        },
        "daysEntitled": {
          "type": "number",
          "readOnly": true
        },
        "daysRemaining": {
          "type": "number",
          "readOnly": true
        }
      }
    },
    "constants": {
      "qualifyingYears": 1,
      "bandDaysTo5Years": 14,
      "bandDaysTo15Years": 20,
      "bandDaysFrom15Years": 26,
      "bandYears5": 5,
      "bandYears15": 15,
      "ageFloorDays": 20,
      "youngAgeThreshold": 18,
      "olderAgeThreshold": 50,
      "undergroundExtraDays": 4
    },
    "library": {
      "layers": [
        {
          "id": "statutory",
          "define": "(\n  /* Shared statutory helpers — the canonical copy (docs/sandbox/d8-statutory-families.md §5.4).\n     DO NOT EDIT A SPEC'S INLINED COPY. Edit this file and run `npm run sync:helpers`; a vitest\n     assertion and a Java assertion both require every inlined copy to be byte-identical to this one.\n\n     Why inlined rather than referenced: the `library` section's `ref` form — a library shared\n     between models — is still proposed, gated on the model-references design (see\n     ../../../../docs/design/library/jsonata-library.md). Inlining keeps each spec self-contained,\n     which is what lets `#e=` embedding, `#share=` import and MCP forking work with no resolver.\n\n     TWO TRAPS, both of which fail SILENTLY and both of which bit during the debt-payoff build:\n\n       1. A library function sees ONLY its arguments. It cannot read the consuming document — not\n          through a bare field name, not through `$`, not through `$$`. Everything these helpers\n          need is passed in. (`$const` is late-bound and so would resolve, but relying on it would\n          couple a shared helper to one spec's constant names.)\n       2. ONE null member wrecks a whole aggregate, and the two engines disagree about how. The JS\n          reference implementation THROWS (\"Argument 1 of function sum must be an array of\n          numbers\"); the Java port can instead yield null for the entire aggregate. Neither gives a\n          partial total, and the null-yielding form is the dangerous one because it is silent. So\n          callers coerce with `$n()` below before aggregating, which makes the behaviour identical\n          in both engines — measured, not assumed: see helpers.test.ts.\n\n       3. A PATH STEP DIRECTLY ON A CALL RESULT RETURNS NOTHING in the Java engine. This matters for\n          every caller of $amortize, which is the one helper here returning an object:\n\n              $amortize(p, r, n, 0).months            -> empty in Java, 360 in JS.  WRONG\n              ($a := $amortize(p, r, n, 0); $a.months) -> 360 in both.               RIGHT\n\n          Bind the result to a variable, then step into it. The JS reference accepts both forms, so\n          this is invisible until the page is served by the real engine, and it fails as a blank\n          field rather than an error. Measured with a probe, not inferred — the field simply does\n          not appear. */\n\n  /* Number coercion with an explicit zero default. The guard against trap 2. */\n  $n := function($v) { $v = null or $not($exists($v)) ? 0 : $number($v) };\n\n  /* ISO date -> its year / month / day parts. String slicing rather than $fromMillis formatting:\n     these are statutory dates (\"2026-04-06\"), and a timezone-sensitive round-trip through millis\n     can shift the day, which silently moves a service anniversary across a band boundary. */\n  $isoY := function($d) { $number($substring($d, 0, 4)) };\n  $isoM := function($d) { $number($substring($d, 5, 2)) };\n  $isoD := function($d) { $number($substring($d, 8, 2)) };\n\n  /* Whole years of completed service between two ISO dates. Counts an anniversary as reached only\n     when the month-and-day has arrived, which is what \"completed years of service\" means in every\n     severance statute — not a 365.25-day division, whose drift crosses a 5-year band boundary in\n     the wrong direction roughly one year in four. */\n  $yearsBetween := function($from, $to) { (\n    $y := $isoY($to) - $isoY($from);\n    $beforeAnniversary := $isoM($to) < $isoM($from)\n      or ($isoM($to) = $isoM($from) and $isoD($to) < $isoD($from));\n    $beforeAnniversary ? $y - 1 : $y\n  ) };\n\n  /* Whole completed months between two ISO dates, same convention. */\n  $monthsBetween := function($from, $to) { (\n    $m := ($isoY($to) - $isoY($from)) * 12 + ($isoM($to) - $isoM($from));\n    $isoD($to) < $isoD($from) ? $m - 1 : $m\n  ) };\n\n  /* Inclusive count of days between two ISO dates — both endpoints counted, because a leave year\n     runs from its first day to its last and an employee present on both is present for both.\n     Pro-rata accrual is then $proRataDays(a, b) / $proRataDays(yearStart, yearEnd). */\n  $proRataDays := function($from, $to) {\n    ($toMillis($to) - $toMillis($from)) / 86400000 + 1\n  };\n\n  /* Progressive charge across bands. `$bands` is ordered lowest-first, each {\"upTo\": n, \"rate\": r};\n     the final band's `upTo` is omitted or null to mean \"no ceiling\". Each band's rate applies ONLY\n     to the slice of the amount inside it — the single most common error in a hand-written bracket\n     walk is applying the top rate to the whole amount. Returns 0 for a non-positive amount. */\n  $bandedCharge := function($amount, $bands) { (\n    $a := $n($amount);\n    $a <= 0 ? 0 : $reduce($bands, function($acc, $band) { (\n      $ceiling := $exists($band.upTo) and $band.upTo != null ? $band.upTo : $a;\n      $top := $ceiling < $a ? $ceiling : $a;\n      $slice := $top - $acc.floor;\n      {\n        \"floor\": $top > $acc.floor ? $top : $acc.floor,\n        \"charge\": $acc.charge + ($slice > 0 ? $slice * $n($band.rate) : 0)\n      }\n    ) }, {\"floor\": 0, \"charge\": 0}).charge\n  ) };\n\n  /* Clamp into [floor, cap]. Either bound may be omitted (null) to leave that side open — a benefit\n     with a cap and no floor is as common as one with both. */\n  $clampFloorCap := function($value, $floor, $cap) { (\n    $v := $n($value);\n    $withFloor := $exists($floor) and $floor != null and $v < $floor ? $floor : $v;\n    $exists($cap) and $cap != null and $withFloor > $cap ? $cap : $withFloor\n  ) };\n\n  /* Level annuity payment for a principal over `$months` at `$annualRatePct` nominal, compounded\n     monthly. Falls back to straight-line division at a zero rate, where the standard formula\n     divides by zero. */\n  $annuityPayment := function($principal, $annualRatePct, $months) { (\n    $p := $n($principal);\n    $m := $n($months);\n    $i := $n($annualRatePct) / 1200;\n    $m <= 0 ? 0 : ($i = 0 ? $p / $m : $p * $i * $power(1 + $i, $m) / ($power(1 + $i, $m) - 1))\n  ) };\n\n  /* Amortisation schedule: one row per month until the balance clears or `$months` is reached.\n     Returns {\"payment\", \"months\", \"totalInterest\", \"schedule\": [{month, interest, principal,\n     balance}]}. `$extra` is an optional monthly overpayment.\n\n     A real month-by-month fold rather than a closed form, because an overpayment changes every\n     later row — the same reason debt-payoff simulates. Bounded by `$months`, so it always\n     terminates. */\n  $amortize := function($principal, $annualRatePct, $months, $extra) { (\n    $i := $n($annualRatePct) / 1200;\n    $pay := $annuityPayment($principal, $annualRatePct, $months) + $n($extra);\n    $r := $reduce([1..$n($months)], function($acc, $month) {\n      $acc.balance <= 0 ? $acc : (\n        /* The running balance is kept UNROUNDED and only the emitted row is rounded. Rounding the\n           balance each month instead accumulates drift: at 200,000 over 360 months it left 2.83\n           still owing in the final row, which would break the \"final balance is zero\" invariant\n           (§7.3) and look to a reader like the schedule does not pay the loan off. Lenders compute\n           on the exact balance and adjust the last payment, which is what this does. */\n        $interest := $acc.balance * $i;\n        $due := $acc.balance + $interest < $pay ? $acc.balance + $interest : $pay;\n        $principalPart := $due - $interest;\n        $balance := $acc.balance - $principalPart;\n        {\n          \"balance\": $balance,\n          \"totalInterest\": $acc.totalInterest + $interest,\n          \"schedule\": $append($acc.schedule, [{\n            \"month\": $month,\n            \"interest\": $round($interest, 2),\n            \"principal\": $round($principalPart, 2),\n            \"balance\": $round($balance, 2)\n          }])\n        }\n      )\n    }, {\"balance\": $n($principal), \"totalInterest\": 0, \"schedule\": []});\n    {\n      \"payment\": $round($pay, 2),\n      \"months\": $count($r.schedule),\n      \"totalInterest\": $round($r.totalInterest, 2),\n      \"schedule\": [$r.schedule]\n    }\n  ) };\n\n  [\"n\", \"isoY\", \"isoM\", \"isoD\", \"yearsBetween\", \"monthsBetween\", \"proRataDays\",\n   \"bandedCharge\", \"clampFloorCap\", \"annuityPayment\", \"amortize\"]\n)\n"
        }
      ]
    },
    "defaultValues": [
      {
        "path": "$",
        "expr": "{ 'serviceYears': 3, 'age': 35, 'worksUnderground': false, 'daysTaken': 0 }"
      }
    ],
    "derivations": [
      {
        "path": "$.bandDays",
        "expr": "($s := $n(serviceYears); $s < $const.qualifyingYears ? 0 : ($s <= $const.bandYears5 ? $const.bandDaysTo5Years : ($s < $const.bandYears15 ? $const.bandDaysTo15Years : $const.bandDaysFrom15Years)))"
      },
      {
        "path": "$.daysEntitled",
        "expr": "($s := $n(serviceYears); $s < $const.qualifyingYears ? 0 : ($base := bandDays + (worksUnderground = true ? $const.undergroundExtraDays : 0); $floored := ($n(age) <= $const.youngAgeThreshold or $n(age) >= $const.olderAgeThreshold) ? ($base > $const.ageFloorDays ? $base : $const.ageFloorDays) : $base; $floored))"
      },
      {
        "path": "$.daysRemaining",
        "expr": "($r := daysEntitled - $n(daysTaken); $r < 0 ? 0 : $round($r, 1))"
      }
    ],
    "constraints": [],
    "viewDefinition": {
      "renderer": "builtin",
      "defaultView": "main",
      "views": [
        {
          "id": "main",
          "label": "Yıllık ücretli izin",
          "layout": "vertical",
          "components": [
            {
              "id": "note",
              "type": "staticText",
              "text": "Madde 53: “İşyerinde işe başladığı gününden itibaren, deneme süresi de içinde olmak üzere, en az bir yıl çalışmış olan işçilere yıllık ücretli izin verilir.” Read that first clause carefully: until a full year has been worked — probation included — the statutory entitlement is **nothing at all**. Türkiye is the only country in this comparison where that is true; everywhere else a part year earns a fraction."
            },
            {
              "id": "inputs",
              "type": "fieldSet",
              "label": "Bilgileriniz",
              "components": [
                {
                  "id": "serviceYears",
                  "type": "numericField",
                  "label": "Years of service with this employer",
                  "bind": "$.serviceYears",
                  "helperText": "Madde 54 combines time at any workplace of the same employer — and pushes the anniversary forward by any interruption outside madde 55's list."
                },
                {
                  "id": "age",
                  "type": "numericField",
                  "label": "Your age",
                  "bind": "$.age",
                  "helperText": "Madde 53 puts a floor of 20 days under workers 18 and UNDER and workers 50 and OVER."
                },
                {
                  "id": "worksUnderground",
                  "type": "checkboxField",
                  "label": "You work in underground work (yer altı işleri)",
                  "bind": "$.worksUnderground",
                  "helperText": "Adds four days to each band."
                },
                {
                  "id": "daysTaken",
                  "type": "numericField",
                  "label": "Days already taken",
                  "bind": "$.daysTaken"
                }
              ]
            },
            {
              "id": "sep",
              "type": "separatorLine"
            },
            {
              "id": "entitledTile",
              "type": "statTile",
              "label": "Days entitled",
              "value": "$.daysEntitled"
            },
            {
              "id": "remainingTile",
              "type": "statTile",
              "label": "Days remaining",
              "value": "$.daysRemaining"
            },
            {
              "id": "breakdown",
              "type": "keyValueList",
              "label": "How that is worked out",
              "items": [
                {
                  "label": "Service band before any floor or uplift",
                  "value": "$.bandDays"
                }
              ]
            },
            {
              "id": "assumptions",
              "type": "staticText",
              "text": "These days are already net. Madde 56: “Yıllık ücretli izin günlerinin hesabında izin süresine rastlayan ulusal bayram, hafta tatili ve genel tatil günleri izin süresinden sayılmaz.” National holidays, the weekly rest day and general holidays falling inside the leave are not counted against it — so 14 days is fourteen days of actual leave, not fourteen consecutive calendar days, and no working-week conversion is needed. One reading this page has to choose: madde 53 says the leave to be GIVEN cannot be less than 20 days, so the age floor is applied after the underground uplift, which means an underground worker over 50 on the lowest band sees 20 rather than 24. The article’s sentence order does not settle it. Madde 53 also excludes seasonal and campaign work lasting under a year from these provisions entirely, and madde 56 forbids the employer to divide the leave. Madde 55 lists absences counted as worked time toward the qualifying year, which this page does not model. Contracts and collective agreements may raise every figure here."
            },
            {
              "id": "sources",
              "type": "sourceList",
              "label": "Kaynaklar",
              "layers": 1,
              "items": [
                {
                  "label": "İş Kanunu (4857) madde 53 — mevzuat.gov.tr (the one-year qualifying period; bands of 14, 20 and 26 days; the 20-day floor for workers 18 and under and 50 and over; four extra days for underground work)",
                  "url": "https://www.mevzuat.gov.tr/MevzuatMetin/1.5.4857.pdf",
                  "date": "2026-10-11",
                  "covers": [
                    "qualifyingYears",
                    "bandDaysTo5Years",
                    "bandDaysTo15Years",
                    "bandDaysFrom15Years",
                    "bandYears5",
                    "bandYears15",
                    "ageFloorDays",
                    "youngAgeThreshold",
                    "olderAgeThreshold",
                    "undergroundExtraDays"
                  ]
                }
              ]
            }
          ]
        }
      ]
    },
    "tests": [
      {
        "description": "N1 under a year there is no statutory entitlement at all",
        "given": {
          "$.serviceYears": 0,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.bandDays": 0,
          "$.daysEntitled": 0
        }
      },
      {
        "description": "N2 at one year the first band opens at 14",
        "given": {
          "$.serviceYears": 1,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.bandDays": 14,
          "$.daysEntitled": 14
        }
      },
      {
        "description": "N3 five years is still band a, because the article includes five",
        "given": {
          "$.serviceYears": 5,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 14
        }
      },
      {
        "description": "N4 just over five years moves to 20",
        "given": {
          "$.serviceYears": 6,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N5 fourteen years is still band b, because band c starts AT fifteen",
        "given": {
          "$.serviceYears": 14,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N6 fifteen years reaches 26, and that is the top band",
        "given": {
          "$.serviceYears": 15,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 26
        }
      },
      {
        "description": "N7 a 17-year-old with two years' service gets the 20-day floor, not 14",
        "given": {
          "$.serviceYears": 2,
          "$.age": 17,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.bandDays": 14,
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N8 so does a 55-year-old with two years' service — the floor is two-sided",
        "given": {
          "$.serviceYears": 2,
          "$.age": 55,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N9 the young threshold is inclusive at 18, so 18 still floors to 20",
        "given": {
          "$.serviceYears": 2,
          "$.age": 18,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N10 nineteen is above the young threshold and below 50, so back to the band",
        "given": {
          "$.serviceYears": 2,
          "$.age": 19,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 14
        }
      },
      {
        "description": "N11 the older threshold is inclusive at 50",
        "given": {
          "$.serviceYears": 2,
          "$.age": 50,
          "$.worksUnderground": false,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N12 underground work adds four days to a band",
        "given": {
          "$.serviceYears": 2,
          "$.age": 35,
          "$.worksUnderground": true,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 18
        }
      },
      {
        "description": "N13 the floor applies to the total, so an underground 55-year-old on band a sees 20",
        "given": {
          "$.serviceYears": 2,
          "$.age": 55,
          "$.worksUnderground": true,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 20
        }
      },
      {
        "description": "N14 underground work earns nothing before the qualifying year either",
        "given": {
          "$.serviceYears": 0,
          "$.age": 55,
          "$.worksUnderground": true,
          "$.daysTaken": 0
        },
        "expect": {
          "$.daysEntitled": 0
        }
      },
      {
        "description": "N15 days already taken come off the remainder",
        "given": {
          "$.serviceYears": 6,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 8
        },
        "expect": {
          "$.daysRemaining": 12
        }
      },
      {
        "description": "N16 taking more than the entitlement never goes negative",
        "given": {
          "$.serviceYears": 2,
          "$.age": 35,
          "$.worksUnderground": false,
          "$.daysTaken": 40
        },
        "expect": {
          "$.daysRemaining": 0
        }
      }
    ]
  }
}
