{
  "schemaVersion": 1,
  "slug": "curtain-calculator",
  "category": "construction",
  "kind": "implementation",
  "independentValidation": false,
  "implementationHash": "f6363dcc53920fe43c21a509ea39f898c0108785d7551417cfe615b42f2e814a",
  "provenance": "Generated from the calculation contract used by the published page. Publication is not external validation.",
  "sourceFiles": [
    "data/calculators/curtain-calculator.json",
    "src/engine.js"
  ],
  "runtimeSource": "/sources/engine.txt",
  "runtimeHash": "5c888d8898afbdee36267bb9977829e1e2ac568f6677c8ee87469c139864645c",
  "scope": "The compute function records implemented equations, constants, lookup tables and custom rules. Inputs record defaults and allowed values; outputs record precision and units. Defaults are examples, not recommendations. General background references are not evidence for this specific algorithm.",
  "publishedFormula": {
    "formula": "Y = p × ceil(L × f ÷ (p × b)) × (H + h) ÷ 36",
    "vars": [
      [
        "L",
        "Rod length = window width + 2 × overlap"
      ],
      [
        "f",
        "Fullness factor"
      ],
      [
        "p",
        "Number of panels"
      ],
      [
        "b",
        "Fabric width (in)"
      ],
      [
        "H",
        "Window height (in)"
      ],
      [
        "h",
        "Hem allowance (in)"
      ]
    ],
    "note": "All units in inches; widths per panel are rounded up; yards = total inches ÷ 36."
  },
  "externalEvidence": null,
  "contract": {
    "compute": "const vals=[windowWidth,windowHeight,fullness,panels,overlap,hemAllowance,fabricWidth].map(Number);if(vals.some(v=>!Number.isFinite(v))||windowWidth<=0||windowHeight<=0||![1.5,2,2.5,3].includes(Number(fullness))||!Number.isInteger(Number(panels))||panels<1||panels>12||overlap<0||hemAllowance<0||fabricWidth<=0)return{rodLength:null,flatWidth:null,widthPerPanel:null,cutLength:null,linearYards:null,widthsPerPanel:null,__error:'inputError'};const flat=(windowWidth+overlap*2)*fullness,perPanel=flat/panels,cut=windowHeight+hemAllowance,widths=Math.ceil(perPanel/fabricWidth),yards=panels*widths*cut/36;return{rodLength:windowWidth+overlap*2,flatWidth:flat,widthPerPanel:perPanel,cutLength:cut,linearYards:yards,widthsPerPanel:widths};",
    "inputs": [
      {
        "name": "windowWidth",
        "type": "number",
        "default": 72,
        "min": 0,
        "max": 400,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "windowHeight",
        "type": "number",
        "default": 84,
        "min": 0,
        "max": 400,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "fullness",
        "type": "select",
        "default": 2,
        "options": [
          {
            "value": 1.5
          },
          {
            "value": 2
          },
          {
            "value": 2.5
          },
          {
            "value": 3
          }
        ]
      },
      {
        "name": "panels",
        "type": "number",
        "default": 2,
        "min": 0,
        "max": 12,
        "step": 1,
        "slider": true
      },
      {
        "name": "overlap",
        "type": "number",
        "default": 4,
        "min": 0,
        "max": 30,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "hemAllowance",
        "type": "number",
        "default": 10,
        "min": 0,
        "max": 30,
        "step": 0.5,
        "slider": true
      },
      {
        "name": "fabricWidth",
        "type": "number",
        "default": 54,
        "min": 0,
        "max": 120,
        "step": 1,
        "slider": true
      }
    ],
    "outputs": [
      {
        "name": "rodLength",
        "precision": 1,
        "unit": "in"
      },
      {
        "name": "flatWidth",
        "precision": 1,
        "unit": "in"
      },
      {
        "name": "widthPerPanel",
        "precision": 1,
        "unit": "in"
      },
      {
        "name": "cutLength",
        "precision": 1,
        "unit": "in"
      },
      {
        "name": "linearYards",
        "precision": 2,
        "unit": "yards"
      },
      {
        "name": "widthsPerPanel",
        "precision": 0,
        "unit": ""
      }
    ],
    "interpret": null
  }
}
