{
  "openapi": "3.1.1",
  "info": {
    "title": "Premium apps API",
    "version": "1.0.0",
    "summary": "Deterministic tools for people and AI agents.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://apps.usefulweb.app"
    }
  ],
  "paths": {
    "/api/v1/tools": {
      "get": {
        "operationId": "listTools",
        "summary": "Catalog of all tools",
        "responses": {
          "200": {
            "description": "Tool list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/{id}": {
      "get": {
        "operationId": "getTool",
        "summary": "Definition of one tool",
        "parameters": [
          {
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "flipline-exposure-sheet",
                "flipline-motion-study",
                "lumina-image-splitter",
                "lumina-image-mask",
                "kerfwise-cabinet-builder",
                "kerfwise-cnc-gcode",
                "kerfwise-cut-list",
                "kerfwise-sheet-nesting",
                "kinetica-collision-check",
                "kinetica-forward-kinematics",
                "kinetica-inverse-kinematics",
                "kinetica-trajectory-planner",
                "lumina-blend-mixer",
                "lumina-layer-transform",
                "ohmforge-ac-sweep",
                "ohmforge-dc-operating-point",
                "ohmforge-led-resistor",
                "ohmforge-transient",
                "lumina-photo-layout",
                "lumina-resize-planner",
                "lumina-histogram-analyzer",
                "lumina-color-analyzer",
                "lumina-print-planner",
                "lumina-gif-maker",
                "lumina-tone-processor",
                "flipline-pixel-quantizer",
                "flipline-pixel-transform",
                "flipline-palette-converter",
                "flipline-retro-batch",
                "flipline-sprite-sheet",
                "threadsmith-design-stats",
                "threadsmith-digitize",
                "threadsmith-stitch-file",
                "twinworks-bottleneck-analysis",
                "twinworks-buffer-sweep",
                "twinworks-buffer-allocation",
                "twinworks-simulate",
                "twinworks-line-balance",
                "twinworks-queue-formulas"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "404": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-exposure-sheet/calculate": {
      "post": {
        "operationId": "calculateFliplineExposureSheet",
        "summary": "Turn drawing holds into a precise animation timing sheet.",
        "description": "Enter how many timeline ticks each drawing lasts and a frame rate. Calculate drawing start times, total duration and GIF timing rounded to 10 milliseconds.\n\nMethodology:\n- A drawing held for N ticks lasts N × 1000 / FPS milliseconds. Start and end tick positions are half-open intervals.\n- GIF stores centiseconds. Each drawing duration rounds independently to the nearest 10 ms; the reported GIF total may differ from ideal playback.\n- 1–24 drawings, holds of 1–10 ticks and 1–30 FPS. No uploaded files are needed.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_exposure_sheet",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineExposureSheetInput"
              },
              "examples": {
                "example1": {
                  "summary": "A six-drawing motion study on twos",
                  "value": {
                    "holds": "2,2,1,1,2,2",
                    "fps": 12
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineExposureSheetResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-motion-study/calculate": {
      "post": {
        "operationId": "calculateFliplineMotionStudy",
        "summary": "Generate an editable flipbook to compare animation easing curves.",
        "description": "Create a ball moving between two canvas positions with linear, ease-in, ease-out or smoothstep spacing. Download the project and open it in Flipline to draw over it.\n\nMethodology:\n- Progress samples include both endpoints: t = index / (drawing count − 1).\n- Easing: linear t, ease-in t², ease-out 1 − (1 − t)², smoothstep t²(3 − 2t). Canvas positions are percentages.\n- This generates a deterministic circle-motion project, not automatic inbetweening of arbitrary artwork. Each drawing is editable.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_motion_study",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineMotionStudyInput"
              },
              "examples": {
                "example1": {
                  "summary": "Twelve drawings with slow-in, slow-out spacing",
                  "value": {
                    "count": 12,
                    "fps": 12,
                    "ease": "ease-in-out",
                    "fromX": 15,
                    "fromY": 50,
                    "toX": 85,
                    "toY": 50
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineMotionStudyResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-image-splitter/calculate": {
      "post": {
        "operationId": "calculateLuminaImageSplitter",
        "summary": "Slice a photo into numbered tiles without losing edge pixels.",
        "description": "Split one image into a row-and-column grid and download every tile as a PNG in one ZIP. Uneven dimensions are distributed across the tiles so the complete photo can be reconstructed.\n\nMethodology:\n- The source is auto-oriented. Integer boundaries use floor(index × size / count), ensuring no gaps, overlaps or missing edge pixels.\n- At most 64 nonempty tiles; original decoded photo limited to 16 MP. ZIP names encode row and column.\n- Outputs are PNGs with original EXIF/GPS removed. The ZIP includes a JSON manifest with the exact tile coordinates.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_image_splitter",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaImageSplitterInput"
              },
              "examples": {
                "example1": {
                  "summary": "Three-by-three tiles",
                  "value": {
                    "images": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "rows": 3,
                    "columns": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaImageSplitterResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-image-mask/calculate": {
      "post": {
        "operationId": "calculateLuminaImageMask",
        "summary": "Clip a photo into a soft rectangle or ellipse with PNG transparency.",
        "description": "Upload a photo and apply a rectangular or elliptical alpha mask with adjustable bounds, inward feathering and inversion. Download a transparent PNG while preserving the original image dimensions.\n\nMethodology:\n- The source is auto-oriented and converted to sRGB. Its existing alpha is multiplied by mask coverage. Source dimensions are preserved; metadata is stripped.\n- Rectangle distance uses the nearest edge; ellipse distance uses normalized radial distance scaled by the smaller radius. Feather transitions inward with smoothstep; it approximates distance for noncircular ellipses.\n- Inputs are limited to 4 MP. GIF uses its first frame. This is a geometric mask, not automatic subject detection. The browser Design Layers mode uses the same mask engine.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_image_mask",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaImageMaskInput"
              },
              "examples": {
                "example1": {
                  "summary": "A circular transparent cutout",
                  "value": {
                    "image": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "shape": "ellipse",
                    "feather": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaImageMaskResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kerfwise-cabinet-builder/calculate": {
      "post": {
        "operationId": "calculateKerfwiseCabinetBuilder",
        "summary": "Parametric cabinet → every part with cut sizes, edge banding, drilling and hardware.",
        "description": "Give a cabinet type (base, wall, tall, drawer bank, blind corner, diagonal corner base or wall cabinet, L-shaped corner, angled end, quarter-round end shelf, open shelf, wardrobe, bookshelf, desk, table, bed frame) with its size, back panel, doors, drawers, shelves and joinery, and get the complete parts list with finished and cut sizes, grain direction, edge banding per edge, hinge and shelf-pin drilling, hardware and a front view. The same engine powers the Kerfwise app.\n\nMethodology:\n- Carcass: sides run full height, so side height = cabinet height − plinth height; top, bottom and fixed shelves sit between the sides (length = inner width = width − 2 × panel thickness), plus 2 × dado depth (T/3) for dado joints or 2 × rabbet depth (T/2) for rabbets.\n- Back panels: grooved = (inner width + 2 × groove depth) × (inner height + 2 × groove depth) and the shelves lose groove set-back + back thickness of depth; inset = inner width × inner height; nailed = outer size − 2 mm, and the carcass depth includes the back.\n- Doors: full overlay = width / n − gap by carcass height − gap (gap 3 mm = 1.5 mm reveal per edge); inset = (opening − (n + 1) × gap) / n. Hinges per door by height: ≤ 900 mm → 2, ≤ 1600 → 3, ≤ 2000 → 4, else 5, placed 100 mm from the ends; Ø35 × 13 mm cup 22.5 mm from the edge with two Ø8 dowel holes 45 mm apart, mounting plates on the side 37 mm from the front.\n- Drawers: slide length = largest standard length ≤ inner depth − 10 mm; box length = nominal slide length; box width = opening − 25.4 mm (side-mount, 12.7 mm per side) or opening − 42 mm + 2 × side thickness (undermount); box height = front height − 40 mm (side) / − 47 mm (undermount). Front/back length: butt or dowel = box width − 2t, dado = + t, dovetail = full box width.\n- Adjustable shelves: inner width − 2 mm, 20 mm set back, on Ø5 holes at 32 mm pitch in two rows (37 mm from the front, rear row on the 32 mm grid). Fasteners: 50 mm from each end, ≤ 150 mm apart (dowels, confirmat, screws, pocket screws), cams 2–3 per joint.\n- Cut size = finished size − edge-banding thickness on each banded edge (L1/L2 run along the length and reduce the width; W1/W2 reduce the length) when deduction is on.\n- Shaped cabinets (diagonal corner, L-shaped corner, angled end, quarter-round end): the plan footprint is a polygon. End panels run through at the corners, wall-side backs (carcass material) butt against them; panel ends that meet a front at an angle are bevelled (angle and long point in the notes). Bottom, top and shelves are the footprint inset by the panel thickness (+1 mm and −20 mm front set-back for adjustable shelves) and get a cut outline for CNC nesting. Diagonal doors use +45° angled hinges; the L-shaped corner uses a bi-fold connector or two doors with a corner post.\n\nDimensions follow common European frameless practice and the stated rules. Always check hardware manufacturers’ planning data for the exact hinges and slides you buy, and verify critical sizes before cutting expensive material.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_cabinet_builder",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseCabinetBuilderInput"
              },
              "examples": {
                "example1": {
                  "summary": "Kitchen base cabinet 600, grooved back, legs, 1 door",
                  "value": {
                    "type": "base",
                    "width": 600,
                    "height": 870,
                    "depth": 560,
                    "backType": "groove",
                    "grooveDepth": 8,
                    "plinthType": "legs",
                    "plinthHeight": 150,
                    "doorCount": 1,
                    "shelfCount": 1,
                    "carcassJoint": "dowel",
                    "topType": "rails"
                  }
                },
                "example2": {
                  "summary": "Drawer bank 800 with undermount slides and dovetails",
                  "value": {
                    "type": "drawer",
                    "width": 800,
                    "height": 870,
                    "depth": 560,
                    "drawerCount": 4,
                    "slideType": "under",
                    "drawerJoint": "dovetail",
                    "carcassJoint": "confirmat"
                  }
                },
                "example3": {
                  "summary": "Wardrobe 1000 × 2200, cam joints, 2 doors",
                  "value": {
                    "type": "wardrobe",
                    "width": 1000,
                    "height": 2200,
                    "depth": 600,
                    "carcassJoint": "cam",
                    "plinthType": "board",
                    "plinthHeight": 80
                  }
                },
                "example4": {
                  "summary": "Dining table 1600 × 900 with mortise & tenon aprons",
                  "value": {
                    "type": "table",
                    "width": 1600,
                    "depth": 900,
                    "height": 750,
                    "materialId": "oakpanel-27",
                    "frameJoint": "mortise-tenon"
                  }
                },
                "example5": {
                  "summary": "Diagonal corner base 900 × 900 with a 45° door",
                  "value": {
                    "type": "diagonal",
                    "width": 900,
                    "returnWidth": 900,
                    "height": 870,
                    "depth": 560,
                    "carcassJoint": "dowel"
                  }
                },
                "example6": {
                  "summary": "L-shaped corner base with bi-fold doors",
                  "value": {
                    "type": "l-corner",
                    "width": 1000,
                    "returnWidth": 900,
                    "cornerDoors": "bifold",
                    "handle": "profile"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseCabinetBuilderResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kerfwise-cnc-gcode/calculate": {
      "post": {
        "operationId": "calculateKerfwiseCncGcode",
        "summary": "Nest parts and get CNC router G-code: compensated profiles, tabs, ramps, drilling cycles, time.",
        "description": "Turns a part list into a CNC router program: parts (rectangles, L-shapes, corner cuts, quarter rounds) are nested with a gap of one cutter diameter, then every part gets an outside profile with the tool-radius offset already in the path (arcs around corners), multiple depth passes with a ramped entry, holding tabs or an onion skin, and the cut order small parts first. Holes become drilling cycles (G81, or G83 peck for deep holes) for LinuxCNC / Fanuc-style controls, or expanded moves for GRBL. You get the G-code, a toolpath preview, a DXF per sheet and the estimated machining time.\n\nMethodology:\n- Nesting: the kerfwise sheet nesting optimiser (MaxRects) with the kerf set to the cutter diameter, so the slots of neighbouring parts never overlap; the margin to the sheet edge must be ≥ the cutter radius.\n- Tool-radius compensation in the path: each outside profile is the part outline offset outward by the radius — straight edges shifted along their outward normal, an arc (G2/G3, centre = part corner) around every convex corner and a line intersection at inside corners of L-shapes. Inner contours are offset inward. The controller runs with G40 (no G41/G42).\n- Passes: n = ⌈(thickness + 0.3 mm overcut) / depth per pass⌉, equal steps. Entry ramps down along the first edge (40 mm), the ramp wedge is cleaned at the end of each pass. Tabs: 2–6 per part on the longest edges, raised to 2 mm above the spoilboard in the passes below that height. Onion skin: the last pass stops 0.3 mm above the bottom.\n- Climb milling: outside profiles run clockwise, inner contours counter-clockwise (conventional = reversed). Order: drilling (one tool at a time, nearest-neighbour tour), helical bores, inner contours, outside profiles from the smallest part to the largest.\n- Drilling: holes go to the drill whose diameter matches (±0.25 mm) — G98 G81 (G83 peck for holes deeper than 3 × Ø) on LinuxCNC/Fanuc, expanded G0/G1 plunges on GRBL. Holes without a matching drill are helically bored with the end mill. Rapids (G0) are only used at or above the clearance height (3 mm) once over the material.\n- Time = feed moves ÷ their feed rate + rapids ÷ 10 m/min + 22 s per tool change; acceleration is ignored, so real machines take longer (add 20–40 %).\n\nSimulate every program in your CAM or controller before running it. Check work zero, tool lengths, feeds and speeds for your machine and material. Kerfwise is not responsible for crashes, broken tools or scrapped material.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_cnc_gcode",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseCncGcodeInput"
              },
              "examples": {
                "example1": {
                  "summary": "Wardrobe side panels with shelf-pin rows (GRBL)",
                  "value": {
                    "parts": [
                      {
                        "name": "Side",
                        "length": 2000,
                        "width": 580,
                        "qty": 2
                      },
                      {
                        "name": "Shelf",
                        "length": 764,
                        "width": 560,
                        "qty": 3
                      }
                    ],
                    "holes": [
                      {
                        "part": 1,
                        "x": 300,
                        "y": 37,
                        "diameter": 5,
                        "depth": 12
                      },
                      {
                        "part": 1,
                        "x": 332,
                        "y": 37,
                        "diameter": 5,
                        "depth": 12
                      },
                      {
                        "part": 1,
                        "x": 300,
                        "y": 543,
                        "diameter": 5,
                        "depth": 12
                      },
                      {
                        "part": 1,
                        "x": 332,
                        "y": 543,
                        "diameter": 5,
                        "depth": 12
                      }
                    ],
                    "sheetLength": 2800,
                    "sheetWidth": 2070,
                    "thickness": 18,
                    "dialect": "grbl"
                  }
                },
                "example2": {
                  "summary": "Doors with Ø35 hinge cups (LinuxCNC, G81 cycles)",
                  "value": {
                    "parts": [
                      {
                        "name": "Door",
                        "length": 716,
                        "width": 446,
                        "qty": 4
                      }
                    ],
                    "holes": [
                      {
                        "part": 1,
                        "x": 100,
                        "y": 22.5,
                        "diameter": 35,
                        "depth": 13
                      },
                      {
                        "part": 1,
                        "x": 616,
                        "y": 22.5,
                        "diameter": 35,
                        "depth": 13
                      }
                    ],
                    "sheetLength": 2440,
                    "sheetWidth": 1220,
                    "thickness": 19,
                    "dialect": "linuxcnc"
                  }
                },
                "example3": {
                  "summary": "L-shaped desk top and corner shelves (Fanuc-style)",
                  "value": {
                    "parts": [
                      {
                        "name": "L desk top",
                        "length": 1600,
                        "width": 1200,
                        "shape": "l",
                        "a": 1000,
                        "b": 600
                      },
                      {
                        "name": "Corner shelf",
                        "length": 400,
                        "width": 400,
                        "qty": 3,
                        "shape": "quarter"
                      }
                    ],
                    "sheetLength": 2800,
                    "sheetWidth": 2070,
                    "thickness": 25,
                    "dialect": "fanuc",
                    "holdDown": "onion"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseCncGcodeResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kerfwise-cut-list/calculate": {
      "post": {
        "operationId": "calculateKerfwiseCutList",
        "summary": "Cabinets or a Kerfwise project → cut list, edge banding, hardware, sheets and costs.",
        "description": "Paste a Kerfwise project (.kerfwise.json) or list cabinets by type and size, and get the complete grouped cut list with edge banding per edge, banding metres, hardware list, optimised sheet count and yield per material, worktops, labour minutes and a full cost estimate with markup and tax. CSV export included.\n\nMethodology:\n- Every cabinet is generated with the Kerfwise cabinet engine (see kerfwise-cabinet-builder for the joinery rules); identical cut pieces (same material, size, grain and banding) are grouped into one row with all their labels.\n- Edge banding metres = sum of banded finished edge lengths × (1 + banding waste %, default 10 %), rounded up to 0.1 m.\n- Sheets: parts of each sheet material are nested with the kerfwise-sheet-nesting optimiser (kerf, trim margins, grain locked for grained materials, shaped parts as polygons); solid stock and worktops use first-fit-decreasing 1D optimisation.\n- Worktop corners: butt = the later run butts against the earlier one (2 connector bolts); mitre = both pieces cut on the line from the outer wall corner to the inner front corner (angle = atan(depth A / depth B), bolts every ~200 mm + 4 biscuits); one-piece = the two halves merged into one L/U polygon nested on the slab material (falls back to a mitre when it does not fit). A diagonal corner cabinet adds a triangular infill (butt/mitre) or a chamfer (one-piece) in the inner corner.\n- Cost = materials (full sheets or used area × (1 + waste %)) + banding + hardware + labour (cut minutes per part, banding minutes per metre, drilling minutes per hole, assembly minutes per cabinet) + worktop cut-outs, then markup and tax.\n\nPrices are editable defaults, not quotes. Labour times are rough workshop averages — adjust them to your shop. Check the cut list against your drawings before cutting.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_cut_list",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseCutListInput"
              },
              "examples": {
                "example1": {
                  "summary": "Three kitchen cabinets (base, drawer bank, wall)",
                  "value": {
                    "cabinets": [
                      {
                        "type": "base",
                        "width": 600,
                        "doors": 1,
                        "shelves": 1
                      },
                      {
                        "type": "drawer",
                        "width": 600,
                        "drawers": 3
                      },
                      {
                        "type": "wall",
                        "width": 800,
                        "doors": 2
                      }
                    ],
                    "kerf": 4,
                    "trim": 10,
                    "labourRate": 45,
                    "currency": "EUR"
                  }
                },
                "example2": {
                  "summary": "Bookshelf ×2 and a desk, 19 % VAT",
                  "value": {
                    "cabinets": [
                      {
                        "type": "bookshelf",
                        "width": 800,
                        "qty": 2
                      },
                      {
                        "type": "desk",
                        "width": 1400,
                        "drawers": 2
                      }
                    ],
                    "materialId": "oak-18",
                    "frontMaterialId": "oak-18",
                    "taxPct": 19,
                    "markupPct": 15
                  }
                },
                "example3": {
                  "summary": "Project JSON: wall cabinet in walnut",
                  "value": {
                    "project": "{\"format\":\"kerfwise\",\"version\":1,\"name\":\"Walnut wall cabinet\",\"cabinets\":[{\"type\":\"wall\",\"params\":{\"width\":900,\"doorCount\":2,\"frontMaterialId\":\"walnut-19\",\"frontBandingId\":\"walnut-veneer-05\"}}]}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseCutListResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kerfwise-sheet-nesting/calculate": {
      "post": {
        "operationId": "calculateKerfwiseSheetNesting",
        "summary": "Nest rectangular and shaped parts on sheets: kerf, grain, yield, lower bound, offcuts, SVG + DXF.",
        "description": "A cutting optimiser for panel saws and CNC routers: list parts (rectangles, L-shapes, corner cuts or quarter rounds) with quantity and grain, choose the sheet size (e.g. 2800 × 2070), saw kerf and trim margin, and get guillotine-cuttable or MaxRects layouts, sheet count, yield per sheet, the lower bound on the sheet count and the gap to it, reusable offcuts, the guillotine cut sequence, SVG drawings and a DXF R12 file. An optional deep search tries many seeded variations and stops as soon as the layout is proven optimal.\n\nMethodology:\n- Parts are inflated by one kerf and the usable sheet (sheet − 2 × trim) by one kerf, so neighbouring parts are exactly one kerf apart and the outer parts touch the trimmed edge.\n- Guillotine: free-rectangle packing with best-area-fit / best-short-side-fit and split rules (shorter-leftover-axis, longer-leftover-axis, min-area); every placement records its edge-to-edge cuts, giving a panel-saw cut sequence. MaxRects: maximal free rectangles with best-short-side, best-area or bottom-left fit (tighter, for CNC).\n- Each method runs a fixed set of strategies (4 sort orders × fit/split rules × rotation on/off) and keeps the best: fewest unplaced parts → fewest sheets → least sheet area → largest single offcut → guillotine preferred. Deterministic for identical input.\n- Grain: on a grained material, parts marked grain keep their length along the sheet length; other parts may rotate 90°. Yield = part area / full sheet area. Offcuts = non-overlapping free rectangles ≥ the minimum offcut size.\n- Shaped parts: nested by their bounding box in 0/90/180/270° (grained: 0/180°); the empty notches of the polygon inside the box go back into the MaxRects free list (inset by the kerf), so smaller parts fill them. Guillotine layouts cannot contain shaped parts — auto picks MaxRects then. Yield counts the true polygon area.\n- Lower bound = max(⌈Σ (part + kerf) area / usable area⌉, number of parts larger than half the usable sheet in both directions). Gap = sheets used − lower bound; a gap of 0 proves the sheet count optimal. Deep search: a mulberry32-seeded multi-start search (perturbed sort orders, random fit/split rules, rotation preferences) that keeps the best layout and stops early at the bound.\n\nHeuristic optimisation: the sheet count is proven optimal only when the gap to the lower bound is 0. Check that your saw can follow the cut sequence and measure your blade’s real kerf.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kerfwise_sheet_nesting",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KerfwiseSheetNestingInput"
              },
              "examples": {
                "example1": {
                  "summary": "16 panels 600 × 500 on 2800 × 2070 (exactly one sheet)",
                  "value": {
                    "parts": [
                      {
                        "name": "Panel",
                        "length": 600,
                        "width": 500,
                        "qty": 16
                      }
                    ],
                    "sheetLength": 2800,
                    "sheetWidth": 2070,
                    "kerf": 4,
                    "trim": 10,
                    "method": "guillotine",
                    "grainedMaterial": false
                  }
                },
                "example2": {
                  "summary": "Base cabinet carcass parts, oak veneer with grain",
                  "value": {
                    "parts": [
                      {
                        "name": "Side",
                        "length": 720,
                        "width": 559,
                        "qty": 4,
                        "grain": true
                      },
                      {
                        "name": "Bottom",
                        "length": 564,
                        "width": 559,
                        "qty": 2,
                        "grain": true
                      },
                      {
                        "name": "Rail",
                        "length": 564,
                        "width": 99,
                        "qty": 4,
                        "grain": true
                      },
                      {
                        "name": "Shelf",
                        "length": 562,
                        "width": 515,
                        "qty": 2,
                        "grain": true
                      },
                      {
                        "name": "Door",
                        "length": 716,
                        "width": 596,
                        "qty": 2,
                        "grain": true
                      }
                    ],
                    "sheetLength": 2800,
                    "sheetWidth": 2070,
                    "sheetPrice": 148,
                    "kerf": 4,
                    "trim": 10,
                    "method": "auto",
                    "grainedMaterial": true
                  }
                },
                "example3": {
                  "summary": "Plywood 2440 × 1220, mixed parts, MaxRects",
                  "value": {
                    "parts": [
                      {
                        "name": "Top",
                        "length": 1200,
                        "width": 600,
                        "qty": 1
                      },
                      {
                        "name": "Side",
                        "length": 700,
                        "width": 580,
                        "qty": 2
                      },
                      {
                        "name": "Shelf",
                        "length": 1160,
                        "width": 300,
                        "qty": 3
                      },
                      {
                        "name": "Strip",
                        "length": 1160,
                        "width": 80,
                        "qty": 4
                      }
                    ],
                    "sheetLength": 2440,
                    "sheetWidth": 1220,
                    "sheetPrice": 74,
                    "kerf": 3.2,
                    "trim": 5,
                    "method": "maxrects",
                    "grainedMaterial": false
                  }
                },
                "example4": {
                  "summary": "L-shaped tops and corner-cut shelves with small parts in the notches",
                  "value": {
                    "parts": [
                      {
                        "name": "L top",
                        "length": 1200,
                        "width": 900,
                        "qty": 2,
                        "shape": "l",
                        "a": 650,
                        "b": 500
                      },
                      {
                        "name": "Corner shelf",
                        "length": 600,
                        "width": 600,
                        "qty": 4,
                        "shape": "chamfer",
                        "a": 300,
                        "b": 300
                      },
                      {
                        "name": "Strip",
                        "length": 500,
                        "width": 120,
                        "qty": 6
                      }
                    ],
                    "sheetLength": 2800,
                    "sheetWidth": 2070,
                    "kerf": 8,
                    "trim": 10,
                    "method": "maxrects",
                    "grainedMaterial": false,
                    "effort": "deep",
                    "iterations": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KerfwiseSheetNestingResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kinetica-collision-check/calculate": {
      "post": {
        "operationId": "calculateKineticaCollisionCheck",
        "summary": "Robot pose + obstacles → collisions, minimum clearance and self-collision between links.",
        "description": "Checks a robot configuration against box, sphere and capsule obstacles and against itself. Robot links are modelled as capsules and boxes; the tool reports every pair closer than the safety margin, the minimum clearance and the closest pair, using exact segment–segment distances and an exact convex search for capsule–box distances.\n\nMethodology:\n- Robot links are capsules (segment + radius) and oriented boxes attached to the link frames; library robots generate capsules along the kinematic skeleton, URDF boxes/cylinders/spheres are imported (cylinders become capsules with caps ending at the cylinder faces; flat discs become an equal-area square prism).\n- Capsule–capsule: closest points between two segments minus both radii. Capsule–box: the squared distance from the segment to the box is convex in the segment parameter, so a 64-step golden-section search finds the minimum. Box–box: separating-axis test for overlap, else the minimum of the 24 edge-to-box distances.\n- Self-collision skips adjacent links, user-listed pairs and pairs that already touch in the robot’s home pose (as in MoveIt’s allowed-collision matrix); the static base link is not checked against the environment.\n- A pair counts as a contact when its clearance is below the safety margin.\n\nKinematic simulation for planning and teaching. It is not a certified safety function: verify every program on the real controller with reduced speed and the cell safety system active. Geometry is a simplified envelope of the real robot.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kinetica_collision_check",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KineticaCollisionCheckInput"
              },
              "examples": {
                "example1": {
                  "summary": "6-axis arm above a table (clear)",
                  "value": {
                    "robot": "kx6-1400",
                    "joints": "0, 0, 0, 0, 90, 0",
                    "obstacles": [
                      {
                        "name": "Table",
                        "shape": "box",
                        "x": 900,
                        "y": 0,
                        "z": 350,
                        "sx": 700,
                        "sy": 900,
                        "sz": 700
                      }
                    ],
                    "margin": 10
                  }
                },
                "example2": {
                  "summary": "Arm reaching into a box (collision)",
                  "value": {
                    "robot": "kx6-1400",
                    "joints": "0, 40, 30, 0, 20, 0",
                    "obstacles": [
                      {
                        "name": "Machine",
                        "shape": "box",
                        "x": 1000,
                        "y": 0,
                        "z": 400,
                        "sx": 600,
                        "sy": 800,
                        "sz": 800
                      }
                    ],
                    "margin": 0
                  }
                },
                "example3": {
                  "summary": "Folded elbow: self-collision check",
                  "value": {
                    "robot": "kx6-1400",
                    "joints": "0, -90, 155, 0, 125, 0",
                    "self": true,
                    "margin": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KineticaCollisionCheckResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kinetica-forward-kinematics/calculate": {
      "post": {
        "operationId": "calculateKineticaForwardKinematics",
        "summary": "DH table or URDF + joint values → TCP pose, every joint frame, Jacobian and manipulability.",
        "description": "Computes the tool-centre-point pose of a serial robot from its Denavit–Hartenberg table (standard or modified), a URDF subset or a library robot and the joint values. Returns the 4×4 transform, position and roll/pitch/yaw, quaternion, all intermediate frames, the geometric Jacobian, manipulability and singularity status, and static payload torques.\n\nMethodology:\n- Each joint compiles to Fᵢ = Fᵢ₋₁ · Preᵢ · Motionᵢ(qᵢ) · Postᵢ. Standard DH: Rz(θ+q)·Tz(d)·Tx(a)·Rx(α); modified DH (Craig): Rx(α)·Tx(a)·Rz(θ+q)·Tz(d); URDF: origin(xyz, rpy) · rotation about / translation along the joint axis.\n- TCP = base mount · F_n · tool offset. Orientation is reported as fixed-axis roll-pitch-yaw (R = Rz(yaw)·Ry(pitch)·Rx(roll), the URDF convention) and as a unit quaternion [w, x, y, z].\n- Geometric Jacobian at the TCP: revolute column [zᵢ × (p − oᵢ); zᵢ], prismatic column [zᵢ; 0]. Rows vx, vy, vz in mm per rad (or per mm), wx, wy, wz in rad per unit.\n- Manipulability: Yoshikawa √det(J·Jᵀ) of the Jacobian with translation rows divided by a characteristic length (the arm’s skeleton length), and σmin/σmax as a 0–1 singularity indicator (< 0.001 singular, < 0.03 near-singular).\n- Payload loads: static holding torque τ = −Jᵀ(cog)·m·g for the tool mass at its centre of gravity (g = 9.80665 m/s², gravity along −z world).\n- Verified against hand-computed planar cases, the PUMA 560 qz pose (452.1, −150.05, 1103.63) mm of the Robotics Toolbox model and the UR5 zero pose (−817.25, −191.45, −5.491) mm.\n\nKinematic simulation for planning and teaching. It is not a certified safety function: verify every program on the real controller with reduced speed and the cell safety system active.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kinetica_forward_kinematics",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KineticaForwardKinematicsInput"
              },
              "examples": {
                "example1": {
                  "summary": "PUMA 560 at qz (all zeros) — textbook pose",
                  "value": {
                    "robot": "puma560",
                    "joints": "0, 0, 0, 0, 0, 0"
                  }
                },
                "example2": {
                  "summary": "UR5-style cobot at its upright home",
                  "value": {
                    "robot": "ur5-like",
                    "joints": "0, -90, 90, -90, -90, 0"
                  }
                },
                "example3": {
                  "summary": "Planar 2-link arm from a custom DH table (l1 = 300, l2 = 250)",
                  "value": {
                    "robot": "custom-dh",
                    "convention": "dh",
                    "dh": [
                      {
                        "type": "revolute",
                        "a": 300,
                        "alpha": 0,
                        "d": 0,
                        "theta": 0
                      },
                      {
                        "type": "revolute",
                        "a": 250,
                        "alpha": 0,
                        "d": 0,
                        "theta": 0
                      }
                    ],
                    "joints": "30, 60"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KineticaForwardKinematicsResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kinetica-inverse-kinematics/calculate": {
      "post": {
        "operationId": "calculateKineticaInverseKinematics",
        "summary": "Target TCP pose → all joint solutions found, residuals, joint limits and reachability.",
        "description": "Solves the inverse kinematics of a serial robot (DH table, URDF subset or library robot) for a target TCP pose with damped least squares from several deterministic seeds, plus closed-form solutions for planar 2-link arms and SCARAs. Lists every distinct solution with its residual, joint-limit and singularity status, and explains why a target is unreachable.\n\nMethodology:\n- Error e = [Δp / L; Δφ] with Δφ the rotation vector from current to target orientation and L the arm’s characteristic length, restricted to the task rows the robot can actually control (e.g. x, y, z, yaw for a SCARA; x, y for a planar arm).\n- Damped least squares step Δq = Jᵀ(J·Jᵀ + λ²I)⁻¹e with adaptive damping λ (decrease after an improving step, increase after a failed one), step-size limit 0.5 rad, and projection onto the joint limits (revolute joints wrap by 2π when the range allows).\n- Restarts: the preferred configuration, the home pose, then pseudo-random configurations from a seeded mulberry32 generator; distinct converged solutions (position residual ≤ 1e-6 mm, orientation ≤ 1e-8 rad) are kept and sorted by distance to the preferred configuration.\n- Closed forms: planar 2R and SCARA use the law of cosines (two elbow branches), then FK verifies and a short DLS polish removes any remaining residual.\n- Unreachable targets report the distance from the first joint versus the skeleton length (upper bound of reach) and the closest configuration found.\n\nKinematic simulation for planning and teaching. It is not a certified safety function: verify every program on the real controller with reduced speed and the cell safety system active.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kinetica_inverse_kinematics",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KineticaInverseKinematicsInput"
              },
              "examples": {
                "example1": {
                  "summary": "6-axis arm, tool pointing down at (700, 200, 600) mm",
                  "value": {
                    "robot": "kx6-1400",
                    "x": 700,
                    "y": 200,
                    "z": 600,
                    "roll": 180,
                    "pitch": 0,
                    "yaw": 0
                  }
                },
                "example2": {
                  "summary": "SCARA closed form: (300, 250, 200) mm, yaw 45°",
                  "value": {
                    "robot": "scara-550",
                    "x": 300,
                    "y": 250,
                    "z": 200,
                    "roll": 180,
                    "pitch": 0,
                    "yaw": 45
                  }
                },
                "example3": {
                  "summary": "Planar arm: elbow-up and elbow-down for (350, 200)",
                  "value": {
                    "robot": "planar2",
                    "x": 350,
                    "y": 200,
                    "z": 50,
                    "roll": 0,
                    "pitch": 0,
                    "yaw": 0,
                    "mode": "position"
                  }
                },
                "example4": {
                  "summary": "Unreachable target (2 m away)",
                  "value": {
                    "robot": "ur5-like",
                    "x": 2000,
                    "y": 0,
                    "z": 300,
                    "roll": 180,
                    "pitch": 0,
                    "yaw": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KineticaInverseKinematicsResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/kinetica-trajectory-planner/calculate": {
      "post": {
        "operationId": "calculateKineticaTrajectoryPlanner",
        "summary": "Waypoints (PTP, LIN, CIRC) + limits → timed joint samples, cycle time and peak velocities.",
        "description": "Plans a robot motion program from waypoints: synchronous joint moves (PTP), straight TCP lines (LIN) and circular arcs (CIRC) with trapezoidal, quintic or jerk-limited S-curve profiles that respect the joint velocity and acceleration limits, blend radii and dwell times. Returns the cycle time, per-segment timing, peak joint velocities and accelerations, a sampled joint/TCP trajectory and a CSV.\n\nMethodology:\n- PTP: synchronous joint interpolation q(s) = q₀ + s·Δq with one profile on s ∈ [0, 1]; the limits on s are min over joints of vmaxᵢ/|Δqᵢ| (and the same for acceleration and jerk), so every joint starts and stops together and none exceeds its limits.\n- Trapezoid: if h ≥ v²/a then Ta = v/a and T = h/v + v/a, else triangular with Ta = √(h/a), T = 2√(h/a). Quintic: s = 10τ³ − 15τ⁴ + 6τ⁵ with T = max(1.875·h/v, √(5.7735·h/a)). S-curve: double-S with jerk limit (Biagiotti & Melchiorri §3.4).\n- LIN/CIRC: the TCP follows the line or the circle through start, via and end point, orientation by quaternion slerp; IK runs per sample seeded from the previous one. If a joint would exceed its velocity/acceleration limit the segment is slowed down uniformly and re-planned (reported as speed scale).\n- Blending: consecutive moves overlap by the time the TCP spends inside the blend radius (capped at half of each move) and are superposed in joint space, which rounds the corner without stopping.\n- Velocities/accelerations in the output are central differences of the sampled trajectory; the cycle time includes dwell times.\n\nKinematic simulation for planning and teaching. It is not a certified safety function: verify every program on the real controller with reduced speed and the cell safety system active. Real controllers add their own look-ahead, dynamics limits and servo lag, so measured cycle times differ.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "kinetica_trajectory_planner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KineticaTrajectoryPlannerInput"
              },
              "examples": {
                "example1": {
                  "summary": "6-axis arm: two PTP moves and a LIN line",
                  "value": {
                    "robot": "kx6-1400",
                    "profile": "trapezoid",
                    "waypoints": [
                      {
                        "name": "Home",
                        "type": "PTP",
                        "target": "j: 0, 0, 0, 0, 90, 0"
                      },
                      {
                        "name": "Above",
                        "type": "PTP",
                        "target": "p: 800, -200, 700, 180, 0, 0",
                        "vel": 80,
                        "acc": 80,
                        "blend": 30
                      },
                      {
                        "name": "Line",
                        "type": "LIN",
                        "target": "p: 800, 200, 700, 180, 0, 0",
                        "vel": 250,
                        "acc": 1500
                      },
                      {
                        "name": "Home",
                        "type": "PTP",
                        "target": "j: 0, 0, 0, 0, 90, 0",
                        "vel": 100,
                        "acc": 100
                      }
                    ]
                  }
                },
                "example2": {
                  "summary": "Single joint, trapezoid timing check (90° at 156°/s, 400°/s²)",
                  "value": {
                    "robot": "kx6-1400",
                    "profile": "trapezoid",
                    "waypoints": [
                      {
                        "name": "A",
                        "type": "PTP",
                        "target": "j: 0, 0, 0, 0, 90, 0"
                      },
                      {
                        "name": "B",
                        "type": "PTP",
                        "target": "j: 90, 0, 0, 0, 90, 0",
                        "vel": 100,
                        "acc": 100
                      }
                    ]
                  }
                },
                "example3": {
                  "summary": "SCARA pick with S-curve profile and CIRC",
                  "value": {
                    "robot": "scara-550",
                    "profile": "scurve",
                    "waypoints": [
                      {
                        "name": "Start",
                        "type": "PTP",
                        "target": "j: -30, 60, 50, 0"
                      },
                      {
                        "name": "P1",
                        "type": "LIN",
                        "target": "p: 350, -200, 250, 180, 0, 0",
                        "vel": 500,
                        "acc": 4000
                      },
                      {
                        "name": "Arc",
                        "type": "CIRC",
                        "target": "p: 350, 200, 250, 180, 0, 0",
                        "via": "450, 0, 250",
                        "vel": 500,
                        "acc": 4000
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KineticaTrajectoryPlannerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-blend-mixer/calculate": {
      "post": {
        "operationId": "calculateLuminaBlendMixer",
        "summary": "Preview eight blend modes and opacity for two opaque colors.",
        "description": "Mix a source HEX color over a backdrop using normal, multiply, screen, overlay, darken, lighten, difference or exclusion. Calculate the final sRGB color at a chosen layer opacity.\n\nMethodology:\n- Separable blend formulas operate on normalized sRGB channel values. Source-over with an opaque backdrop is (1 − opacity) × backdrop + opacity × blend(backdrop,source).\n- Results round to 8-bit RGB. This calculator assumes opaque colors; image layer alpha is additionally handled by the browser compositor.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_blend_mixer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaBlendMixerInput"
              },
              "examples": {
                "example1": {
                  "summary": "A gentle green multiply tint",
                  "value": {
                    "backdrop": "#c8d6bd",
                    "source": "#426a60",
                    "opacity": 60,
                    "mode": "multiply"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaBlendMixerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-layer-transform/calculate": {
      "post": {
        "operationId": "calculateLuminaLayerTransform",
        "summary": "Calculate a layer transform, rotated corners and canvas bounds.",
        "description": "Enter canvas and layer dimensions, center position, rotation and flips. Get the affine matrix for normalized layer coordinates plus exact corner and bounding-box coordinates.\n\nMethodology:\n- For layer-local coordinates u/v from 0 to 1, canvas x = a*u + c*v + e and y = b*u + d*v + f.\n- The layer rotates around its center. Flips apply in local space before rotation. Coordinates outside the canvas are retained.\n- The same affine transform is used by Design Layers for selection, dragging and mask coordinates.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_layer_transform",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaLayerTransformInput"
              },
              "examples": {
                "example1": {
                  "summary": "A rotated photo on a cover",
                  "value": {
                    "canvasWidth": 1000,
                    "canvasHeight": 700,
                    "width": 600,
                    "height": 400,
                    "x": 500,
                    "y": 350,
                    "rotation": 20
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaLayerTransformResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ohmforge-ac-sweep/calculate": {
      "post": {
        "operationId": "calculateOhmforgeAcSweep",
        "summary": "Frequency response of a circuit: Bode magnitude/phase, −3 dB corner and unity gain.",
        "description": "Linearises the circuit at its DC operating point and solves the complex MNA equations across a logarithmic frequency sweep. Returns magnitude in dB and unwrapped phase of an output node (optionally relative to an input node), the peak gain, lower/upper −3 dB frequencies, the unity-gain frequency, a Bode chart and CSV.\n\nMethodology:\n- The DC operating point is solved first; every nonlinear device is replaced by its small-signal conductances at that point (diode gd, BJT gm/gπ/go, MOSFET gm/gds, op-amp output slope).\n- Capacitors become admittances jωC, inductors impedances jωL in their branch equations, coupled inductors jωM; the complex system is solved by dense LU with partial pivoting at every frequency.\n- Magnitude is 20·log10|H|, phase is unwrapped to be continuous. The −3 dB points are found 3.01 dB below the peak by logarithmic interpolation; unity gain is the 0 dB crossing.\n\nTeaching-grade simulation: device models are simplified (Ebers–Moll BJT, level-1 MOSFET, behavioural op-amps and 555) and part ratings are typical values. Verify real designs on the bench and against manufacturer data before building anything connected to mains voltage.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ohmforge_ac_sweep",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OhmforgeAcSweepInput"
              },
              "examples": {
                "example1": {
                  "summary": "RC low-pass (fc ≈ 1.59 kHz)",
                  "value": {
                    "netlist": "* RC low-pass, fc = 1/(2*pi*1k*100n) = 1591.5 Hz\nV1 in 0 DC 0 AC 1\nR1 in out 1k\nC1 out 0 100n\n.ac dec 20 10 1meg",
                    "output": "out",
                    "input": "V1"
                  }
                },
                "example2": {
                  "summary": "Inverting op-amp, gain −10",
                  "value": {
                    "netlist": "* Inverting amplifier, gain -10 (ideal op-amp as VCVS)\nV1 in 0 AC 1\nR1 in m 1k\nR2 m out 10k\nE1 out 0 0 m 100k\n.ac dec 10 10 10meg",
                    "output": "out",
                    "input": "V1"
                  }
                },
                "example3": {
                  "summary": ".param + .subckt: two cascaded RC stages",
                  "value": {
                    "netlist": "* Two-stage RC low-pass from a parameterised subcircuit\n.param fc=1k R=10k\n.subckt rclp in out params: r=1k f=1k\nR1 in out {r}\nC1 out 0 {1/(2*pi*r*f)}\n.ends rclp\nV1 in 0 DC 0 AC 1\nX1 in mid rclp r={R} f={fc}\nX2 mid out rclp r={R*10} f={fc}\n.ac dec 20 10 100k",
                    "output": "out",
                    "input": "V1"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OhmforgeAcSweepResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ohmforge-dc-operating-point/calculate": {
      "post": {
        "operationId": "calculateOhmforgeDcOperatingPoint",
        "summary": "SPICE netlist in, node voltages, branch currents, power and overload warnings out.",
        "description": "Solves the DC operating point of a circuit with Ohmforge's SPICE-style engine (Modified Nodal Analysis, Newton–Raphson with junction limiting, damping, gmin and source stepping). Returns every node voltage, the current and power of every element, total supply power, parts beyond their ratings and plain-language convergence diagnostics.\n\nMethodology:\n- The netlist is parsed (SPICE subset; .param values and {expressions} are evaluated and .subckt instances are flattened into plain elements named like R.X1.R1) and compiled into Modified Nodal Analysis unknowns: one voltage per node plus one current per voltage source, inductor and controlled-source output.\n- Nonlinear devices (Shockley diode with series resistance and breakdown, Ebers–Moll BJT with Early effect, Shichman–Hodges level-1 MOSFET) are linearised at each Newton–Raphson iteration; junction voltages are limited with the SPICE3 pnjlim rule.\n- If plain Newton–Raphson fails, the solver retries with damping (≤ 0.5 V per iteration), then gmin stepping (1e-2 S → 1e-12 S to ground on every node) and finally source stepping (all sources ramped 0 → 100 %). Every attempt is reported in plain words.\n- Topology checks run first: missing ground, loops of voltage sources/inductors (no unique solution) and nodes without a DC path to ground.\n- Element power is Σ(terminal current × node voltage); sources show negative power when delivering. Ratings (resistor watts, LED current, diode/transistor limits, capacitor voltage) are compared with the result.\n\nTeaching-grade simulation: device models are simplified (Ebers–Moll BJT, level-1 MOSFET, behavioural op-amps and 555) and part ratings are typical values. Verify real designs on the bench and against manufacturer data before building anything connected to mains voltage.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ohmforge_dc_operating_point",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OhmforgeDcOperatingPointInput"
              },
              "examples": {
                "example1": {
                  "summary": "Voltage divider (10 V, 1k/3k → 7.5 V)",
                  "value": {
                    "netlist": "* Voltage divider\nV1 in 0 DC 10\nR1 in out 1k\nR2 out 0 3k\n.op\n.end"
                  }
                },
                "example2": {
                  "summary": "Transistor switching an LED",
                  "value": {
                    "netlist": "* LED with series resistor and a 2N3904 switch\nVCC vcc 0 9\nRB vcc b 10k\nRC vcc a 470\nD1 a c LED_RED\nQ1 c b 0 2N3904\n.op"
                  }
                },
                "example3": {
                  "summary": "Diode forward drop (Shockley)",
                  "value": {
                    "netlist": "* Shockley diode check\nV1 in 0 5\nR1 in a 1k\nD1 a 0 DMOD\n.model DMOD D(IS=1e-14 N=1)\n.op"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OhmforgeDcOperatingPointResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ohmforge-led-resistor/calculate": {
      "post": {
        "operationId": "calculateOhmforgeLedResistor",
        "summary": "Series resistor for LEDs, rounded to a standard value and checked with a real diode model.",
        "description": "Calculates the series resistor for one or more LEDs in series: R = (Vs − n·Vf) / I, rounds up to the next E6/E12/E24 value, and then verifies the actual current, LED voltage and resistor dissipation by simulating the circuit with Ohmforge's diode model. Suggests a power rating with 2× margin.\n\nMethodology:\n- Exact value R = (Vs − n·Vf) / I with the typical forward voltage of the chosen colour at 20 mA.\n- Rounded UP to the next preferred value of the chosen E-series so the current never exceeds the target.\n- The circuit (supply, resistor, n LEDs) is then simulated with Ohmforge's LED diode model (n = 2, Rs = 4 Ω, fitted to Vf at 20 mA) to report the real current, which is slightly different because Vf depends on current.\n- Resistor rating: next of 0.125/0.25/0.5/1/2/5 W at or above twice the dissipation.\n\nForward voltages vary by LED type and batch (±0.2 V is common); check the datasheet of your LED. Mains-powered LED circuits need proper isolation – do not build them from this calculation.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ohmforge_led_resistor",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OhmforgeLedResistorInput"
              },
              "examples": {
                "example1": {
                  "summary": "Red LED on 9 V at 20 mA",
                  "value": {
                    "supply": 9,
                    "colour": "red",
                    "current": 20,
                    "count": 1,
                    "series": "E12"
                  }
                },
                "example2": {
                  "summary": "Three white LEDs on 12 V at 15 mA",
                  "value": {
                    "supply": 12,
                    "colour": "white",
                    "current": 15,
                    "count": 3,
                    "series": "E24"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OhmforgeLedResistorResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/ohmforge-transient/calculate": {
      "post": {
        "operationId": "calculateOhmforgeTransient",
        "summary": "Simulate a circuit over time: waveforms, Vpp/RMS/frequency, CSV and a chart.",
        "description": "Runs a transient (time-domain) simulation of a SPICE netlist with trapezoidal or backward-Euler integration, adaptive time steps and breakpoints at source corners. Returns resampled waveforms of the chosen nodes, oscilloscope-style measurements (min, max, Vpp, mean, RMS, frequency, duty cycle, rise time), rating warnings, a chart and CSV.\n\nMethodology:\n- Starts from the DC operating point (or from initial conditions with \"uic\"), then integrates capacitors and inductors with companion models: trapezoidal (2C/h) or backward Euler (C/h); inductors and coupled inductors use branch currents.\n- Each time step is solved with Newton–Raphson. The step size adapts: the difference between the corrector and a linear predictor estimates the local truncation error; failed steps are retried with h/8.\n- Source corners (PULSE edges, PWL points) are breakpoints: the integrator lands on them and restarts with backward Euler to avoid trapezoidal ringing. Behavioural events (555 latch, relay pull-in, fuse, current limit) are localised by step bisection.\n- Returned samples are linearly interpolated onto a uniform grid (output step). Measurements: time-weighted mean and RMS, Vpp, frequency from mid-level crossings with 10 % hysteresis, 10–90 % rise time.\n\nTeaching-grade simulation: device models are simplified (Ebers–Moll BJT, level-1 MOSFET, behavioural op-amps and 555) and part ratings are typical values. Verify real designs on the bench and against manufacturer data before building anything connected to mains voltage.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "ohmforge_transient",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OhmforgeTransientInput"
              },
              "examples": {
                "example1": {
                  "summary": "RC step response (τ = 1 ms)",
                  "value": {
                    "netlist": "* RC step response, tau = 1 ms\nV1 in 0 PULSE(0 1 0 1n 1n 10m 20m)\nR1 in out 1k\nC1 out 0 1u\n.tran 10u 5m\n.end",
                    "probes": [
                      "in",
                      "out"
                    ]
                  }
                },
                "example2": {
                  "summary": "Half-wave rectifier with smoothing",
                  "value": {
                    "netlist": "* Half-wave rectifier with reservoir capacitor\nV1 ac 0 SIN(0 12 50)\nD1 ac out 1N4007\nC1 out 0 470u\nRL out 0 1k\n.tran 50u 100m",
                    "probes": [
                      "ac",
                      "out"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OhmforgeTransientResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-photo-layout/calculate": {
      "post": {
        "operationId": "calculateLuminaPhotoLayout",
        "summary": "Make a photo grid or join photos horizontally and vertically.",
        "description": "Upload photos in a ZIP to compose a collage, a contact-sheet grid, or a continuous horizontal or vertical strip. Control spacing, margins, fit and background, then download a PNG, JPEG or WebP.\n\nMethodology:\n- Images are detected by content, auto-oriented and converted to sRGB. ZIP entries keep their archive order.\n- The same integer layout geometry powers Lumina Photo Lab. Fill crops from the center; fit adds the selected background.\n- Contact mode places a small numbered index in each reserved caption strip; the browser Photo Lab also supports filename captions.\n- Canvas limit: 16 MP and 16000 px per side. Metadata is stripped. Download artifacts expire after about two hours.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_photo_layout",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaPhotoLayoutInput"
              },
              "examples": {
                "example1": {
                  "summary": "Single-image gallery canvas",
                  "value": {
                    "images": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "width": 320,
                    "height": 240,
                    "gap": 8,
                    "margin": 16,
                    "columns": 2,
                    "mode": "grid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaPhotoLayoutResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-resize-planner/calculate": {
      "post": {
        "operationId": "calculateLuminaResizePlanner",
        "summary": "Plan a precise crop and output size before resizing a photo.",
        "description": "Calculate a centered crop, final pixel dimensions and scale for a target box. Fit keeps the whole image; fill crops to the requested aspect ratio. Upscaling is optional.\n\nMethodology:\n- Fit uses the smaller target/source scale; fill uses the larger one and a centered crop.\n- When enlargement is disabled, scale is capped at 1. Pixel coordinates and output dimensions are rounded to whole pixels.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_resize_planner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaResizePlannerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Square social photo",
                  "value": {
                    "width": 6000,
                    "height": 4000,
                    "targetWidth": 1080,
                    "targetHeight": 1080,
                    "mode": "cover",
                    "upscale": false
                  }
                },
                "example2": {
                  "summary": "Fit a small photo without enlargement",
                  "value": {
                    "width": 600,
                    "height": 400,
                    "targetWidth": 1920,
                    "targetHeight": 1080,
                    "mode": "contain"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaResizePlannerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-histogram-analyzer/calculate": {
      "post": {
        "operationId": "calculateLuminaHistogramAnalyzer",
        "summary": "Inspect RGB histograms, clipped channels and tonal percentiles in a photo.",
        "description": "Upload a photo to measure luminance and RGB distributions, shadow and highlight clipping, and tonal percentiles. Download the full 256-bin analysis and get percentile-based black and white starting points.\n\nMethodology:\n- Auto-oriented, display-referred 8-bit sRGB analysis. Fully transparent pixels are excluded; other pixels count equally.\n- Luma is rounded 0.2126 R + 0.7152 G + 0.0722 B. Clipping counts any channel at or below 2, or at or above 253. This does not measure sensor exposure or recover clipped RAW data.\n- Report 1st, 50th and 99th luminance percentiles using nearest-rank selection. Suggested levels discard 0.5% from each luminance tail; flat or empty images retain 0–255. Values are suggestions, not an exposure judgment.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_histogram_analyzer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaHistogramAnalyzerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Inspect a red image",
                  "value": {
                    "image": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaHistogramAnalyzerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-color-analyzer/calculate": {
      "post": {
        "operationId": "calculateLuminaColorAnalyzer",
        "summary": "Check palette luminance and choose readable text for photos and graphics.",
        "description": "Analyze up to 64 six-digit hex colors for relative luminance and contrast against black and white. Use the results to choose watermark or caption colors for opaque backgrounds.\n\nMethodology:\n- Convert sRGB components to linear light and calculate relative luminance using coefficients 0.2126, 0.7152 and 0.0722.\n- Contrast ratio is (lighter luminance + 0.05) / (darker luminance + 0.05). Choose black or white by the larger ratio.\n- A single color cannot certify text over an entire photograph. Check the actual text area and opacity.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_color_analyzer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaColorAnalyzerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Alpine color palette",
                  "value": {
                    "colors": "#164e63 #c5d6a5 #f4e6cf"
                  }
                },
                "example2": {
                  "summary": "Black and white extremes",
                  "value": {
                    "colors": "#000000 #ffffff"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaColorAnalyzerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-print-planner/calculate": {
      "post": {
        "operationId": "calculateLuminaPrintPlanner",
        "summary": "Find how large your photo can print at your chosen resolution.",
        "description": "Convert image pixel dimensions into print dimensions in inches and centimeters at a specified pixels-per-inch resolution. Compare sizes without uploading the image.\n\nMethodology:\n- Print width in inches equals pixel width divided by PPI. One inch equals 2.54 centimeters.\n- PPI describes image pixel density, not printer dot density. Calculated dimensions do not guarantee print quality.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_print_planner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaPrintPlannerInput"
              },
              "examples": {
                "example1": {
                  "summary": "24 MP photo at 300 PPI",
                  "value": {
                    "width": 6000,
                    "height": 4000,
                    "ppi": 300
                  }
                },
                "example2": {
                  "summary": "Web image at 150 PPI",
                  "value": {
                    "width": 1920,
                    "height": 1080,
                    "ppi": 150
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaPrintPlannerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-gif-maker/calculate": {
      "post": {
        "operationId": "calculateLuminaGifMaker",
        "summary": "Turn an ordered set of photos into a looping animated GIF.",
        "description": "Upload a ZIP of photos, choose frame dimensions, timing and fit, and download an animated GIF. A shared deterministic encoder uses a fixed 256-color palette, with no external service.\n\nMethodology:\n- Archive entry order is playback order. Each image is auto-oriented, centered, resized and flattened on the chosen background.\n- Frame duration is rounded to the GIF time unit of 10 milliseconds. The limit is 24 frames and 12 million total frame pixels.\n- GIF uses a fixed RGB332 palette (256 colors), no transparency and no adaptive photographic quantization. File sizes may be larger than optimized GIF encoders.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_gif_maker",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaGifMakerInput"
              },
              "examples": {
                "example1": {
                  "summary": "One-frame GIF",
                  "value": {
                    "images": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "width": 32,
                    "height": 32,
                    "delay": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaGifMakerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/lumina-tone-processor/calculate": {
      "post": {
        "operationId": "calculateLuminaToneProcessor",
        "summary": "Apply levels, RGB curves and white balance, or reuse a Lumina photo profile.",
        "description": "Develop a photo with black and white points, gamma, five-point master and RGB curves, and linear-light white-balance gains. Download a transparent PNG and reusable settings profile, or paste a profile saved from Lumina.\n\nMethodology:\n- Auto-oriented, display-referred 8-bit sRGB analysis. Fully transparent pixels are excluded; other pixels count equally.\n- Luma is rounded 0.2126 R + 0.7152 G + 0.0722 B. Clipping counts any channel at or below 2, or at or above 253. This does not measure sensor exposure or recover clipped RAW data.\n- A supplied Lumina profile overrides all manual fields. Otherwise apply linear-light RGB gains, existing Develop exposure/contrast/saturation, encoded-sRGB levels and gamma, master curve, then channel curves. Five fixed inputs are interpolated with straight segments; outputs may be non-monotonic.\n- Gamma uses normalizedInput^(1/gamma). PNG retains the source alpha and omits source metadata. Profiles contain only adjustments and tone settings; camera PP3, RAW and ICC profiles are not supported.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "lumina_tone_processor",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LuminaToneProcessorInput"
              },
              "examples": {
                "example1": {
                  "summary": "Gentle contrast curve",
                  "value": {
                    "image": {
                      "filename": "sample.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "master": "0,50,128,206,255"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LuminaToneProcessorResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-pixel-quantizer/calculate": {
      "post": {
        "operationId": "calculateFliplinePixelQuantizer",
        "summary": "Convert images into pixel art with palettes, dithering and retro effects.",
        "description": "Resize a photo or drawing, adjust its colors, and apply an automatic or imported palette with ordered or error-diffusion dithering. Download a transparent PNG, editable pixel sprite and reusable recipe.\n\nMethodology:\n- Auto-orient and convert the source to sRGB, then sample native output pixels using nearest-neighbor fit, centered fill or stretch. Images are limited to 4 MB and 2 MP.\n- Apply RGB offsets, brightness and contrast, gamma, then saturation. Palette matching uses squared sRGB distance with first-entry tie-breaking. Automatic palettes select frequent adjusted colors.\n- Ordered dithering uses a 4 × 4 Bayer bias. Serpentine Floyd–Steinberg distributes errors with 7/16, 3/16, 5/16 and 1/16 weights; transparent pixels neither receive nor spread errors. Strength zero equals no dithering.\n- Alpha below the threshold becomes transparent. PNG integer scaling repeats pixels exactly. Dithering runs on native pixels and does not change with preview zoom or export scale.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_pixel_quantizer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplinePixelQuantizerInput"
              },
              "examples": {
                "example1": {
                  "summary": "A small red pixel sprite",
                  "value": {
                    "image": {
                      "filename": "red.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "width": 8,
                    "height": 8,
                    "colors": 4,
                    "scale": 4
                  }
                },
                "example2": {
                  "summary": "Moss ordered dither",
                  "value": {
                    "image": {
                      "filename": "red.png",
                      "contentType": "image/png",
                      "contentBase64": "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAQMAAABJtOi3AAAABlBMVEX/////AADrWueTAAAAEklEQVQI12NgIBb8/09dgjgAAPcnH+GApkH4AAAAAElFTkSuQmCC"
                    },
                    "width": 16,
                    "height": 16,
                    "scale": 4,
                    "palette": "#172c2f,#416350,#93ab69,#e3e6b1",
                    "dither": "bayer",
                    "contrast": 12
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplinePixelQuantizerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-pixel-transform/calculate": {
      "post": {
        "operationId": "calculateFliplinePixelTransform",
        "summary": "Flip, rotate or shift an indexed pixel sprite without blur.",
        "description": "Enter a compact pixel grid and HEX palette. Transform it with exact integer coordinates, optional wrapping and transparent pixels, then download an editable Flipline Pixel Studio project.\n\nMethodology:\n- Grid index 0 is transparent; 1–9 and A–W refer to palette entries 1–32. Rows must have equal width, at most 128 pixels per side.\n- Rotation is clockwise and swaps width and height. Shift clips outside pixels or wraps them to the opposite edge. No interpolation or color changes.\n- The same transform powers Pixel Studio. Open the downloaded .flipline-pixels.json file with the main Open button or Open sprite.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_pixel_transform",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplinePixelTransformInput"
              },
              "examples": {
                "example1": {
                  "summary": "Rotate a tiny sprite",
                  "value": {
                    "rows": "1..\n.2.",
                    "palette": "#213b45,#65a87b",
                    "operation": "rotate"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplinePixelTransformResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-palette-converter/calculate": {
      "post": {
        "operationId": "calculateFliplinePaletteConverter",
        "summary": "Convert HEX, GIMP GPL and JASC-PAL palettes without changing color order.",
        "description": "Paste a palette in HEX, GPL or JASC-PAL format, validate its colors, and download all three formats for pixel-art workflows. Keep duplicate colors and entry order across conversions.\n\nMethodology:\n- Accept 1–32 opaque RGB colors. HEX uses six-digit #RRGGBB values; GPL supports RGB rows and optional color names; JASC-PAL requires version 0100 and its declared count.\n- Entry order and duplicates are preserved. Transparency is not stored in these text palettes; Pixel Studio reserves a separate transparent index. Binary PAL, ASE and ACO files are not supported.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_palette_converter",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplinePaletteConverterInput"
              },
              "examples": {
                "example1": {
                  "summary": "Four-color HEX palette",
                  "value": {
                    "text": "#172c2f,#416350,#93ab69,#e3e6b1",
                    "name": "Moss"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplinePaletteConverterResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-retro-batch/calculate": {
      "post": {
        "operationId": "calculateFliplineRetroBatch",
        "summary": "Apply one retro palette and effect recipe to a ZIP of images.",
        "description": "Convert up to eight images into consistent pixel-art assets with shared colors, dithering and adjustments. Download PNGs, editable pixel projects and the resolved recipe in one ZIP.\n\nMethodology:\n- Auto-orient and convert the source to sRGB, then sample native output pixels using nearest-neighbor fit, centered fill or stretch. Images are limited to 4 MB and 2 MP.\n- Apply RGB offsets, brightness and contrast, gamma, then saturation. Palette matching uses squared sRGB distance with first-entry tie-breaking. Automatic palettes select frequent adjusted colors.\n- Ordered dithering uses a 4 × 4 Bayer bias. Serpentine Floyd–Steinberg distributes errors with 7/16, 3/16, 5/16 and 1/16 weights; transparent pixels neither receive nor spread errors. Strength zero equals no dithering.\n- Alpha below the threshold becomes transparent. PNG integer scaling repeats pixels exactly. Dithering runs on native pixels and does not change with preview zoom or export scale.\n- ZIP limits: 16 entries, eight supported images, 4 MB per entry, 16 MB uncompressed input and 8 MP of decoded images total. Unsupported files are reported. ZIP order defines the first image.\n- If the palette is automatic, derive it from the first adjusted image and reuse it for every image. The recipe stored in the output resolves the palette to exact HEX values. Output names have numbered prefixes to avoid collisions.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_retro_batch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineRetroBatchInput"
              },
              "examples": {
                "example1": {
                  "summary": "Two sprites with one moss palette",
                  "value": {
                    "images": {
                      "filename": "images.zip",
                      "contentType": "application/zip",
                      "contentBase64": "UEsDBBQAAAgAAAAAIQATAmpkXQAAAF0AAAAHAAAAb25lLnBuZ4lQTkcNChoKAAAADUlIRFIAAAAgAAAAIAEDAAAASbTotwAAAAZQTFRF/////wAA61rnkwAAABJJREFUCNdjYCAW/P9PXYI4AAD3Jx/hgKZB+AAAAABJRU5ErkJgglBLAwQUAAAIAAAAACEAEwJqZF0AAABdAAAABwAAAHR3by5wbmeJUE5HDQoaCgAAAA1JSERSAAAAIAAAACABAwAAAEm06LcAAAAGUExURf////8AAOta55MAAAASSURBVAjXY2AgFvz/T12COAAA9ycf4YCmQfgAAAAASUVORK5CYIJQSwECFAAUAAAIAAAAACEAEwJqZF0AAABdAAAABwAAAAAAAAAAAAAAAAAAAAAAb25lLnBuZ1BLAQIUABQAAAgAAAAAIQATAmpkXQAAAF0AAAAHAAAAAAAAAAAAAAAAAIIAAAB0d28ucG5nUEsFBgAAAAACAAIAagAAAAQBAAAAAA=="
                    },
                    "width": 8,
                    "height": 8,
                    "scale": 2,
                    "palette": "#172c2f,#416350,#93ab69,#e3e6b1",
                    "dither": "bayer"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineRetroBatchResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/flipline-sprite-sheet/calculate": {
      "post": {
        "operationId": "calculateFliplineSpriteSheet",
        "summary": "Plan an animation atlas with exact frame rectangles and padding.",
        "description": "Calculate a sprite sheet canvas and the pixel coordinates of every drawing. Use the JSON rectangles in a game or export the actual sheet from Flipline.\n\nMethodology:\n- Frames are placed in row-major order with padding around the sheet and between cells.\n- Width = columns × frame width + (columns + 1) × padding. Height uses the ceiling of drawing count / columns.\n- Columns are capped at the drawing count. Canvas limit: 16 MP and 16000 px per side. This planner returns coordinates, not an image.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "flipline_sprite_sheet",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FliplineSpriteSheetInput"
              },
              "examples": {
                "example1": {
                  "summary": "Twelve sprites in four columns",
                  "value": {
                    "count": 12,
                    "width": 256,
                    "height": 256,
                    "columns": 4,
                    "padding": 2
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FliplineSpriteSheetResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/threadsmith-design-stats/calculate": {
      "post": {
        "operationId": "calculateThreadsmithDesignStats",
        "summary": "Stitch count, colours, trims, thread, sewing time and density of a DST, PES, JEF or EXP file.",
        "description": "Reads a Tajima DST, Brother PES, Janome JEF or Melco EXP file (or a stitch list) and reports stitches, jumps, trims, colour changes, size and hoop fit, thread length per colour with a bobbin estimate, sewing time at your machine speed and a thread-density check with the densest spots, plus a thread-look preview.\n\nMethodology:\n- DST records are decoded from balanced ternary (bits for ±1, ±3, ±9, ±27, ±81 per axis); runs of three or more jumps count as one trim, as on most machines. EXP bytes are two’s-complement 0.1 mm steps with 0x80 control codes. PES files are read from their PEC block (7/12-bit moves, jump/trim flags, PEC palette colours); JEF from the stitch offset in its header (Janome palette colours).\n- Sewn length = sum of stitch lengths per colour; top thread = sewn length + 0.8 mm take-up per penetration + 5 % waste; bobbin ≈ 33 % of sewn length.\n- Sewing time = stitches ÷ speed + trims × trim time + colour changes × change time + 0.15 s per jump.\n- Density: each stitch deposits length × 0.4 mm thread width into the 1 mm cells it crosses; values are thread layers (1 ≈ fully covered once).\n\nThread and time figures are planning estimates; real consumption depends on tension, fabric, stabiliser and machine.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_design_stats",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithDesignStatsInput"
              },
              "examples": {
                "example1": {
                  "summary": "Leaf template as DST, 850 stitches/min",
                  "value": {
                    "file": {
                      "filename": "leaf.dst",
                      "contentBase64": "TEE6TEVBRiAgICAgICAgICAgIA1TVDogICAyMjc5DUNPOiAgMg0rWDogIDE4OQ0tWDogIDE4OA0rWTogIDM4Mg0tWTogIDQ2OA1BWDorICAgNzgNQVk6LSAgNDUwDU1YOisgICAgMA1NWTorICAgIDANUEQ6KioqKioqDRogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAUQIcWQIcAAAOgQANQgAMCYQMCYQOCYAMCYAMAYgMCYgMIYQNKYQNJYANKYAOZogMYogNVqQNUqQMWKQOVaAOUaANVagOligOkmgOlmgNmmAMlGANlGAOkWQMqUgMqUgOJkgOIkAOJkAMKkQMJkQNCkgMBkgNCkANAkANCkQMCkQMBkQNEkgMFkgOFkgMEkANlUAOGkQMkUQNlUQOoVgOoVgNpFgMqFAOoFAOoFANZJAMYJAOYJANZpgNYpgMYpgOVoQOVoQNGYQMFYAMEYAMFYgOEYgOGYgOBYQOJEgNJkgNIkgNJkgOJEgNJkgNJkgNJkgOJEgNJkgNJkgNUCAMgGgMWSgNUaQOmQQNRJQMQJQNQJQMpBgOgJgNVCANVCAOiGgMgGgOgGgMWCQMUCQMVaQNphAMRJQNQJQMQJQNRJQMQJQMpBgNoZgMhmAMgGgOgGgMiGgOgGgMgGgOiGgNKQQOYYgNQZgMRJQNQJQMQJQNRJQMQJQNQJQMpBgMpBgOGIANVCANVCAOiGgMgGgOgGgMiGgOgGgMgGgOiGgNZggNZIgOQZANRJQMQJQNQJQMRJQNQJQMQJQNRJQNohAOoBANGIgNUCAOUSAOgGgMiGgOgGgMgGgOiGgMgGgOgGgMiGgNaggNYIAOQZAMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQOqBAOqBAMCYQMUCQMWCQOgGgMgGgOiGgMgGgOgGgMiGgOgGgMgGgOiGgNZggMYIAMRZgNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQOQZANAYANWSgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgMiGgNKQQOaIQMphANRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNqBgORogNKQQMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgMWCQMUCQNYoQMmQQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQNSJQNZoQOWSAOiGgMgGgOgGgMgGgOiGgMgGgOgGgMiGgOgGgMgGgMWSgMSogMpBgNoBgMQJQNQJQMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQMkQAOYIQNimAMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgOgGgOYgAORogMpBgNQJQMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQMpBgOZIANVCAMWCQMiGgOgGgMgGgOgGgMiGgOgGgMgGgOiGgMgGgNWSgNAYAOQZAMQJQNRJQMQJQNQJQMRJQNQJQMQJQNRJQMQJQNphANaIANVCANVCAOiGgMgGgOgGgMgGgOiGgMgGgOgGgMiGgMUCQMWCQOAYQMpBgOqBAMRJQNQJQMQJQNRJQMQJQNQJQMRJQNQJQNphANYIgNWCQMWCQMiGgOgGgMgGgOgGgMiGgOgGgMgGgNUCANVCAMGIgMoBAOqBAMQJQNQJQMRJQNQJQMQJQNRJQMQJQMoBgMaYgNgmAOiGgMgGgOgGgMgGgOiGgMgGgMWCQMWCQMGoQMSZANQJQMRJQNQJQMQJQNRJQMQJQOFggNWaQNWCQOiGgMgGgOgGgMgGgOiGgNWCQNqJQMpBgMpBgMQJQNQJQMRJQNRZgOkiAMWCQNVCAOgGgNUCAOVSAOCRAMARAMARAOCRAMARAMARAOCRAMVQgOaFQOaFQOVQgOEkAMYogNlKgOlagNlKgMoAQNYYgNlEAMmEQNalQOaFQOaFQNKlgNmkgOGKQOlagNlKgNlKgOlagNEKQMoAAOlkgNalQOaFQNalQOaFQOaFQMJFgNQQQMGKQOlagNlKgNlKgOlagNlKgOlagOpQAOkUQOaFQNalQOaFQOaFQNalQOaFQMFkQOSQQOYogOlagNlKgNlKgOlagNlKgOlagNYogNZogMpQAMKlgOaFQOaFQNalQOaFQOaFQNalQOaFQMlEANRAAMZYAOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNiAgOJgAOIFgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQNUAgMRAANYogNZogNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOoAQOAQgMkUQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMlEAMlEAORAAMEqANlKgOlagNlKgNlKgOlagNlKgOlagNlKgMmaAMAQgMWAgNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQMKlgOQAAOZIgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNZogOCQgOZlAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNVQgNQgAOiQgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagMpAQOCQgOkUQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQMlEAMlEANQgANFqANlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOEqAOAQgORAQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOKlgNSgAOYYANlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOZIgMCQgMIVAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMWAgNSgAOaIAMZIgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNYogMYogMAQgOlEAOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQOKVAMSgAOaogOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNGqQMAQgOmUQNkEQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMmkAMSgANiAgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgMYYAOAQgMIFgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMSgAMGKQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgMZIgOaIAMCQgNWQAOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQNmEQMSgANogQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOFaQMAQgMlEAOlEAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNmEQNmEQORggMFqAOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNogQMAQgNkUQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNlUQORggOpgANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNkaAMAQgOWAgOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOmUQOkUQMRggNkaANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOZIgMAQgOKVAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOKlgORggMYYANlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNZogNYogMBQgNmkAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOkUQMkUQORggNkaANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOEaQMAQgMlEAMmEQOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQMKlgORggMYYANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgMqgQNBQgMkUQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOkUQOkUQORggNmaANlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNkaAMAQgOWAgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQOFkQNBQgNZYAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNaIAMAQgNJFgOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOkUQOmUQORggOEqANlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagOZIgOaIANAQgMVQgOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNlUQNBQgMqgQNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNKYgNAQgMZlANalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNmEQNmEQNBQgNFqAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOiQgNAQgOlEAOaFQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNkEQOQggNpgQOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMGKQNBQgMlEAMmEQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMlEAMlEANBQgNEKQNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagNlKgNqgQNBQgMkUQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOlEANAQgOiQgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgMFqANBQgOmUQOkUQNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQOYlANBQgNKYgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMZYAOQggOKlgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNWQANAQgOZIgOZIgNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagMmaANBQgORAQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOKVAMAQgMaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOaYAORggNIlgNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMWAgNAQgOmaAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagORggOWAgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMKlgMAQgNZYAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgOlagMaIAOSgAOIFgOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOmUQNmEQNAQgNGqQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgORggMWAgNalQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNmEQNAQgMpAQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNaIAOSgAOKFgNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMalQNAQgMKYgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgMlagOSgANRgQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNVQgMBQgMZIgOZIgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOYYAMSgAOFkQOaFQOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQNJFgMAQgNaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNkaAMSgAOmUQNkEQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQNRgQNAQgNkaAOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgNlKgMqgQMRggOkUQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMkUQMAQgNogQOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgMGKQMSgAOlEAMlEAOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMlEAOmEQMAQgMFaQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNpAQMSgAMkkAOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMmkAMAQgNZogOZIgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagNlKgOaogNQgANJFgOaFQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNJFgMAQgMaIAOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgMaIAMaIANSgAOWAgOaFQNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQMkUQMmUQMBQgOEqANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNlKgNZYAOSAAOkUQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQMkUQMAQgOoAQNlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgOlagMEKQOQAAOYlAOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMalQMAQgNKYgOlagNlKgOlagNlKgNlKgOlagNlKgNlKgOlagOZIgOZIgOQAAMWAgNalQOaFQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQMVQgMAQgMZIgMaIANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNZYAMQAANmEQNalQOaFQOaFQNalQOaFQOaFQNalQOaFQNalQOIFgOAQgNYYANlKgOlagNlKgNlKgOlagNlKgNlKgOlagNlKgNKYgMQAANKFANalQOaFQOaFQNalQOaFQOaFQNalQOaFQNmEQNmEQMAQgOGKQOlagNlKgOlagNlKgNlKgOlagNlKgNlKgNmaAOSQQOlEAMlEANalQOaFQOaFQNalQOaFQOaFQNalQOaFQOlEAOAQgNYogNYogNlKgOlagNlKgNlKgOlagNlKgNlKgOZIgOZIgOSQQMkUQOmUQOaFQNalQOaFQOaFQNalQOaFQNalQMKlgNoAAMZIgOaIAOlagNlKgNlKgOlagNlKgNlKgOlagNpAQNQQQOWAgOaFQOaFQNalQOaFQOaFQNalQOaFQOmkANpggMFaQNlKgOlagNlKgNlKgOlagNlKgNpAQMmkgMmUQOkUQOaFQNalQOaFQOaFQNalQNaYgMYogNYogNlKgNlKgOlagOaIAMZIgOlUgMIVAOaFQOaFQNalQNWAgNGKANYogNZogOlagMZYAMQgQMgQgNCgYNCQoNCgYMAAMNSVqMAAAMEQgMIgQOYBANkCAOYBAMZBAMmCAMZBAOYBANkCAOYBAOYBANkCAOYBAMZBAMmCAMZBAOYBANkCAOYBAMKBYMAAANoBAOUCANoBAMpBAMWCAMpBANoBAOUCANoBANoBAOUCANoBAMpBAMWCAMpBANoBAOUCANoBAMIQQMEggNCgYNCQoNCgYORSqsAAAMEggMIQQNoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRANoBAOUCANoBAOqRANViAOqRAMIQQMEggNCgYNCQoNCgYNgkKNgkKMAAAMIQQMEggMVCAMqBAMVCAOUCANoBAOUCAMVCAMqBAMVCAMUCAMoBAMUCAMVCAMqBAMVCAOUCANoBAOUCAMVCAMqBAMVCAMUCAMoBAMUCAMFCoMAAAMlCAMaBAMlCAMkCAMYBAMkCANlCAOaBANlCAMkCAMYBAMkCAMlCAMaBAMlCAMkCAMYBAMkCANlCAOaBANlCAMkCAMYBAMkCAMEQgMIgQNCgYNCQoNCgYOQYJOQYJMAAAMEQgMIgQNYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBANYhAOkSANYhAOaBANlCAOaBAMIgQMEQgNCgYNCQoNCgYMgoKMgoKMAAAOIgQNEQgNmiQOZRgNmiQMkiQMYRgMkiQNmiQOZRgNmiQMmiQMZRgMmiQNmiQOZRgNmiQMkiQMYRgMkiQNmiQOZRgNmiQNEQgOIgQNCgYNCQoNCgYNWWYdWWYcAAAOEggNIQQNphgOWSQNphgMohgMUSQMohgNphgOWSQNphgMphgMWSQMphgNphgOWSQNphgMohgMUSQMohgNphgOWSQNphgNIQQOEggNCgYNCQoNCgYMoWqsAAANRgAOiQANiUQORogNiUQMhUAMSoAMhUANiUQORogNiUQMiUQMRogMiUQNhUAOSoANhUANhUAOSoANhUAMiUQMRogMiUQNhUAOSoANhUANgUAOQoANgUAMhUAMSoAMhUANhUAOSoANhUANgUAOQoANgUANhUAOSoANhUAMgUAMQoAMgUANgUAOQoANgUANgUAOQoANgUAMgUAMQoAMgUANiUAORoANiUANgUAOQoANgUAMiUAMRoAMiUANgUAOQoANgUANiUAORoANiUAMiUAMRoAMiUANiUAORoANiUANhUgOSoQNhUgMiUAMRoAMiUANhUgOSoQNhUgNiUAORoANiUAMhUgMSoQMhUgNhUgOSoQNhUgOhQANSgANCgYNCQoNCgYMAAMOgUIMAAANSgQOhQgOFEgNFkANqVgOpVAOqFQNpQQOWggNCgYNCQoNCgYNBIqsAAAOFAgNKAQNAhANSCQNChANQCQMChAORSQOChAMQSQNiRANRSQMiRAOmmgNgBgOkmgMhBgNkmgMgBgNlmgMghgNmGAMihgMkGANZZQMlWAMZZQOlWAOYZQMkWQOYZQNkWQNaJQOKkgMZJAOKkgMZJAOZgQNmQgNCgYNCQoNCgYMAAPM="
                    },
                    "spm": 850,
                    "colors": [
                      "#0b7a4b",
                      "#1f5c3f",
                      "#6d4a33"
                    ],
                    "hoop": "100x100"
                  }
                },
                "example2": {
                  "summary": "Hand-written stitch list with a jump",
                  "value": {
                    "stitches": "[[0,0],[5,0],[10,0],[10,5],[10,10],[40,10,\"jump\"],[40,10],[45,12],[50,10]]",
                    "spm": 600
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithDesignStatsResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/threadsmith-digitize/calculate": {
      "post": {
        "operationId": "calculateThreadsmithDigitize",
        "summary": "Turn shapes, SVG paths, text or a whole project into embroidery stitches with stats and preview.",
        "description": "Digitizes vector artwork into machine embroidery: running, bean, satin (with pull compensation, auto split and short stitches), tatami fill with stagger and graded density, contour fill and motif runs, plus underlay. Returns stitch counts, trims, colour changes, thread and bobbin estimates, sewing time, hoop fit, a density check and a thread-look SVG preview — the same engine as the Threadsmith app.\n\nMethodology:\n- Shapes are flattened to polygons (Bézier curves within 0.04 mm); closed shapes are regions (even-odd, holes allowed), open paths are lines.\n- Tatami: scanlines at the fill angle every row-spacing; intervals are split into blocks that can be sewn back and forth, ordered nearest-first, and penetrations sit on a staggered grid (1/3 offset by default) so no visible columns form.\n- Satin: zig-zag between the edges every density step (rows perpendicular to the shape’s principal axis); columns along lines measure spacing on the outer edge and shorten every other inner stitch on tight curves; stitches longer than the max width are split with interleaved penetrations.\n- Contour fill and edge-walk underlay use an exact Euclidean distance transform of the rasterised shape and marching-squares iso-lines, so insets stay valid for concave shapes and holes.\n- Sequencing: objects grouped by colour (first appearance) and visited nearest-first; gaps up to 1.2 mm are sewn, gaps below the trim distance are jumped, longer gaps get tie-off, trim, jump and tie-in.\n- Thread estimate = sewn length + 0.8 mm take-up per penetration + 5 % waste; bobbin ≈ ⅓ of the sewn length; time = stitches ÷ speed + 4 s per trim + 20 s per colour change.\n\nEstimates assume average fabric and settings; always test-stitch on the real fabric and stabiliser. Thread names are generic colours, not brand shade numbers.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_digitize",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithDigitizeInput"
              },
              "examples": {
                "example1": {
                  "summary": "40 × 30 mm oval, automatic tatami with underlay",
                  "value": {
                    "source": "shape",
                    "shape": "ellipse",
                    "widthMm": 40,
                    "heightMm": 30,
                    "color": "ruby"
                  }
                },
                "example2": {
                  "summary": "Satin column along a curve",
                  "value": {
                    "source": "path",
                    "pathD": "M-30 10 C-15 -15 15 25 30 0",
                    "stitchType": "satin",
                    "satinWidth": 3.5,
                    "spacing": 0.4,
                    "color": "emerald"
                  }
                },
                "example3": {
                  "summary": "Name in single-line satin lettering",
                  "value": {
                    "source": "text",
                    "text": "Ada",
                    "textSize": 14,
                    "stitchType": "satin",
                    "satinWidth": 2,
                    "color": "navy",
                    "includeStitches": true
                  }
                },
                "example4": {
                  "summary": "Club name arched over a badge (text on a 40 mm arc)",
                  "value": {
                    "source": "text",
                    "text": "RIVER CLUB",
                    "textSize": 8,
                    "textArc": 40,
                    "stitchType": "satin",
                    "satinWidth": 1.2,
                    "color": "navy"
                  }
                },
                "example5": {
                  "summary": "SVG badge (circle + square)",
                  "value": {
                    "source": "svg",
                    "svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"60mm\" height=\"60mm\" viewBox=\"0 0 60 60\"><circle cx=\"30\" cy=\"30\" r=\"26\" fill=\"#0f4c81\"/><rect x=\"20\" y=\"20\" width=\"20\" height=\"20\" fill=\"#e4c13b\" stroke=\"#ffffff\" stroke-width=\"1.5\"/></svg>"
                  }
                },
                "example6": {
                  "summary": "Template project: botanical leaf",
                  "value": {
                    "source": "project",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Botanical leaf\",\"hoop\":\"100x100\",\"palette\":[{\"id\":\"emerald\",\"name\":\"Emerald\",\"hex\":\"#0b7a4b\"},{\"id\":\"malachite\",\"name\":\"Malachite\",\"hex\":\"#1f5c3f\"},{\"id\":\"walnut\",\"name\":\"Walnut\",\"hex\":\"#6d4a33\"}],\"objects\":[{\"id\":\"leaf\",\"name\":\"Leaf\",\"kind\":\"path\",\"color\":\"emerald\",\"d\":\"M0 -38C24 -24 26 14 0 36C-26 14 -24 -24 0 -38Z\",\"stitch\":{\"type\":\"tatami\",\"angle\":60,\"spacing\":0.4,\"length\":4,\"stagger\":\"1/3\",\"pullComp\":0.2},\"underlay\":{\"edge\":true,\"tatami\":true,\"inset\":0.5}},{\"id\":\"vein\",\"name\":\"Centre vein\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M0 -32C2 -10 2 12 0 33\",\"stitch\":{\"type\":\"running\",\"length\":2.2,\"bean\":true}},{\"id\":\"veins\",\"name\":\"Side veins\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M1 -18L12 -26M1 -6L15 -13M1 6L15 0M1 18L12 13M-1 -18L-12 -26M-1 -6L-15 -13M-1 6L-15 0M-1 18L-12 13\",\"stitch\":{\"type\":\"running\",\"length\":2,\"bean\":true}},{\"id\":\"stem\",\"name\":\"Stem\",\"kind\":\"path\",\"color\":\"walnut\",\"d\":\"M0 35C1 40 3 43 7 46\",\"stitch\":{\"type\":\"satin\",\"width\":2.2,\"density\":0.35},\"underlay\":{\"center\":true}}]}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithDigitizeResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/threadsmith-stitch-file/calculate": {
      "post": {
        "operationId": "calculateThreadsmithStitchFile",
        "summary": "Write DST, PES, JEF or EXP embroidery machine files from a stitch list or a Threadsmith project.",
        "description": "Encodes stitches as a Tajima DST (512-byte header, balanced-ternary 3-byte records, jumps split to ≤ 12.1 mm, colour changes, trims as zero-sum jumps, END), Brother PES (v1 wrapper around the PEC stitch block with thread palette indices and thumbnails, real trim flags), Janome JEF (header with hoop, extents and Janome thread indices) or Melco EXP file. The file is decoded again to verify every stitch position before you download it.\n\nMethodology:\n- Coordinates are rounded to 0.1 mm on absolute positions, so rounding never accumulates; each move becomes the difference to the previous rounded position.\n- DST: each axis is written in balanced ternary with digits for 1, 3, 9, 27 and 81 (±121 max). Byte 3 = 0x03 | flags: 0x80 jump, 0xC0 colour change; END = 00 00 F3. Longer moves are split into equal records. Y is inverted (DST is y-up).\n- Trims are written as three jumps (+2,+2) (−4,−4) (+2,+2) that cancel out — the convention machines use to trigger a trim after a run of jumps.\n- Header: LA label, ST record count, CO colour changes, ±X/±Y extents in 0.1 mm, AX/AY end offset, MX/MY 0, PD ******, 0x1A, padded with spaces to 512 bytes.\n- EXP: 2 signed bytes per stitch (±127), 0x80 0x04 dx dy jump, 0x80 0x01 colour change; trims as three zero-sum jumps.\n- PES: \"#PES0001\" + PEC offset + empty v1 object section, then the PEC block every Brother/Babylock machine reads: 512-byte header with label, colour count and the nearest of the 64 PEC palette threads per colour block; stitches as 7-bit short or 12-bit long moves (0x1000 jump, 0x2000 trim flag), FE B0 colour change, FF end; 48×38 1-bit thumbnails. Y is down.\n- JEF: 116-byte little-endian header (stitch offset, date, colour and point counts, hoop code, extents, edge distances to the 110×110 / 50×50 / 140×200 / custom hoops) + nearest Janome thread index per colour; stitches as 2 signed bytes (y up), 80 02 jump, 80 01 colour change, 80 10 end; trims as a zero jump.\n\nDST and EXP carry no thread colours; PES and JEF store the nearest colour of the machine’s own thread chart, which is only an approximation. Use the colour list from Threadsmith to thread the needles. Test on scrap fabric first.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "threadsmith_stitch_file",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ThreadsmithStitchFileInput"
              },
              "examples": {
                "example1": {
                  "summary": "Tiny square outline with a jump and a colour change → DST",
                  "value": {
                    "format": "dst",
                    "label": "SQUARE",
                    "stitches": "[[0,0],[10,0],[10,10],[0,10],[0,0],[30,0,\"jump\"],[30,0,\"color\"],[30,0],[40,5],[30,10]]"
                  }
                },
                "example2": {
                  "summary": "Template project (name tag) → Melco EXP",
                  "value": {
                    "format": "exp",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Name tag\",\"hoop\":\"130x180\",\"palette\":[{\"id\":\"sapphire\",\"name\":\"Sapphire\",\"hex\":\"#0f4c81\"},{\"id\":\"coral\",\"name\":\"Coral\",\"hex\":\"#e8705a\"},{\"id\":\"ivory\",\"name\":\"Ivory\",\"hex\":\"#fffbf0\"}],\"objects\":[{\"id\":\"frame\",\"name\":\"Frame\",\"kind\":\"rect\",\"color\":\"sapphire\",\"x\":-45,\"y\":-22,\"w\":90,\"h\":44,\"r\":8,\"stitch\":{\"type\":\"satin\",\"mode\":\"border\",\"width\":3,\"density\":0.4},\"underlay\":{\"center\":true}},{\"id\":\"hello\",\"name\":\"Hello\",\"kind\":\"text\",\"color\":\"coral\",\"text\":\"Hello, my name is\",\"x\":0,\"y\":-7,\"size\":6,\"font\":\"single\",\"align\":\"center\",\"stitch\":{\"type\":\"running\",\"length\":1.8,\"bean\":true}},{\"id\":\"name\",\"name\":\"Name\",\"kind\":\"text\",\"color\":\"sapphire\",\"text\":\"Ada\",\"x\":0,\"y\":14,\"size\":16,\"font\":\"single\",\"align\":\"center\",\"spacing\":1,\"stitch\":{\"type\":\"satin\",\"width\":2.4,\"density\":0.36},\"underlay\":{\"center\":true}}]}"
                  }
                },
                "example3": {
                  "summary": "Template project (botanical leaf) → Brother PES with thread colours",
                  "value": {
                    "format": "pes",
                    "label": "LEAF",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Botanical leaf\",\"hoop\":\"100x100\",\"palette\":[{\"id\":\"emerald\",\"name\":\"Emerald\",\"hex\":\"#0b7a4b\"},{\"id\":\"malachite\",\"name\":\"Malachite\",\"hex\":\"#1f5c3f\"},{\"id\":\"walnut\",\"name\":\"Walnut\",\"hex\":\"#6d4a33\"}],\"objects\":[{\"id\":\"leaf\",\"name\":\"Leaf\",\"kind\":\"path\",\"color\":\"emerald\",\"d\":\"M0 -38C24 -24 26 14 0 36C-26 14 -24 -24 0 -38Z\",\"stitch\":{\"type\":\"tatami\",\"angle\":60,\"spacing\":0.4,\"length\":4,\"stagger\":\"1/3\",\"pullComp\":0.2},\"underlay\":{\"edge\":true,\"tatami\":true,\"inset\":0.5}},{\"id\":\"vein\",\"name\":\"Centre vein\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M0 -32C2 -10 2 12 0 33\",\"stitch\":{\"type\":\"running\",\"length\":2.2,\"bean\":true}},{\"id\":\"veins\",\"name\":\"Side veins\",\"kind\":\"path\",\"color\":\"malachite\",\"d\":\"M1 -18L12 -26M1 -6L15 -13M1 6L15 0M1 18L12 13M-1 -18L-12 -26M-1 -6L-15 -13M-1 6L-15 0M-1 18L-12 13\",\"stitch\":{\"type\":\"running\",\"length\":2,\"bean\":true}},{\"id\":\"stem\",\"name\":\"Stem\",\"kind\":\"path\",\"color\":\"walnut\",\"d\":\"M0 35C1 40 3 43 7 46\",\"stitch\":{\"type\":\"satin\",\"width\":2.2,\"density\":0.35},\"underlay\":{\"center\":true}}]}"
                  }
                },
                "example4": {
                  "summary": "Template project (heart appliqué) → Janome JEF",
                  "value": {
                    "format": "jef",
                    "project": "{\"format\":\"threadsmith\",\"version\":1,\"name\":\"Heart appliqué\",\"hoop\":\"100x100\",\"sequence\":\"manual\",\"palette\":[{\"id\":\"silver\",\"name\":\"Silver grey\",\"hex\":\"#b9bcc0\"},{\"id\":\"magenta\",\"name\":\"Magenta\",\"hex\":\"#b5266f\"},{\"id\":\"plum\",\"name\":\"Plum\",\"hex\":\"#5b2a52\"}],\"objects\":[{\"id\":\"placement\",\"name\":\"Placement line\",\"kind\":\"path\",\"color\":\"silver\",\"d\":\"M0 30C-44 2 -30 -38 0 -16C30 -38 44 2 0 30Z\",\"stitch\":{\"type\":\"running\",\"length\":3}},{\"id\":\"tack\",\"name\":\"Tack-down\",\"kind\":\"path\",\"color\":\"magenta\",\"d\":\"M0 30C-44 2 -30 -38 0 -16C30 -38 44 2 0 30Z\",\"stitch\":{\"type\":\"running\",\"length\":2.5,\"repeats\":1}},{\"id\":\"finish\",\"name\":\"Blanket finish\",\"kind\":\"path\",\"color\":\"plum\",\"d\":\"M0 30C-44 2 -30 -38 0 -16C30 -38 44 2 0 30Z\",\"stitch\":{\"type\":\"motif\",\"motif\":\"estitch\",\"motifSize\":2.5,\"motifLength\":2.5,\"length\":3}},{\"id\":\"love\",\"name\":\"Lettering\",\"kind\":\"text\",\"color\":\"plum\",\"text\":\"love\",\"x\":0,\"y\":4,\"size\":9,\"font\":\"single\",\"align\":\"center\",\"stitch\":{\"type\":\"satin\",\"width\":1.4,\"density\":0.35},\"underlay\":{\"center\":true}}]}"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ThreadsmithStitchFileResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-bottleneck-analysis/calculate": {
      "post": {
        "operationId": "calculateTwinworksBottleneckAnalysis",
        "summary": "Find the constraint of a production line and test fixes: ranked bottlenecks with what-if gains.",
        "description": "Ranks the stations of a Twinworks factory model by how often they constrain the line (active-period method: sole and shifting bottleneck shares), explains the dominant loss (working, breakdowns, setups, waiting for operators) and simulates typical improvements of the top bottleneck — an extra server, a 10 % faster cycle, faster repairs, shorter setups, more operators or a bigger buffer — with paired confidence intervals.\n\nMethodology:\n- Each replication records, per station, every active period (uninterrupted time working, in setup, under repair/maintenance or waiting for an operator; starved and blocked times end a period).\n- At every instant the station with the longest current active period is the bottleneck. Where the periods of consecutive bottlenecks overlap, both are shifting bottlenecks; otherwise the station is the sole bottleneck (Roser, Nakano & Tanaka 2001).\n- Stations are ranked by sole + shifting share (averaged over the window of replication 1, votes = how often a station was the top bottleneck across replications); ties are broken by active percentage.\n- What-if options for the top station are simulated with the same seeds as the baseline (common random numbers); the gain interval is a paired Student-t interval of the throughput differences.\n\nImprovement options are illustrative single changes; real fixes have costs and side effects that the model may not capture.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_bottleneck_analysis",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksBottleneckAnalysisInput"
              },
              "examples": {
                "example1": {
                  "summary": "Bottleneck demo (5 stations, tiny buffers)",
                  "value": {
                    "template": "bottleneck-buffer-sizing",
                    "replications": 5,
                    "horizonHours": 8
                  }
                },
                "example2": {
                  "summary": "Job shop with setups on the mill",
                  "value": {
                    "template": "job-shop-routings",
                    "replications": 4,
                    "whatIf": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksBottleneckAnalysisResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-buffer-sweep/calculate": {
      "post": {
        "operationId": "calculateTwinworksBufferSweep",
        "summary": "Sweep a buffer size (or any parameter) and get the throughput curve with confidence intervals.",
        "description": "Runs a Twinworks factory model repeatedly while one parameter changes — typically the capacity of a buffer from 1 to 10 places, but also servers, cycle times, MTTR, batch sizes, conveyor speed or the number of operators. Returns throughput, lead time and WIP per value with confidence intervals, plus the knee point where extra capacity stops paying off.\n\nMethodology:\n- For each parameter value the model is copied, the parameter is set and the model is simulated with the same seeds (common random numbers), so differences between values are not masked by random noise.\n- Throughput, lead time and WIP are reported as Student-t intervals across replications.\n- Knee point = smallest value whose mean throughput is at least 98 % of the best mean throughput in the sweep: beyond it, more buffer mostly adds WIP and lead time (Little’s law).\n\nThe sweep varies one parameter at a time; interactions between buffers are not optimised jointly.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_buffer_sweep",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksBufferSweepInput"
              },
              "examples": {
                "example1": {
                  "summary": "Bottleneck demo: buffer B2 from 1 to 10",
                  "value": {
                    "template": "bottleneck-buffer-sizing",
                    "target": "B2",
                    "parameter": "capacity",
                    "from": 1,
                    "to": 10,
                    "step": 1,
                    "replications": 4,
                    "horizonHours": 6
                  }
                },
                "example2": {
                  "summary": "3-station line: arrival interval 45…70 s",
                  "value": {
                    "template": "three-station-line",
                    "target": "SRC1",
                    "parameter": "interval.mean",
                    "from": 45,
                    "to": 70,
                    "step": 5,
                    "replications": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksBufferSweepResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-buffer-allocation/calculate": {
      "post": {
        "operationId": "calculateTwinworksBufferAllocation",
        "summary": "Distribute a fixed number of buffer places over a line for maximum throughput.",
        "description": "Solves the buffer allocation problem of a production line by simulation: a fixed total of buffer places (for example the floor space you have) is distributed over the buffers so that throughput is as high as possible. Greedy marginal allocation with common random numbers, compared against the current allocation with a paired confidence interval.\n\nMethodology:\n- Every buffer starts at the minimum capacity; then, one place at a time, each buffer is tried with one more place and the place goes to the buffer with the highest mean throughput (ties: lower mean lead time). This greedy marginal allocation is the standard heuristic for the buffer allocation problem of serial lines.\n- All candidates use the same random-number seeds (common random numbers), so differences are caused by the allocation and not by noise; the gain over the current allocation is a paired Student-t interval.\n\nGreedy allocation is a heuristic: it is optimal or near-optimal for typical serial lines but not guaranteed for every network. Confirm the result with more replications in the app.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_buffer_allocation",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksBufferAllocationInput"
              },
              "examples": {
                "example1": {
                  "summary": "Bottleneck demo: 8 places over four buffers",
                  "value": {
                    "template": "bottleneck-buffer-sizing",
                    "total": 8,
                    "replications": 3,
                    "horizonHours": 6
                  }
                },
                "example2": {
                  "summary": "3-station line: redistribute the current 10 places",
                  "value": {
                    "template": "three-station-line",
                    "replications": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksBufferAllocationResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-simulate/calculate": {
      "post": {
        "operationId": "calculateTwinworksSimulate",
        "summary": "Simulate a production line: throughput, lead time, WIP, OEE and the bottleneck, with CIs.",
        "description": "Runs the Twinworks discrete-event simulation on a factory model (sources, buffers, machines with failures and setups, assembly, conveyors, quality checks, operators and shift calendars). Returns throughput per hour, lead time, WIP with a Little’s-law check, per-station states and OEE, the bottleneck and a cost per good part, with Student-t confidence intervals across seeded replications.\n\nMethodology:\n- Discrete-event simulation with a binary-heap event calendar; ties are processed in (time, priority, insertion order) so every run is reproducible.\n- Parts are pushed downstream with blocking after service: a machine that cannot hand its part on is “blocked”; a machine without parts is “starved/idle”.\n- Each random quantity (arrivals, cycle times, failures, routing choices) has its own seeded stream, so scenario changes do not shift other random numbers (common random numbers).\n- Statistics are reset after the warm-up. Replications use derived seeds; intervals are Student-t intervals of the replication means.\n- Little’s law is checked as L (time-average WIP) versus λ·W (departure rate × mean time in system); differences above 5 % signal a non-stationary system or too short a run.\n- OEE = availability × performance × quality with planned time = time on shift minus planned maintenance; availability losses = breakdowns + setups; performance = ideal cycle time × parts ÷ run time (starving and blocking reduce it); quality = good ÷ processed.\n- Bottleneck: active-period method (Roser et al.) — at each moment the station with the longest uninterrupted active period (working, setup, repair, maintenance, waiting for an operator) is the bottleneck; overlaps between consecutive bottlenecks count as shifting.\n\nA simulation is only as good as its input data. Validate cycle times, failure data and routings against your shop floor before making investment decisions.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_simulate",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksSimulateInput"
              },
              "examples": {
                "example1": {
                  "summary": "3-station line template, 5 replications",
                  "value": {
                    "template": "three-station-line",
                    "replications": 5
                  }
                },
                "example2": {
                  "summary": "Own model: two machines and a buffer, deterministic",
                  "value": {
                    "model": "{\"name\":\"Two machines\",\"settings\":{\"horizon\":7200,\"warmup\":600},\"nodes\":[{\"id\":\"S\",\"type\":\"source\",\"arrival\":\"saturated\"},{\"id\":\"M1\",\"type\":\"machine\",\"cycle\":{\"type\":\"constant\",\"value\":30}},{\"id\":\"B\",\"type\":\"buffer\",\"capacity\":3},{\"id\":\"M2\",\"type\":\"machine\",\"cycle\":{\"type\":\"constant\",\"value\":40}},{\"id\":\"OUT\",\"type\":\"sink\"}],\"links\":[{\"from\":\"S\",\"to\":\"M1\"},{\"from\":\"M1\",\"to\":\"B\"},{\"from\":\"B\",\"to\":\"M2\"},{\"from\":\"M2\",\"to\":\"OUT\"}]}",
                    "replications": 1
                  }
                },
                "example3": {
                  "summary": "Line with failures over 2 days, 3 replications",
                  "value": {
                    "template": "line-with-failures",
                    "horizonHours": 48,
                    "warmupMinutes": 120,
                    "replications": 3
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksSimulateResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-line-balance/calculate": {
      "post": {
        "operationId": "calculateTwinworksLineBalance",
        "summary": "Assign assembly tasks to stations for a takt time: efficiency, balance delay and smoothness index.",
        "description": "Solves the simple assembly line balancing problem (type 1) with the Ranked Positional Weight (Helgeson–Birnie) and Largest Candidate rules: tasks with times and precedence relations are assigned to as few stations as possible without exceeding the takt time. Returns the station assignment, line efficiency, balance delay, smoothness index and a ready-to-simulate Twinworks model of the balanced line.\n\nMethodology:\n- Takt time = available production time ÷ demand (or as given). Theoretical minimum stations = ⌈Σ task times ÷ takt⌉.\n- RPW: positional weight = own time + times of all direct and indirect successors; LCR: task time. Stations are filled one by one with the highest-priority task whose predecessors are all assigned and that still fits into the remaining takt time.\n- Efficiency = Σ task times ÷ (stations × takt); balance delay = 1 − efficiency; smoothness index = √Σ(max station time − station time)².\n- The result includes a Twinworks model (one constant-time machine per station, single-place buffers) so the balance can be simulated or opened in the app.\n\nHeuristics give good but not always optimal balances. Zoning constraints, operator skills and task variability are not considered.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_line_balance",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksLineBalanceInput"
              },
              "examples": {
                "example1": {
                  "summary": "Wheelbarrow-style example: 8 tasks, 450 min shift, demand 270",
                  "value": {
                    "tasks": [
                      {
                        "id": "A",
                        "name": "Fit frame",
                        "time": 60,
                        "predecessors": ""
                      },
                      {
                        "id": "B",
                        "name": "Mount axle",
                        "time": 40,
                        "predecessors": "A"
                      },
                      {
                        "id": "C",
                        "name": "Fit wheel",
                        "time": 55,
                        "predecessors": "B"
                      },
                      {
                        "id": "D",
                        "name": "Attach tray",
                        "time": 50,
                        "predecessors": "A"
                      },
                      {
                        "id": "E",
                        "name": "Fit legs",
                        "time": 30,
                        "predecessors": "D"
                      },
                      {
                        "id": "F",
                        "name": "Attach handles",
                        "time": 45,
                        "predecessors": "D"
                      },
                      {
                        "id": "G",
                        "name": "Grips",
                        "time": 20,
                        "predecessors": "F"
                      },
                      {
                        "id": "H",
                        "name": "Final check",
                        "time": 35,
                        "predecessors": "C,E,G"
                      }
                    ],
                    "availableMinutes": 450,
                    "demand": 270,
                    "method": "best"
                  }
                },
                "example2": {
                  "summary": "Six tasks with a fixed takt time of 10 s",
                  "value": {
                    "tasks": [
                      {
                        "id": "1",
                        "time": 4,
                        "predecessors": ""
                      },
                      {
                        "id": "2",
                        "time": 3,
                        "predecessors": "1"
                      },
                      {
                        "id": "3",
                        "time": 5,
                        "predecessors": "1"
                      },
                      {
                        "id": "4",
                        "time": 6,
                        "predecessors": "2"
                      },
                      {
                        "id": "5",
                        "time": 2,
                        "predecessors": "3"
                      },
                      {
                        "id": "6",
                        "time": 4,
                        "predecessors": "4,5"
                      }
                    ],
                    "taktTime": 10,
                    "method": "rpw"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksLineBalanceResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tools/twinworks-queue-formulas/calculate": {
      "post": {
        "operationId": "calculateTwinworksQueueFormulas",
        "summary": "M/M/1, M/M/c, M/D/1, M/G/1 and Kingman waiting times — optionally verified by simulation.",
        "description": "Closed-form queueing results for a single workstation: utilization, mean waiting time, queue length and time in system for M/M/1, M/M/c (Erlang C), M/D/1, M/G/1 (Pollaczek–Khinchine) and the Kingman G/G/1 approximation. Optionally runs the Twinworks simulation of the same station and reports whether the theory lies inside the simulated confidence interval.\n\nMethodology:\n- M/M/1: Wq = ρ/(μ−λ). M/M/c: Wq = C(c, a)/(cμ−λ) with the Erlang C probability C computed by a stable recursion. M/G/1: Wq = λE[S²]/(2(1−ρ)) (Pollaczek–Khinchine), M/D/1 is the special case SCV = 0. Kingman: Wq ≈ ((ca²+cs²)/2)·(ρ/(1−ρ))·te.\n- L = λW and Lq = λWq (Little’s law).\n- Verification builds a source → unlimited queue → station → sink model, simulates long replications (≈ 20 000 arrivals each, 5 % warm-up) and checks whether the theoretical Wq lies inside the Student-t interval of the simulated mean wait.\n\nFormulas assume steady state, infinite queue capacity and FIFO service. Kingman is an approximation.",
        "tags": [
          "apps"
        ],
        "x-mcp-tool": "twinworks_queue_formulas",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TwinworksQueueFormulasInput"
              },
              "examples": {
                "example1": {
                  "summary": "M/M/1 at 80 % utilization, verified by simulation",
                  "value": {
                    "queue": "mm1",
                    "arrivalsPerHour": 60,
                    "serviceTime": 48,
                    "verify": true,
                    "replications": 10
                  }
                },
                "example2": {
                  "summary": "Two operators (M/M/2)",
                  "value": {
                    "queue": "mmc",
                    "arrivalsPerHour": 100,
                    "serviceTime": 60,
                    "servers": 2
                  }
                },
                "example3": {
                  "summary": "Kingman: low arrival variability, high process variability",
                  "value": {
                    "queue": "gg1",
                    "arrivalsPerHour": 50,
                    "serviceTime": 60,
                    "arrivalScv": 0.25,
                    "serviceScv": 1.5
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TwinworksQueueFormulasResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/workflows": {
      "get": {
        "operationId": "listWorkflows",
        "summary": "Multi-tool workflows",
        "responses": {
          "200": {
            "description": "Workflow list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "FliplineExposureSheetInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "holds": {
            "title": "Drawing holds (ticks, separated by commas)",
            "type": "string",
            "maxLength": 120,
            "default": "2,2,1,1,2,2"
          },
          "fps": {
            "title": "Timeline frame rate (FPS)",
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 12
          }
        },
        "required": []
      },
      "FliplineExposureSheetResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "fps",
              "drawings",
              "ticks",
              "durationMs",
              "gifDurationMs",
              "frames"
            ],
            "properties": {
              "fps": {
                "type": "integer"
              },
              "drawings": {
                "type": "integer"
              },
              "ticks": {
                "type": "integer"
              },
              "durationMs": {
                "type": "number"
              },
              "gifDurationMs": {
                "type": "number"
              },
              "frames": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "drawing",
                    "hold",
                    "startTick",
                    "endTick",
                    "startMs",
                    "durationMs",
                    "gifDurationMs"
                  ],
                  "properties": {
                    "drawing": {
                      "type": "integer"
                    },
                    "hold": {
                      "type": "integer"
                    },
                    "startTick": {
                      "type": "integer"
                    },
                    "endTick": {
                      "type": "integer"
                    },
                    "startMs": {
                      "type": "number"
                    },
                    "durationMs": {
                      "type": "number"
                    },
                    "gifDurationMs": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplineMotionStudyInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "count": {
            "title": "Drawing count",
            "type": "integer",
            "minimum": 2,
            "maximum": 24,
            "default": 12
          },
          "fps": {
            "title": "Frame rate (FPS)",
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 12
          },
          "ease": {
            "title": "Spacing curve",
            "type": "string",
            "enum": [
              "linear",
              "ease-in",
              "ease-out",
              "ease-in-out"
            ],
            "default": "ease-in-out"
          },
          "fromX": {
            "title": "Start X (%)",
            "type": "number",
            "minimum": 8,
            "maximum": 92,
            "default": 15
          },
          "fromY": {
            "title": "Start Y (%)",
            "type": "number",
            "minimum": 8,
            "maximum": 92,
            "default": 50
          },
          "toX": {
            "title": "End X (%)",
            "type": "number",
            "minimum": 8,
            "maximum": 92,
            "default": 85
          },
          "toY": {
            "title": "End Y (%)",
            "type": "number",
            "minimum": 8,
            "maximum": 92,
            "default": 50
          }
        },
        "required": []
      },
      "FliplineMotionStudyResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "project"
            ],
            "properties": {
              "project": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "name",
                  "width",
                  "height",
                  "fps",
                  "background",
                  "transparent",
                  "frames",
                  "layers"
                ],
                "properties": {
                  "format": {
                    "const": "flipline"
                  },
                  "version": {
                    "const": 1
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "width": {
                    "type": "integer",
                    "minimum": 32,
                    "maximum": 1600
                  },
                  "height": {
                    "type": "integer",
                    "minimum": 32,
                    "maximum": 1600
                  },
                  "fps": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "background": {
                    "type": "string",
                    "pattern": "^#[a-fA-F0-9]{6}$"
                  },
                  "transparent": {
                    "type": "boolean"
                  },
                  "frames": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 24,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "hold",
                        "label"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 80
                        },
                        "hold": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 10
                        },
                        "label": {
                          "type": "string",
                          "maxLength": 80
                        }
                      }
                    }
                  },
                  "layers": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 4,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "id",
                        "name",
                        "visible",
                        "locked",
                        "opacity",
                        "cels"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 80
                        },
                        "name": {
                          "type": "string",
                          "maxLength": 80
                        },
                        "visible": {
                          "type": "boolean"
                        },
                        "locked": {
                          "type": "boolean"
                        },
                        "opacity": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "cels": {
                          "type": "array",
                          "minItems": 1,
                          "maxItems": 24,
                          "items": {
                            "type": "array",
                            "maxItems": 500,
                            "items": {
                              "type": "object",
                              "additionalProperties": false,
                              "required": [
                                "id",
                                "kind",
                                "points",
                                "color",
                                "size",
                                "fill"
                              ],
                              "properties": {
                                "id": {
                                  "type": "string",
                                  "minLength": 1,
                                  "maxLength": 80
                                },
                                "kind": {
                                  "enum": [
                                    "pen",
                                    "eraser",
                                    "line",
                                    "rect",
                                    "ellipse",
                                    "image"
                                  ]
                                },
                                "points": {
                                  "type": "array",
                                  "minItems": 1,
                                  "maxItems": 4096,
                                  "items": {
                                    "type": "array",
                                    "minItems": 2,
                                    "maxItems": 2,
                                    "items": {
                                      "type": "number",
                                      "minimum": 0,
                                      "maximum": 1
                                    }
                                  }
                                },
                                "color": {
                                  "type": "string",
                                  "pattern": "^#[a-fA-F0-9]{6}$"
                                },
                                "size": {
                                  "type": "number",
                                  "minimum": 1,
                                  "maximum": 100
                                },
                                "fill": {
                                  "type": "boolean"
                                },
                                "source": {
                                  "type": "string",
                                  "pattern": "^data:image/(png|jpeg|webp);base64,[A-Za-z0-9+/=]+$"
                                },
                                "angle": {
                                  "type": "number",
                                  "minimum": -360,
                                  "maximum": 360,
                                  "description": "Clockwise rotation in degrees around the center of the mark bounds (normalized to -180…180)."
                                },
                                "flipX": {
                                  "type": "boolean",
                                  "description": "Mirror horizontally around the mark center."
                                },
                                "flipY": {
                                  "type": "boolean",
                                  "description": "Mirror vertically around the mark center."
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "description": "Normalized drawing coordinates. Each layer has one cel per frame. At most 2 MP canvas, 50000 points, 2000 marks and 8 million embedded source characters in total. Image/shape marks require two points; IDs are unique within their collection. Optional angle/flipX/flipY transform a mark around the center of its point bounds."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaImageSplitterInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "Single photo (PNG, JPEG, WebP or GIF)",
            "description": "Up to 24 PNG, JPEG, WebP or GIF images (first frame). ZIP order is preserved. Decoded total: 32 MP; each photo: 16 MP. Upload as {filename, contentType?, contentBase64}. Max 78125 KiB. Accepts .zip,.png,.jpg,.jpeg,.webp,.gif.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "rows": {
            "title": "Rows",
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 3
          },
          "columns": {
            "title": "Columns",
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 3
          }
        },
        "required": [
          "images"
        ]
      },
      "LuminaImageSplitterResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "tiles",
              "artifact"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "tiles": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaImageMaskInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "image": {
            "title": "Photo (PNG, JPEG, WebP or GIF)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 11719 KiB. Accepts .png,.jpg,.jpeg,.webp,.gif.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "shape": {
            "title": "Mask shape",
            "type": "string",
            "enum": [
              "ellipse",
              "rect"
            ],
            "default": "ellipse"
          },
          "x": {
            "title": "Mask left (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 99,
            "default": 0
          },
          "y": {
            "title": "Mask top (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 99,
            "default": 0
          },
          "width": {
            "title": "Mask width (%)",
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 100
          },
          "height": {
            "title": "Mask height (%)",
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 100
          },
          "feather": {
            "title": "Inward feather (normalized %)",
            "type": "number",
            "minimum": 0,
            "maximum": 25,
            "default": 0
          },
          "invert": {
            "title": "Invert mask",
            "type": "boolean",
            "default": false
          }
        },
        "required": [
          "image"
        ]
      },
      "LuminaImageMaskResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "artifact"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KerfwiseCabinetBuilderInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "type": {
            "title": "Cabinet type",
            "type": "string",
            "enum": [
              "base",
              "wall",
              "tall",
              "drawer",
              "corner",
              "diagonal",
              "diagonal-wall",
              "l-corner",
              "angled-end",
              "round-end",
              "open",
              "wardrobe",
              "bookshelf",
              "desk",
              "table",
              "bed"
            ],
            "default": "base"
          },
          "width": {
            "title": "Width",
            "description": "Outer width (table/desk: top length).",
            "type": "number",
            "minimum": 150,
            "maximum": 4000,
            "default": 600,
            "x-unit": "mm"
          },
          "height": {
            "title": "Height",
            "description": "Outer height incl. plinth, without worktop. Empty = type default.",
            "type": "number",
            "minimum": 150,
            "maximum": 3000,
            "x-unit": "mm"
          },
          "depth": {
            "title": "Depth",
            "description": "Outer depth. Empty = type default.",
            "type": "number",
            "minimum": 100,
            "maximum": 2500,
            "x-unit": "mm"
          },
          "materialId": {
            "title": "Carcass material",
            "type": "string",
            "enum": [
              "mel-18",
              "oak-18",
              "birch-15",
              "mdf-19",
              "walnut-19",
              "oakpanel-27",
              "hdf-8",
              "hdf-3",
              "wt-slab-20"
            ],
            "default": "mel-18"
          },
          "frontMaterialId": {
            "title": "Front material",
            "type": "string",
            "enum": [
              "mel-18",
              "oak-18",
              "birch-15",
              "mdf-19",
              "walnut-19",
              "oakpanel-27",
              "hdf-8",
              "hdf-3",
              "wt-slab-20"
            ],
            "default": "oak-18"
          },
          "backType": {
            "title": "Back panel",
            "type": "string",
            "enum": [
              "groove",
              "inset",
              "nailed",
              "none"
            ],
            "default": "groove"
          },
          "grooveDepth": {
            "title": "Groove depth",
            "type": "number",
            "minimum": 0,
            "maximum": 20,
            "default": 8,
            "x-unit": "mm"
          },
          "topType": {
            "title": "Top",
            "type": "string",
            "enum": [
              "full",
              "rails"
            ],
            "default": "full"
          },
          "plinthType": {
            "title": "Plinth / toe kick",
            "type": "string",
            "enum": [
              "legs",
              "board",
              "none"
            ],
            "default": "legs"
          },
          "plinthHeight": {
            "title": "Plinth height",
            "type": "number",
            "minimum": 0,
            "maximum": 300,
            "default": 150,
            "x-unit": "mm"
          },
          "doorCount": {
            "title": "Doors",
            "description": "Empty = type default.",
            "type": "integer",
            "minimum": 0,
            "maximum": 4
          },
          "doorStyle": {
            "title": "Door style",
            "type": "string",
            "enum": [
              "overlay",
              "inset"
            ],
            "default": "overlay"
          },
          "drawerCount": {
            "title": "Drawers",
            "description": "Empty = type default. With doors, drawers go on top (150 mm each).",
            "type": "integer",
            "minimum": 0,
            "maximum": 8
          },
          "slideType": {
            "title": "Drawer slides",
            "type": "string",
            "enum": [
              "side",
              "under"
            ],
            "default": "side"
          },
          "shelfCount": {
            "title": "Shelves",
            "description": "Empty = type default.",
            "type": "integer",
            "minimum": 0,
            "maximum": 12
          },
          "adjustableShelves": {
            "title": "Adjustable shelves (32 mm system holes)",
            "type": "boolean",
            "default": true
          },
          "faceFrame": {
            "title": "Face frame",
            "type": "boolean",
            "default": false
          },
          "carcassJoint": {
            "title": "Carcass joinery",
            "type": "string",
            "enum": [
              "butt",
              "dowel",
              "confirmat",
              "pocket",
              "cam",
              "dado",
              "rabbet"
            ],
            "default": "dowel"
          },
          "frameJoint": {
            "title": "Frame / apron joinery",
            "type": "string",
            "enum": [
              "pocket",
              "dowel",
              "mortise-tenon",
              "butt"
            ],
            "default": "pocket"
          },
          "drawerJoint": {
            "title": "Drawer box joinery",
            "type": "string",
            "enum": [
              "butt",
              "dado",
              "dovetail",
              "dowel"
            ],
            "default": "dado"
          },
          "handle": {
            "title": "Handles",
            "type": "string",
            "enum": [
              "bar128",
              "bar160",
              "knob",
              "cup",
              "profile",
              "push",
              "none"
            ],
            "default": "bar128"
          },
          "returnWidth": {
            "title": "Corner: second leg length",
            "description": "Diagonal and L-shaped corner cabinets: length along the adjoining wall. Empty = same as the width.",
            "type": "number",
            "minimum": 300,
            "maximum": 2000,
            "x-unit": "mm"
          },
          "cornerDoors": {
            "title": "L-shaped corner doors",
            "type": "string",
            "enum": [
              "bifold",
              "two"
            ],
            "default": "bifold"
          },
          "endAngle": {
            "title": "Angled end: angle to the wall",
            "type": "number",
            "minimum": 30,
            "maximum": 90,
            "default": 60,
            "x-unit": "°"
          },
          "bandingDeduct": {
            "title": "Deduct edge-banding thickness from cut sizes",
            "type": "boolean",
            "default": true
          }
        },
        "required": [
          "width"
        ]
      },
      "KerfwiseCabinetBuilderResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "type",
              "size",
              "dimensions",
              "parts",
              "hardware",
              "totals",
              "warnings",
              "project",
              "openInApp"
            ],
            "properties": {
              "type": {
                "type": "string"
              },
              "size": {
                "type": "object",
                "properties": {
                  "w": {
                    "type": "number"
                  },
                  "h": {
                    "type": "number"
                  },
                  "d": {
                    "type": "number"
                  }
                }
              },
              "dimensions": {
                "type": "object",
                "description": "Key derived dimensions (carcass height, inner sizes, door/back sizes, slide length …)."
              },
              "parts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "label",
                    "name",
                    "L",
                    "W",
                    "T",
                    "cutL",
                    "cutW",
                    "material",
                    "grain",
                    "edges",
                    "holes"
                  ],
                  "properties": {
                    "label": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "group": {
                      "type": "string"
                    },
                    "L": {
                      "type": "number"
                    },
                    "W": {
                      "type": "number"
                    },
                    "T": {
                      "type": "number"
                    },
                    "cutL": {
                      "type": "number"
                    },
                    "cutW": {
                      "type": "number"
                    },
                    "material": {
                      "type": "string"
                    },
                    "grain": {
                      "type": "boolean"
                    },
                    "edges": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "holes": {
                      "type": "integer"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "shape": {
                      "enum": [
                        "rect",
                        "polygon"
                      ]
                    },
                    "outline": {
                      "type": "array",
                      "description": "Cut outline of shaped parts [u, v] in mm (u from the W1 end, v from the L1 edge).",
                      "items": {
                        "type": "array",
                        "items": {
                          "type": "number"
                        }
                      }
                    }
                  }
                }
              },
              "hardware": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "qty": {
                      "type": "number"
                    },
                    "unit": {
                      "type": "string"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "totals": {
                "type": "object",
                "properties": {
                  "parts": {
                    "type": "integer"
                  },
                  "holes": {
                    "type": "integer"
                  },
                  "areaM2": {
                    "type": "number"
                  },
                  "bandingM": {
                    "type": "number"
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "project": {
                "type": "object",
                "description": "Kerfwise project JSON containing this cabinet (open it in the app or pass it to kerfwise-cut-list)."
              },
              "openInApp": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KerfwiseCncGcodeInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "parts": {
            "title": "Parts",
            "description": "Finished cut sizes. Length runs along the grain.",
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 60
                },
                "length": {
                  "title": "Length (mm)",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 10000
                },
                "width": {
                  "title": "Width (mm)",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 5000
                },
                "qty": {
                  "title": "Qty",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 200
                },
                "grain": {
                  "title": "Grain",
                  "type": "boolean"
                },
                "shape": {
                  "title": "Shape",
                  "type": "string",
                  "enum": [
                    "rect",
                    "l",
                    "chamfer",
                    "quarter"
                  ]
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 10000
                },
                "b": {
                  "title": "b (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5000
                }
              },
              "required": [
                "name",
                "length",
                "width"
              ]
            }
          },
          "holes": {
            "title": "Holes",
            "description": "Part = row number in the parts table; x along the length, y across, from the part’s first corner. Depth ≥ thickness = through.",
            "type": "array",
            "maxItems": 400,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "part": {
                  "title": "Part row",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 200
                },
                "x": {
                  "title": "x (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 10000
                },
                "y": {
                  "title": "y (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5000
                },
                "diameter": {
                  "title": "Ø (mm)",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 100
                },
                "depth": {
                  "title": "Depth (mm)",
                  "type": "number",
                  "minimum": 0.5,
                  "maximum": 100
                }
              },
              "required": [
                "part",
                "x",
                "y",
                "diameter",
                "depth"
              ]
            }
          },
          "sheetLength": {
            "title": "Sheet length",
            "type": "number",
            "minimum": 100,
            "maximum": 10000,
            "default": 2800,
            "x-unit": "mm"
          },
          "sheetWidth": {
            "title": "Sheet width",
            "type": "number",
            "minimum": 100,
            "maximum": 5000,
            "default": 2070,
            "x-unit": "mm"
          },
          "thickness": {
            "title": "Material thickness",
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 18,
            "x-unit": "mm"
          },
          "trim": {
            "title": "Margin to the sheet edge",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 15,
            "x-unit": "mm"
          },
          "grainedMaterial": {
            "title": "Material has grain",
            "type": "boolean",
            "default": false
          },
          "dialect": {
            "title": "Controller",
            "type": "string",
            "enum": [
              "grbl",
              "linuxcnc",
              "fanuc"
            ],
            "default": "grbl"
          },
          "origin": {
            "title": "Work zero (X/Y)",
            "type": "string",
            "enum": [
              "bottom-left",
              "top-left"
            ],
            "default": "bottom-left"
          },
          "toolDiameter": {
            "title": "End mill diameter",
            "type": "number",
            "minimum": 1,
            "maximum": 25,
            "default": 6,
            "x-unit": "mm"
          },
          "feed": {
            "title": "Cutting feed",
            "type": "number",
            "minimum": 100,
            "maximum": 50000,
            "default": 6000,
            "x-unit": "mm/min"
          },
          "plunge": {
            "title": "Plunge feed",
            "type": "number",
            "minimum": 50,
            "maximum": 20000,
            "default": 1500,
            "x-unit": "mm/min"
          },
          "rpm": {
            "title": "Spindle speed",
            "type": "integer",
            "minimum": 1000,
            "maximum": 30000,
            "default": 18000,
            "x-unit": "rpm"
          },
          "stepdown": {
            "title": "Depth per pass",
            "type": "number",
            "minimum": 0.5,
            "maximum": 50,
            "default": 6,
            "x-unit": "mm"
          },
          "holdDown": {
            "title": "Hold-down",
            "type": "string",
            "enum": [
              "tabs",
              "onion",
              "none"
            ],
            "default": "tabs"
          },
          "direction": {
            "title": "Cut direction",
            "type": "string",
            "enum": [
              "climb",
              "conventional"
            ],
            "default": "climb"
          },
          "safeZ": {
            "title": "Safe Z",
            "type": "number",
            "minimum": 2,
            "maximum": 100,
            "default": 15,
            "x-unit": "mm"
          },
          "sheetIndex": {
            "title": "Sheet to show",
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 1
          }
        },
        "required": [
          "parts"
        ]
      },
      "KerfwiseCncGcodeResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "sheetCount",
              "sheet",
              "gcode",
              "stats"
            ],
            "properties": {
              "sheetCount": {
                "type": "integer"
              },
              "sheet": {
                "type": "integer"
              },
              "dialect": {
                "type": "string"
              },
              "gcode": {
                "type": "string",
                "description": "G-code of the shown sheet."
              },
              "stats": {
                "type": "object",
                "properties": {
                  "feedLength": {
                    "type": "number"
                  },
                  "rapidLength": {
                    "type": "number"
                  },
                  "holes": {
                    "type": "integer"
                  },
                  "bores": {
                    "type": "integer"
                  },
                  "profiles": {
                    "type": "integer"
                  },
                  "toolChanges": {
                    "type": "integer"
                  },
                  "passes": {
                    "type": "integer"
                  },
                  "tabs": {
                    "type": "integer"
                  },
                  "timeSec": {
                    "type": "number"
                  },
                  "lines": {
                    "type": "integer"
                  }
                }
              },
              "sheetsTimeSec": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "tools": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "diameter": {
                      "type": "number"
                    }
                  }
                }
              },
              "bounds": {
                "type": "object",
                "properties": {
                  "x0": {
                    "type": "number"
                  },
                  "y0": {
                    "type": "number"
                  },
                  "x1": {
                    "type": "number"
                  },
                  "y1": {
                    "type": "number"
                  },
                  "zMin": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KerfwiseCutListInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "project": {
            "title": "Kerfwise project JSON (optional)",
            "description": "Paste a .kerfwise.json file. When given, the cabinet table below is ignored.",
            "type": "string",
            "maxLength": 400000
          },
          "cabinets": {
            "title": "Cabinets",
            "description": "Quick input without a project file. Empty height/depth/doors/drawers/shelves use the type defaults.",
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "base",
                    "wall",
                    "tall",
                    "drawer",
                    "corner",
                    "diagonal",
                    "diagonal-wall",
                    "l-corner",
                    "angled-end",
                    "round-end",
                    "open",
                    "wardrobe",
                    "bookshelf",
                    "desk",
                    "table",
                    "bed"
                  ]
                },
                "width": {
                  "title": "Width (mm)",
                  "type": "number",
                  "minimum": 150,
                  "maximum": 4000
                },
                "height": {
                  "title": "Height (mm)",
                  "type": "number",
                  "minimum": 150,
                  "maximum": 3000
                },
                "depth": {
                  "title": "Depth (mm)",
                  "type": "number",
                  "minimum": 100,
                  "maximum": 2500
                },
                "doors": {
                  "title": "Doors",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 4
                },
                "drawers": {
                  "title": "Drawers",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 8
                },
                "shelves": {
                  "title": "Shelves",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 12
                },
                "qty": {
                  "title": "Qty",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 50
                }
              },
              "required": [
                "type",
                "width"
              ]
            }
          },
          "materialId": {
            "title": "Carcass material (table input)",
            "type": "string",
            "enum": [
              "mel-18",
              "oak-18",
              "birch-15",
              "mdf-19",
              "walnut-19",
              "oakpanel-27",
              "hdf-8",
              "hdf-3",
              "wt-slab-20"
            ],
            "default": "mel-18"
          },
          "frontMaterialId": {
            "title": "Front material (table input)",
            "type": "string",
            "enum": [
              "mel-18",
              "oak-18",
              "birch-15",
              "mdf-19",
              "walnut-19",
              "oakpanel-27",
              "hdf-8",
              "hdf-3",
              "wt-slab-20"
            ],
            "default": "oak-18"
          },
          "currency": {
            "title": "Currency",
            "type": "string",
            "maxLength": 8,
            "default": "EUR"
          },
          "kerf": {
            "title": "Saw kerf",
            "type": "number",
            "minimum": 0,
            "maximum": 10,
            "default": 4,
            "x-unit": "mm"
          },
          "trim": {
            "title": "Sheet trim margin",
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "default": 10,
            "x-unit": "mm"
          },
          "labourRate": {
            "title": "Labour rate",
            "description": "Per hour, in the chosen currency.",
            "type": "number",
            "minimum": 0,
            "maximum": 1000,
            "default": 45
          },
          "costMode": {
            "title": "Material cost",
            "type": "string",
            "enum": [
              "full-sheets",
              "used-area"
            ],
            "default": "full-sheets"
          },
          "markupPct": {
            "title": "Markup",
            "type": "number",
            "minimum": 0,
            "maximum": 500,
            "default": 0,
            "x-unit": "%"
          },
          "taxPct": {
            "title": "Tax / VAT",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 0,
            "x-unit": "%"
          },
          "worktopJoint": {
            "title": "Worktop corner joint",
            "description": "Empty = the project setting (butt by default).",
            "type": "string",
            "enum": [
              "butt",
              "mitre",
              "one-piece"
            ]
          },
          "nestingEffort": {
            "title": "Nesting effort",
            "description": "Empty = the project setting.",
            "type": "string",
            "enum": [
              "fast",
              "deep"
            ]
          }
        },
        "required": []
      },
      "KerfwiseCutListResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "projectName",
              "rows",
              "banding",
              "hardware",
              "materials",
              "cost",
              "totals",
              "warnings"
            ],
            "properties": {
              "projectName": {
                "type": "string"
              },
              "rows": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "no",
                    "name",
                    "qty",
                    "L",
                    "W",
                    "T",
                    "cutL",
                    "cutW",
                    "material"
                  ],
                  "properties": {
                    "no": {
                      "type": "integer"
                    },
                    "labels": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "name": {
                      "type": "string"
                    },
                    "cabinets": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "qty": {
                      "type": "integer"
                    },
                    "L": {
                      "type": "number"
                    },
                    "W": {
                      "type": "number"
                    },
                    "T": {
                      "type": "number"
                    },
                    "cutL": {
                      "type": "number"
                    },
                    "cutW": {
                      "type": "number"
                    },
                    "material": {
                      "type": "string"
                    },
                    "grain": {
                      "type": "boolean"
                    },
                    "edges": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "banding": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "metres": {
                      "type": "number"
                    },
                    "cost": {
                      "type": "number"
                    }
                  }
                }
              },
              "hardware": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "qty": {
                      "type": "number"
                    },
                    "unit": {
                      "type": "string"
                    },
                    "cost": {
                      "type": "number"
                    }
                  }
                }
              },
              "materials": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "material": {
                      "type": "string"
                    },
                    "kind": {
                      "type": "string"
                    },
                    "stock": {
                      "type": "integer"
                    },
                    "yieldPct": {
                      "type": "number"
                    },
                    "cost": {
                      "type": "number"
                    },
                    "lowerBound": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "gap": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "provenOptimal": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "worktops": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "cost": {
                "type": "object",
                "properties": {
                  "materials": {
                    "type": "number"
                  },
                  "banding": {
                    "type": "number"
                  },
                  "hardware": {
                    "type": "number"
                  },
                  "labour": {
                    "type": "number"
                  },
                  "labourMinutes": {
                    "type": "number"
                  },
                  "extras": {
                    "type": "number"
                  },
                  "subtotal": {
                    "type": "number"
                  },
                  "markup": {
                    "type": "number"
                  },
                  "tax": {
                    "type": "number"
                  },
                  "total": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string"
                  }
                }
              },
              "totals": {
                "type": "object"
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KerfwiseSheetNestingInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "parts": {
            "title": "Parts",
            "description": "Length runs along the grain. Tick grain to forbid rotation (on a grained material).",
            "type": "array",
            "maxItems": 300,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 60
                },
                "length": {
                  "title": "Length (mm)",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 10000
                },
                "width": {
                  "title": "Width (mm)",
                  "type": "number",
                  "minimum": 1,
                  "maximum": 5000
                },
                "qty": {
                  "title": "Qty",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 500
                },
                "grain": {
                  "title": "Grain",
                  "type": "boolean"
                },
                "shape": {
                  "title": "Shape",
                  "type": "string",
                  "enum": [
                    "rect",
                    "l",
                    "chamfer",
                    "quarter"
                  ]
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 10000
                },
                "b": {
                  "title": "b (mm)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 5000
                }
              },
              "required": [
                "name",
                "length",
                "width"
              ]
            }
          },
          "sheetLength": {
            "title": "Sheet length",
            "description": "Common sizes: 2800 × 2070, 2440 × 1220, 3050 × 1220, 2500 × 1250, 2620 × 2070, 2750 × 1830, 2800 × 2100, 2440 × 1830, 1220 × 610.",
            "type": "number",
            "minimum": 100,
            "maximum": 10000,
            "default": 2800,
            "x-unit": "mm"
          },
          "sheetWidth": {
            "title": "Sheet width",
            "type": "number",
            "minimum": 100,
            "maximum": 5000,
            "default": 2070,
            "x-unit": "mm"
          },
          "sheetQty": {
            "title": "Sheets available",
            "description": "0 = unlimited.",
            "type": "integer",
            "minimum": 0,
            "maximum": 1000,
            "default": 0
          },
          "sheetPrice": {
            "title": "Price per sheet",
            "type": "number",
            "minimum": 0,
            "maximum": 100000,
            "default": 0
          },
          "kerf": {
            "title": "Saw kerf",
            "type": "number",
            "minimum": 0,
            "maximum": 10,
            "default": 4,
            "x-unit": "mm"
          },
          "trim": {
            "title": "Trim margin per edge",
            "type": "number",
            "minimum": 0,
            "maximum": 50,
            "default": 10,
            "x-unit": "mm"
          },
          "method": {
            "title": "Method",
            "type": "string",
            "enum": [
              "auto",
              "guillotine",
              "maxrects"
            ],
            "default": "auto"
          },
          "grainedMaterial": {
            "title": "Material has grain",
            "type": "boolean",
            "default": true
          },
          "allowRotation": {
            "title": "Allow rotating parts without grain",
            "type": "boolean",
            "default": true
          },
          "minOffcutLength": {
            "title": "Min. offcut length to keep",
            "type": "number",
            "minimum": 0,
            "maximum": 3000,
            "default": 300,
            "x-unit": "mm"
          },
          "minOffcutWidth": {
            "title": "Min. offcut width to keep",
            "type": "number",
            "minimum": 0,
            "maximum": 3000,
            "default": 100,
            "x-unit": "mm"
          },
          "effort": {
            "title": "Optimisation",
            "type": "string",
            "enum": [
              "fast",
              "deep"
            ],
            "default": "fast"
          },
          "iterations": {
            "title": "Deep search iterations",
            "type": "integer",
            "minimum": 1,
            "maximum": 5000,
            "default": 400
          },
          "seed": {
            "title": "Search seed",
            "description": "Same seed + same input = same layout.",
            "type": "integer",
            "minimum": 1,
            "maximum": 1000000,
            "default": 1
          }
        },
        "required": [
          "parts"
        ]
      },
      "KerfwiseSheetNestingResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "method",
              "strategy",
              "sheetCount",
              "yieldPct",
              "sheets",
              "unplaced",
              "offcutAreaM2",
              "cutLengthM",
              "cost"
            ],
            "properties": {
              "method": {
                "type": "string"
              },
              "strategy": {
                "type": "string"
              },
              "sheetCount": {
                "type": "integer"
              },
              "yieldPct": {
                "type": "number"
              },
              "partAreaM2": {
                "type": "number"
              },
              "sheetAreaM2": {
                "type": "number"
              },
              "offcutAreaM2": {
                "type": "number"
              },
              "cutLengthM": {
                "type": "number"
              },
              "cost": {
                "type": "number"
              },
              "sheets": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "index",
                    "yieldPct",
                    "placements",
                    "offcuts"
                  ],
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "yieldPct": {
                      "type": "number"
                    },
                    "cutLength": {
                      "type": "number"
                    },
                    "placements": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "x": {
                            "type": "number"
                          },
                          "y": {
                            "type": "number"
                          },
                          "w": {
                            "type": "number"
                          },
                          "h": {
                            "type": "number"
                          },
                          "rotated": {
                            "type": "boolean"
                          },
                          "rot": {
                            "type": "integer"
                          },
                          "outline": {
                            "type": "array",
                            "items": {
                              "type": "array",
                              "items": {
                                "type": "number"
                              }
                            }
                          }
                        }
                      }
                    },
                    "offcuts": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "x": {
                            "type": "number"
                          },
                          "y": {
                            "type": "number"
                          },
                          "w": {
                            "type": "number"
                          },
                          "h": {
                            "type": "number"
                          }
                        }
                      }
                    }
                  }
                }
              },
              "unplaced": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "lowerBound": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Proven minimum number of sheets (area bound / large-part bound); null when sheets are limited."
              },
              "gap": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Sheets used − lower bound; 0 = proven optimal sheet count."
              },
              "provenOptimal": {
                "type": "boolean"
              },
              "searchIterations": {
                "type": "integer"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KineticaCollisionCheckInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "robot": {
            "title": "Robot",
            "description": "A library robot, or choose a custom DH table, URDF or Kinetica robot JSON.",
            "type": "string",
            "enum": [
              "kx6-1400",
              "ur5-like",
              "puma560",
              "scara-550",
              "gantry-xyz",
              "planar2",
              "custom-dh",
              "urdf",
              "json"
            ],
            "default": "kx6-1400"
          },
          "convention": {
            "title": "DH convention (custom table)",
            "type": "string",
            "enum": [
              "dh",
              "mdh"
            ],
            "default": "dh"
          },
          "dh": {
            "title": "DH table (custom)",
            "description": "One row per joint. Lengths in mm, angles in degrees. For revolute joints θ is the zero offset, for prismatic joints d is.",
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "revolute",
                    "prismatic"
                  ],
                  "default": "revolute"
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "default": 0
                },
                "alpha": {
                  "title": "α (deg)",
                  "type": "number",
                  "default": 0
                },
                "d": {
                  "title": "d (mm)",
                  "type": "number",
                  "default": 0
                },
                "theta": {
                  "title": "θ (deg)",
                  "type": "number",
                  "default": 0
                },
                "min": {
                  "title": "min",
                  "type": "number"
                },
                "max": {
                  "title": "max",
                  "type": "number"
                },
                "vmax": {
                  "title": "v max (/s)",
                  "type": "number"
                },
                "amax": {
                  "title": "a max (/s²)",
                  "type": "number"
                }
              },
              "required": []
            }
          },
          "robotText": {
            "title": "URDF or robot JSON",
            "description": "Used when Robot is “URDF” or “Kinetica robot JSON”. URDF subset: links with box/cylinder/sphere, revolute/continuous/prismatic/fixed joints.",
            "type": "string",
            "maxLength": 400000
          },
          "tool": {
            "title": "Tool offset (TCP)",
            "description": "x, y, z (mm), roll, pitch, yaw (deg) in the flange frame, e.g. “0, 0, 150, 0, 0, 0”. Empty = robot default.",
            "type": "string",
            "default": ""
          },
          "joints": {
            "title": "Joint values",
            "description": "Degrees / mm. Empty = home pose.",
            "type": "string",
            "default": ""
          },
          "obstacles": {
            "title": "Obstacles",
            "description": "Box: centre x, y, z and size sx, sy, sz. Sphere: centre and r. Capsule: start x, y, z, end x2, y2, z2 (in the size columns) and r. All in mm, world frame.",
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 40
                },
                "shape": {
                  "title": "Shape",
                  "type": "string",
                  "enum": [
                    "box",
                    "sphere",
                    "capsule"
                  ],
                  "default": "box"
                },
                "x": {
                  "title": "x",
                  "type": "number"
                },
                "y": {
                  "title": "y",
                  "type": "number"
                },
                "z": {
                  "title": "z",
                  "type": "number"
                },
                "sx": {
                  "title": "sx / x2",
                  "type": "number"
                },
                "sy": {
                  "title": "sy / y2",
                  "type": "number"
                },
                "sz": {
                  "title": "sz / z2",
                  "type": "number"
                },
                "r": {
                  "title": "r",
                  "type": "number",
                  "minimum": 0
                }
              },
              "required": [
                "x",
                "y",
                "z"
              ]
            }
          },
          "self": {
            "title": "Check self-collision",
            "type": "boolean",
            "default": true
          },
          "margin": {
            "title": "Safety margin",
            "type": "number",
            "minimum": 0,
            "maximum": 1000,
            "default": 10,
            "x-unit": "mm"
          }
        },
        "required": []
      },
      "KineticaCollisionCheckResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "collides",
              "minClearance",
              "contacts",
              "links",
              "checkedPairs"
            ],
            "properties": {
              "collides": {
                "type": "boolean"
              },
              "minClearance": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "closest": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "contacts": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "a": {
                      "type": "string"
                    },
                    "b": {
                      "type": "string"
                    },
                    "distance": {
                      "type": "number"
                    },
                    "kind": {
                      "type": "string"
                    }
                  }
                }
              },
              "links": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "checkedPairs": {
                "type": "integer"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KineticaForwardKinematicsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "robot": {
            "title": "Robot",
            "description": "A library robot, or choose a custom DH table, URDF or Kinetica robot JSON.",
            "type": "string",
            "enum": [
              "kx6-1400",
              "ur5-like",
              "puma560",
              "scara-550",
              "gantry-xyz",
              "planar2",
              "custom-dh",
              "urdf",
              "json"
            ],
            "default": "kx6-1400"
          },
          "convention": {
            "title": "DH convention (custom table)",
            "type": "string",
            "enum": [
              "dh",
              "mdh"
            ],
            "default": "dh"
          },
          "dh": {
            "title": "DH table (custom)",
            "description": "One row per joint. Lengths in mm, angles in degrees. For revolute joints θ is the zero offset, for prismatic joints d is.",
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "revolute",
                    "prismatic"
                  ],
                  "default": "revolute"
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "default": 0
                },
                "alpha": {
                  "title": "α (deg)",
                  "type": "number",
                  "default": 0
                },
                "d": {
                  "title": "d (mm)",
                  "type": "number",
                  "default": 0
                },
                "theta": {
                  "title": "θ (deg)",
                  "type": "number",
                  "default": 0
                },
                "min": {
                  "title": "min",
                  "type": "number"
                },
                "max": {
                  "title": "max",
                  "type": "number"
                },
                "vmax": {
                  "title": "v max (/s)",
                  "type": "number"
                },
                "amax": {
                  "title": "a max (/s²)",
                  "type": "number"
                }
              },
              "required": []
            }
          },
          "robotText": {
            "title": "URDF or robot JSON",
            "description": "Used when Robot is “URDF” or “Kinetica robot JSON”. URDF subset: links with box/cylinder/sphere, revolute/continuous/prismatic/fixed joints.",
            "type": "string",
            "maxLength": 400000
          },
          "tool": {
            "title": "Tool offset (TCP)",
            "description": "x, y, z (mm), roll, pitch, yaw (deg) in the flange frame, e.g. “0, 0, 150, 0, 0, 0”. Empty = robot default.",
            "type": "string",
            "default": ""
          },
          "joints": {
            "title": "Joint values",
            "description": "Comma-separated, degrees for revolute and mm for prismatic joints. Empty = the robot’s home pose.",
            "type": "string",
            "default": ""
          }
        },
        "required": []
      },
      "KineticaForwardKinematicsResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "robot",
              "dof",
              "joints",
              "tcp",
              "matrix",
              "frames",
              "jacobian",
              "manipulability",
              "withinLimits"
            ],
            "properties": {
              "robot": {
                "type": "string"
              },
              "dof": {
                "type": "integer"
              },
              "joints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "value": {
                      "type": "number"
                    },
                    "unit": {
                      "type": "string"
                    }
                  }
                }
              },
              "tcp": {
                "type": "object",
                "properties": {
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  },
                  "z": {
                    "type": "number"
                  },
                  "roll": {
                    "type": "number"
                  },
                  "pitch": {
                    "type": "number"
                  },
                  "yaw": {
                    "type": "number"
                  },
                  "quat": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  }
                },
                "required": [
                  "x",
                  "y",
                  "z",
                  "roll",
                  "pitch",
                  "yaw",
                  "quat"
                ]
              },
              "flange": {
                "type": "object",
                "properties": {
                  "x": {
                    "type": "number"
                  },
                  "y": {
                    "type": "number"
                  },
                  "z": {
                    "type": "number"
                  },
                  "roll": {
                    "type": "number"
                  },
                  "pitch": {
                    "type": "number"
                  },
                  "yaw": {
                    "type": "number"
                  },
                  "quat": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  }
                },
                "required": [
                  "x",
                  "y",
                  "z",
                  "roll",
                  "pitch",
                  "yaw",
                  "quat"
                ]
              },
              "matrix": {
                "type": "array",
                "items": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  }
                }
              },
              "frames": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "jacobian": {
                "type": "array",
                "items": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  }
                }
              },
              "manipulability": {
                "type": "object",
                "properties": {
                  "yoshikawa": {
                    "type": "number"
                  },
                  "inverseCondition": {
                    "type": "number"
                  },
                  "status": {
                    "type": "string"
                  }
                }
              },
              "payloadLoads": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "withinLimits": {
                "type": "boolean"
              },
              "violatedJoints": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KineticaInverseKinematicsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "robot": {
            "title": "Robot",
            "description": "A library robot, or choose a custom DH table, URDF or Kinetica robot JSON.",
            "type": "string",
            "enum": [
              "kx6-1400",
              "ur5-like",
              "puma560",
              "scara-550",
              "gantry-xyz",
              "planar2",
              "custom-dh",
              "urdf",
              "json"
            ],
            "default": "kx6-1400"
          },
          "convention": {
            "title": "DH convention (custom table)",
            "type": "string",
            "enum": [
              "dh",
              "mdh"
            ],
            "default": "dh"
          },
          "dh": {
            "title": "DH table (custom)",
            "description": "One row per joint. Lengths in mm, angles in degrees. For revolute joints θ is the zero offset, for prismatic joints d is.",
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "revolute",
                    "prismatic"
                  ],
                  "default": "revolute"
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "default": 0
                },
                "alpha": {
                  "title": "α (deg)",
                  "type": "number",
                  "default": 0
                },
                "d": {
                  "title": "d (mm)",
                  "type": "number",
                  "default": 0
                },
                "theta": {
                  "title": "θ (deg)",
                  "type": "number",
                  "default": 0
                },
                "min": {
                  "title": "min",
                  "type": "number"
                },
                "max": {
                  "title": "max",
                  "type": "number"
                },
                "vmax": {
                  "title": "v max (/s)",
                  "type": "number"
                },
                "amax": {
                  "title": "a max (/s²)",
                  "type": "number"
                }
              },
              "required": []
            }
          },
          "robotText": {
            "title": "URDF or robot JSON",
            "description": "Used when Robot is “URDF” or “Kinetica robot JSON”. URDF subset: links with box/cylinder/sphere, revolute/continuous/prismatic/fixed joints.",
            "type": "string",
            "maxLength": 400000
          },
          "tool": {
            "title": "Tool offset (TCP)",
            "description": "x, y, z (mm), roll, pitch, yaw (deg) in the flange frame, e.g. “0, 0, 150, 0, 0, 0”. Empty = robot default.",
            "type": "string",
            "default": ""
          },
          "x": {
            "title": "Target x",
            "type": "number",
            "default": 700,
            "x-unit": "mm"
          },
          "y": {
            "title": "Target y",
            "type": "number",
            "default": 200,
            "x-unit": "mm"
          },
          "z": {
            "title": "Target z",
            "type": "number",
            "default": 600,
            "x-unit": "mm"
          },
          "roll": {
            "title": "Roll",
            "type": "number",
            "default": 180,
            "x-unit": "deg"
          },
          "pitch": {
            "title": "Pitch",
            "type": "number",
            "default": 0,
            "x-unit": "deg"
          },
          "yaw": {
            "title": "Yaw",
            "type": "number",
            "default": 0,
            "x-unit": "deg"
          },
          "mode": {
            "title": "Task",
            "type": "string",
            "enum": [
              "auto",
              "full",
              "position"
            ],
            "default": "auto"
          },
          "seed": {
            "title": "Preferred configuration",
            "description": "Joint values the solutions are sorted by (closest first). Empty = home pose.",
            "type": "string",
            "default": ""
          },
          "restarts": {
            "title": "Random restarts",
            "type": "integer",
            "minimum": 0,
            "maximum": 64,
            "default": 12
          },
          "rngSeed": {
            "title": "Random seed",
            "description": "Seeds the deterministic restart generator, so the same input always gives the same solutions.",
            "type": "integer",
            "minimum": 0,
            "maximum": 2147483647,
            "default": 7
          }
        },
        "required": [
          "x",
          "y",
          "z"
        ]
      },
      "KineticaInverseKinematicsResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "ok",
              "solutions",
              "reach",
              "message",
              "task"
            ],
            "properties": {
              "ok": {
                "type": "boolean"
              },
              "message": {
                "type": "string"
              },
              "task": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "best": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "solutions": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "joints": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "posError": {
                      "type": "number"
                    },
                    "rotErrorDeg": {
                      "type": "number"
                    },
                    "withinLimits": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string"
                    },
                    "manipulability": {
                      "type": "number"
                    },
                    "source": {
                      "type": "string"
                    }
                  }
                }
              },
              "roundTrip": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "reach": {
                "type": "object",
                "properties": {
                  "distance": {
                    "type": "number"
                  },
                  "maxReach": {
                    "type": "number"
                  },
                  "inReach": {
                    "type": "boolean"
                  }
                }
              },
              "closest": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "attempts": {
                "type": "integer"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "KineticaTrajectoryPlannerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "robot": {
            "title": "Robot",
            "description": "A library robot, or choose a custom DH table, URDF or Kinetica robot JSON.",
            "type": "string",
            "enum": [
              "kx6-1400",
              "ur5-like",
              "puma560",
              "scara-550",
              "gantry-xyz",
              "planar2",
              "custom-dh",
              "urdf",
              "json"
            ],
            "default": "kx6-1400"
          },
          "convention": {
            "title": "DH convention (custom table)",
            "type": "string",
            "enum": [
              "dh",
              "mdh"
            ],
            "default": "dh"
          },
          "dh": {
            "title": "DH table (custom)",
            "description": "One row per joint. Lengths in mm, angles in degrees. For revolute joints θ is the zero offset, for prismatic joints d is.",
            "type": "array",
            "maxItems": 12,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "revolute",
                    "prismatic"
                  ],
                  "default": "revolute"
                },
                "a": {
                  "title": "a (mm)",
                  "type": "number",
                  "default": 0
                },
                "alpha": {
                  "title": "α (deg)",
                  "type": "number",
                  "default": 0
                },
                "d": {
                  "title": "d (mm)",
                  "type": "number",
                  "default": 0
                },
                "theta": {
                  "title": "θ (deg)",
                  "type": "number",
                  "default": 0
                },
                "min": {
                  "title": "min",
                  "type": "number"
                },
                "max": {
                  "title": "max",
                  "type": "number"
                },
                "vmax": {
                  "title": "v max (/s)",
                  "type": "number"
                },
                "amax": {
                  "title": "a max (/s²)",
                  "type": "number"
                }
              },
              "required": []
            }
          },
          "robotText": {
            "title": "URDF or robot JSON",
            "description": "Used when Robot is “URDF” or “Kinetica robot JSON”. URDF subset: links with box/cylinder/sphere, revolute/continuous/prismatic/fixed joints.",
            "type": "string",
            "maxLength": 400000
          },
          "tool": {
            "title": "Tool offset (TCP)",
            "description": "x, y, z (mm), roll, pitch, yaw (deg) in the flange frame, e.g. “0, 0, 150, 0, 0, 0”. Empty = robot default.",
            "type": "string",
            "default": ""
          },
          "waypoints": {
            "title": "Waypoints",
            "description": "Row 1 is the start. Target “j: 0, -90, 90, 0, 0, 0” (joints) or “p: x, y, z, roll, pitch, yaw” (TCP pose, mm/deg). PTP speed/accel in % of the joint limits; LIN/CIRC in mm/s and mm/s². CIRC needs a via point “x, y, z”.",
            "type": "array",
            "maxItems": 60,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 40
                },
                "type": {
                  "title": "Move",
                  "type": "string",
                  "enum": [
                    "PTP",
                    "LIN",
                    "CIRC"
                  ],
                  "default": "PTP"
                },
                "target": {
                  "title": "Target",
                  "type": "string",
                  "maxLength": 200
                },
                "via": {
                  "title": "Via (CIRC)",
                  "type": "string",
                  "maxLength": 100
                },
                "vel": {
                  "title": "Speed",
                  "type": "number",
                  "minimum": 0.01
                },
                "acc": {
                  "title": "Accel",
                  "type": "number",
                  "minimum": 0.01
                },
                "blend": {
                  "title": "Blend (mm)",
                  "type": "number",
                  "minimum": 0,
                  "default": 0
                },
                "dwell": {
                  "title": "Dwell (s)",
                  "type": "number",
                  "minimum": 0,
                  "default": 0
                }
              },
              "required": [
                "target"
              ]
            }
          },
          "projectJson": {
            "title": "Kinetica project JSON (optional)",
            "description": "A complete .kinetica.json project; when given, its robot, work objects and program are used instead of the fields above.",
            "type": "string",
            "maxLength": 1000000
          },
          "profile": {
            "title": "Velocity profile",
            "type": "string",
            "enum": [
              "trapezoid",
              "quintic",
              "scurve"
            ],
            "default": "trapezoid"
          },
          "override": {
            "title": "Speed override",
            "type": "number",
            "minimum": 1,
            "maximum": 100,
            "default": 100,
            "x-unit": "%"
          },
          "outputDt": {
            "title": "Output sample period",
            "type": "number",
            "minimum": 0.005,
            "maximum": 1,
            "default": 0.05,
            "x-unit": "s"
          }
        },
        "required": []
      },
      "KineticaTrajectoryPlannerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "ok",
              "cycleTime",
              "segments",
              "peaks",
              "samples",
              "joints"
            ],
            "properties": {
              "ok": {
                "type": "boolean"
              },
              "cycleTime": {
                "type": "number"
              },
              "pathLength": {
                "type": "number"
              },
              "joints": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "segments": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "start": {
                      "type": "number"
                    },
                    "duration": {
                      "type": "number"
                    },
                    "blendOverlap": {
                      "type": "number"
                    },
                    "dwell": {
                      "type": "number"
                    },
                    "speedScale": {
                      "type": "number"
                    }
                  }
                }
              },
              "peaks": {
                "type": "object",
                "properties": {
                  "jointVel": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "jointAcc": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "jointVelPercent": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "tcpSpeed": {
                    "type": "number"
                  }
                }
              },
              "samples": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "t": {
                      "type": "number"
                    },
                    "joints": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    },
                    "tcp": {
                      "type": "array",
                      "items": {
                        "type": "number"
                      }
                    }
                  }
                }
              },
              "errors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaBlendMixerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "backdrop": {
            "title": "Backdrop HEX",
            "type": "string",
            "maxLength": 7,
            "default": "#c8d6bd"
          },
          "source": {
            "title": "Source HEX",
            "type": "string",
            "maxLength": 7,
            "default": "#426a60"
          },
          "opacity": {
            "title": "Source opacity (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 60
          },
          "mode": {
            "title": "Blend mode",
            "type": "string",
            "enum": [
              "normal",
              "multiply",
              "screen",
              "overlay",
              "darken",
              "lighten",
              "difference",
              "exclusion"
            ],
            "default": "multiply"
          }
        },
        "required": []
      },
      "LuminaBlendMixerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "hex",
              "r",
              "g",
              "b",
              "opacity",
              "mode"
            ],
            "properties": {
              "hex": {
                "type": "string"
              },
              "r": {
                "type": "integer"
              },
              "g": {
                "type": "integer"
              },
              "b": {
                "type": "integer"
              },
              "opacity": {
                "type": "number"
              },
              "mode": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaLayerTransformInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "canvasWidth": {
            "title": "Canvas width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 4000,
            "default": 1000
          },
          "canvasHeight": {
            "title": "Canvas height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 4000,
            "default": 700
          },
          "width": {
            "title": "Layer width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 4000,
            "default": 600
          },
          "height": {
            "title": "Layer height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 4000,
            "default": 400
          },
          "x": {
            "title": "Layer center X (px)",
            "type": "number",
            "minimum": -8000,
            "maximum": 8000,
            "default": 500
          },
          "y": {
            "title": "Layer center Y (px)",
            "type": "number",
            "minimum": -8000,
            "maximum": 8000,
            "default": 350
          },
          "rotation": {
            "title": "Rotation (degrees)",
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "default": 20
          },
          "flipX": {
            "title": "Flip horizontally",
            "type": "boolean",
            "default": false
          },
          "flipY": {
            "title": "Flip vertically",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "LuminaLayerTransformResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "matrix",
              "corners",
              "bounds"
            ],
            "properties": {
              "matrix": {
                "type": "array",
                "minItems": 6,
                "maxItems": 6,
                "items": {
                  "type": "number"
                }
              },
              "corners": {
                "type": "array",
                "minItems": 4,
                "maxItems": 4,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "x",
                    "y"
                  ],
                  "properties": {
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    }
                  }
                }
              },
              "bounds": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "left",
                  "top",
                  "right",
                  "bottom",
                  "width",
                  "height"
                ],
                "properties": {
                  "left": {
                    "type": "number"
                  },
                  "top": {
                    "type": "number"
                  },
                  "right": {
                    "type": "number"
                  },
                  "bottom": {
                    "type": "number"
                  },
                  "width": {
                    "type": "number"
                  },
                  "height": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "OhmforgeAcSweepInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "netlist": {
            "title": "SPICE netlist",
            "description": "Subset: R C L V I D Q M E G K, X subcircuit instances with .subckt/.ends (nested, with params), .param and {expressions}, .model, SIN/PULSE/PWL sources, engineering suffixes (k, m, u, n, p, meg). Node 0 is ground. Leave empty when you pass an Ohmforge project instead.",
            "type": "string",
            "maxLength": 60000
          },
          "project": {
            "title": "Ohmforge project JSON (optional)",
            "description": "Alternative to the netlist: a .ohmforge.json project as saved by the app (format \"ohmforge\", version 1).",
            "type": "string",
            "maxLength": 200000
          },
          "output": {
            "title": "Output node",
            "type": "string",
            "maxLength": 64,
            "default": "out"
          },
          "input": {
            "title": "Input source (gets AC = 1)",
            "description": "Name of the V or I source to excite (e.g. V1). Empty = keep the AC magnitudes from the netlist.",
            "type": "string",
            "maxLength": 64
          },
          "inputNode": {
            "title": "Reference node (optional)",
            "description": "Report V(out)/V(ref) instead of V(out).",
            "type": "string",
            "maxLength": 64
          },
          "fstart": {
            "title": "Start frequency",
            "description": "Empty = .ac line or 1 Hz.",
            "type": "number",
            "minimum": 0.001,
            "maximum": 10000000000,
            "x-unit": "Hz"
          },
          "fstop": {
            "title": "Stop frequency",
            "description": "Empty = .ac line or 10 MHz.",
            "type": "number",
            "minimum": 0.001,
            "maximum": 100000000000,
            "x-unit": "Hz"
          },
          "pointsPerDecade": {
            "title": "Points per decade",
            "description": "Empty = .ac line or 20.",
            "type": "integer",
            "minimum": 1,
            "maximum": 200
          }
        },
        "required": [
          "output"
        ]
      },
      "OhmforgeAcSweepResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "output",
              "frequencies",
              "magnitudeDb",
              "phaseDeg",
              "peakDb"
            ],
            "properties": {
              "output": {
                "type": "string"
              },
              "input": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "frequencies": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "magnitudeDb": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "phaseDeg": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "peakDb": {
                "type": "number"
              },
              "peakFrequency": {
                "type": "number"
              },
              "lowGainDb": {
                "type": "number"
              },
              "lower3dB": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "upper3dB": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "unityGainFrequency": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "diagnostics": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "OhmforgeDcOperatingPointInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "netlist": {
            "title": "SPICE netlist",
            "description": "Subset: R C L V I D Q M E G K, X subcircuit instances with .subckt/.ends (nested, with params), .param and {expressions}, .model, SIN/PULSE/PWL sources, engineering suffixes (k, m, u, n, p, meg). Node 0 is ground. Leave empty when you pass an Ohmforge project instead.",
            "type": "string",
            "maxLength": 60000
          },
          "project": {
            "title": "Ohmforge project JSON (optional)",
            "description": "Alternative to the netlist: a .ohmforge.json project as saved by the app (format \"ohmforge\", version 1).",
            "type": "string",
            "maxLength": 200000
          }
        },
        "required": []
      },
      "OhmforgeDcOperatingPointResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "converged",
              "nodes",
              "elements",
              "stress",
              "diagnostics"
            ],
            "properties": {
              "title": {
                "type": "string"
              },
              "converged": {
                "type": "boolean"
              },
              "strategy": {
                "type": "string"
              },
              "iterations": {
                "type": "integer"
              },
              "nodes": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                },
                "description": "Node voltages in V (ground = 0 is implicit)."
              },
              "elements": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "current": {
                      "type": "number"
                    },
                    "voltage": {
                      "type": "number"
                    },
                    "power": {
                      "type": "number"
                    },
                    "state": {
                      "type": "string"
                    }
                  }
                }
              },
              "totalSupplyPower": {
                "type": "number"
              },
              "stress": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "element": {
                      "type": "string"
                    },
                    "level": {
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              },
              "diagnostics": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "OhmforgeLedResistorInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "supply": {
            "title": "Supply voltage",
            "type": "number",
            "minimum": 0.5,
            "maximum": 400,
            "default": 9,
            "x-unit": "V"
          },
          "colour": {
            "title": "LED colour",
            "type": "string",
            "enum": [
              "red",
              "orange",
              "yellow",
              "green",
              "blue",
              "white",
              "ir",
              "custom"
            ],
            "default": "red"
          },
          "vf": {
            "title": "Forward voltage (custom)",
            "description": "Only used for \"Custom\".",
            "type": "number",
            "minimum": 0.5,
            "maximum": 50,
            "x-unit": "V"
          },
          "current": {
            "title": "LED current",
            "type": "number",
            "minimum": 0.1,
            "maximum": 1000,
            "default": 20,
            "x-unit": "mA"
          },
          "count": {
            "title": "LEDs in series",
            "type": "integer",
            "minimum": 1,
            "maximum": 50,
            "default": 1
          },
          "series": {
            "title": "Resistor series",
            "type": "string",
            "enum": [
              "E6",
              "E12",
              "E24"
            ],
            "default": "E12"
          }
        },
        "required": [
          "supply",
          "current"
        ]
      },
      "OhmforgeLedResistorResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "exactOhms",
              "standardOhms",
              "simulatedCurrent",
              "resistorPower",
              "recommendedRating"
            ],
            "properties": {
              "vf": {
                "type": "number"
              },
              "exactOhms": {
                "type": "number"
              },
              "standardOhms": {
                "type": "number"
              },
              "series": {
                "type": "string"
              },
              "simulatedCurrent": {
                "type": "number"
              },
              "simulatedLedVoltage": {
                "type": "number"
              },
              "resistorPower": {
                "type": "number"
              },
              "ledPower": {
                "type": "number"
              },
              "recommendedRating": {
                "type": "number"
              },
              "efficiency": {
                "type": "number"
              },
              "netlist": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "OhmforgeTransientInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "netlist": {
            "title": "SPICE netlist",
            "description": "Subset: R C L V I D Q M E G K, X subcircuit instances with .subckt/.ends (nested, with params), .param and {expressions}, .model, SIN/PULSE/PWL sources, engineering suffixes (k, m, u, n, p, meg). Node 0 is ground. Leave empty when you pass an Ohmforge project instead.",
            "type": "string",
            "maxLength": 60000
          },
          "project": {
            "title": "Ohmforge project JSON (optional)",
            "description": "Alternative to the netlist: a .ohmforge.json project as saved by the app (format \"ohmforge\", version 1).",
            "type": "string",
            "maxLength": 200000
          },
          "tstop": {
            "title": "Stop time",
            "description": "Simulated duration. Empty = use the .tran line of the netlist (or the project's transient settings).",
            "type": "number",
            "minimum": 0,
            "maximum": 1000,
            "x-unit": "s"
          },
          "tstep": {
            "title": "Output step",
            "description": "Spacing of the returned samples (also the largest internal step). Empty = .tran value or stop time / 1000.",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "x-unit": "s"
          },
          "method": {
            "title": "Integration",
            "type": "string",
            "enum": [
              "trap",
              "be"
            ],
            "default": "trap"
          },
          "uic": {
            "title": "Start from initial conditions (skip the operating point)",
            "description": "Capacitors start at their IC (default 0 V). Needed for oscillators.",
            "type": "boolean",
            "default": false
          },
          "probes": {
            "title": "Nodes to return",
            "description": "Node names; empty = all nodes (max 8 in the chart).",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 8
          }
        },
        "required": []
      },
      "OhmforgeTransientResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "ok",
              "tstop",
              "tstep",
              "time",
              "waveforms",
              "measurements"
            ],
            "properties": {
              "ok": {
                "type": "boolean"
              },
              "tstop": {
                "type": "number"
              },
              "tstep": {
                "type": "number"
              },
              "method": {
                "type": "string"
              },
              "start": {
                "type": "string"
              },
              "steps": {
                "type": "integer"
              },
              "rejectedSteps": {
                "type": "integer"
              },
              "time": {
                "type": "array",
                "items": {
                  "type": "number"
                }
              },
              "waveforms": {
                "type": "object",
                "additionalProperties": {
                  "type": "array",
                  "items": {
                    "type": "number"
                  }
                }
              },
              "measurements": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "properties": {
                    "min": {
                      "type": "number"
                    },
                    "max": {
                      "type": "number"
                    },
                    "vpp": {
                      "type": "number"
                    },
                    "mean": {
                      "type": "number"
                    },
                    "rms": {
                      "type": "number"
                    },
                    "frequency": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "duty": {
                      "type": [
                        "number",
                        "null"
                      ]
                    },
                    "riseTime": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              },
              "stress": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "diagnostics": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaPhotoLayoutInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "Photo or ZIP of photos",
            "description": "Up to 24 PNG, JPEG, WebP or GIF images (first frame). ZIP order is preserved. Decoded total: 32 MP; each photo: 16 MP. Upload as {filename, contentType?, contentBase64}. Max 78125 KiB. Accepts .zip,.png,.jpg,.jpeg,.webp,.gif.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "mode": {
            "title": "Layout",
            "type": "string",
            "enum": [
              "grid",
              "contact",
              "horizontal",
              "vertical"
            ],
            "default": "grid"
          },
          "width": {
            "title": "Canvas width (px)",
            "type": "integer",
            "minimum": 32,
            "maximum": 16000,
            "default": 1600
          },
          "height": {
            "title": "Grid height (px; joins calculate this)",
            "type": "integer",
            "minimum": 32,
            "maximum": 16000,
            "default": 1200
          },
          "columns": {
            "title": "Grid columns",
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 3
          },
          "gap": {
            "title": "Gap (px)",
            "type": "integer",
            "minimum": 0,
            "maximum": 200,
            "default": 16
          },
          "margin": {
            "title": "Margin (px)",
            "type": "integer",
            "minimum": 0,
            "maximum": 200,
            "default": 24
          },
          "fit": {
            "title": "Photo fit",
            "type": "string",
            "enum": [
              "cover",
              "contain"
            ],
            "default": "cover"
          },
          "background": {
            "title": "Background (#RRGGBB)",
            "type": "string",
            "maxLength": 7,
            "default": "#ffffff"
          },
          "format": {
            "title": "Output",
            "type": "string",
            "enum": [
              "png",
              "jpeg",
              "webp"
            ],
            "default": "png"
          }
        },
        "required": [
          "images"
        ]
      },
      "LuminaPhotoLayoutResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "photos",
              "skipped",
              "artifact"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "photos": {
                "type": "integer"
              },
              "skipped": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaResizePlannerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "width": {
            "title": "Source width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 6000
          },
          "height": {
            "title": "Source height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 4000
          },
          "targetWidth": {
            "title": "Target width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 1080
          },
          "targetHeight": {
            "title": "Target height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 1080
          },
          "mode": {
            "title": "Fit mode",
            "type": "string",
            "enum": [
              "contain",
              "cover"
            ],
            "default": "cover"
          },
          "upscale": {
            "title": "Allow enlargement",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "LuminaResizePlannerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "scale",
              "crop",
              "megapixels",
              "upscaled"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "scale": {
                "type": "number"
              },
              "megapixels": {
                "type": "number"
              },
              "upscaled": {
                "type": "boolean"
              },
              "crop": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "x",
                  "y",
                  "width",
                  "height"
                ],
                "properties": {
                  "x": {
                    "type": "integer"
                  },
                  "y": {
                    "type": "integer"
                  },
                  "width": {
                    "type": "integer"
                  },
                  "height": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaHistogramAnalyzerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "image": {
            "title": "Image (PNG, JPEG or WebP; up to 8 MB and 16 MP)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 7813 KiB. Accepts .png,.jpg,.jpeg,.webp.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "image"
        ]
      },
      "LuminaHistogramAnalyzerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "analysis",
              "suggestedLevels"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "analysis": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "bins",
                  "visible",
                  "transparent",
                  "mean",
                  "shadow",
                  "highlight",
                  "shadowPercent",
                  "highlightPercent",
                  "p01",
                  "median",
                  "p99"
                ],
                "properties": {
                  "bins": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "luminance",
                      "red",
                      "green",
                      "blue"
                    ],
                    "properties": {
                      "luminance": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "red": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "green": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "blue": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "visible": {
                    "type": "integer"
                  },
                  "transparent": {
                    "type": "integer"
                  },
                  "mean": {
                    "type": "number"
                  },
                  "shadow": {
                    "type": "integer"
                  },
                  "highlight": {
                    "type": "integer"
                  },
                  "shadowPercent": {
                    "type": "number"
                  },
                  "highlightPercent": {
                    "type": "number"
                  },
                  "p01": {
                    "type": "integer"
                  },
                  "median": {
                    "type": "integer"
                  },
                  "p99": {
                    "type": "integer"
                  }
                }
              },
              "suggestedLevels": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "black",
                  "white"
                ],
                "properties": {
                  "black": {
                    "type": "integer"
                  },
                  "white": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaColorAnalyzerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "colors": {
            "title": "Hex colors (comma or space separated)",
            "type": "string",
            "maxLength": 1024,
            "default": "#164e63 #c5d6a5 #f4e6cf"
          }
        },
        "required": []
      },
      "LuminaColorAnalyzerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "colors"
            ],
            "properties": {
              "colors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "hex",
                    "luminance",
                    "contrastBlack",
                    "contrastWhite",
                    "textColor"
                  ],
                  "properties": {
                    "hex": {
                      "type": "string"
                    },
                    "luminance": {
                      "type": "number"
                    },
                    "contrastBlack": {
                      "type": "number"
                    },
                    "contrastWhite": {
                      "type": "number"
                    },
                    "textColor": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaPrintPlannerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "width": {
            "title": "Photo width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 6000
          },
          "height": {
            "title": "Photo height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 100000,
            "default": 4000
          },
          "ppi": {
            "title": "Print resolution (PPI)",
            "type": "integer",
            "minimum": 1,
            "maximum": 2400,
            "default": 300
          }
        },
        "required": []
      },
      "LuminaPrintPlannerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "widthInches",
              "heightInches",
              "widthCm",
              "heightCm",
              "ppi",
              "megapixels"
            ],
            "properties": {
              "widthInches": {
                "type": "number"
              },
              "heightInches": {
                "type": "number"
              },
              "widthCm": {
                "type": "number"
              },
              "heightCm": {
                "type": "number"
              },
              "ppi": {
                "type": "number"
              },
              "megapixels": {
                "type": "number"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaGifMakerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "Photo or ZIP of photos",
            "description": "Up to 24 PNG, JPEG, WebP or GIF images (first frame). ZIP order is preserved. Decoded total: 32 MP; each photo: 16 MP. Upload as {filename, contentType?, contentBase64}. Max 78125 KiB. Accepts .zip,.png,.jpg,.jpeg,.webp,.gif.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "width": {
            "title": "Frame width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 1600,
            "default": 640
          },
          "height": {
            "title": "Frame height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 1600,
            "default": 480
          },
          "delay": {
            "title": "Milliseconds per frame",
            "type": "integer",
            "minimum": 20,
            "maximum": 10000,
            "default": 500
          },
          "loop": {
            "title": "Repeat forever",
            "type": "boolean",
            "default": true
          },
          "fit": {
            "title": "Photo fit",
            "type": "string",
            "enum": [
              "cover",
              "contain"
            ],
            "default": "cover"
          },
          "background": {
            "title": "Background (#RRGGBB)",
            "type": "string",
            "maxLength": 7,
            "default": "#ffffff"
          }
        },
        "required": [
          "images"
        ]
      },
      "LuminaGifMakerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "frames",
              "durationMs",
              "artifact"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "frames": {
                "type": "integer"
              },
              "durationMs": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "LuminaToneProcessorInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "image": {
            "title": "Image (PNG, JPEG or WebP; up to 8 MB and 16 MP)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 7813 KiB. Accepts .png,.jpg,.jpeg,.webp.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "profile": {
            "title": "Lumina profile JSON (optional; overrides all manual fields)",
            "type": "string",
            "maxLength": 64000,
            "default": ""
          },
          "black": {
            "title": "Input black",
            "type": "number",
            "minimum": 0,
            "maximum": 254,
            "default": 0
          },
          "white": {
            "title": "Input white",
            "type": "number",
            "minimum": 1,
            "maximum": 255,
            "default": 255
          },
          "gamma": {
            "title": "Midtone gamma",
            "type": "number",
            "minimum": 0.2,
            "maximum": 5,
            "default": 1
          },
          "exposure": {
            "title": "Exposure (EV)",
            "type": "number",
            "minimum": -3,
            "maximum": 3,
            "default": 0
          },
          "contrast": {
            "title": "Contrast",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "saturation": {
            "title": "Saturation",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "redGain": {
            "title": "Red linear-light gain",
            "type": "number",
            "minimum": 0.25,
            "maximum": 4,
            "default": 1
          },
          "greenGain": {
            "title": "Green linear-light gain",
            "type": "number",
            "minimum": 0.25,
            "maximum": 4,
            "default": 1
          },
          "blueGain": {
            "title": "Blue linear-light gain",
            "type": "number",
            "minimum": 0.25,
            "maximum": 4,
            "default": 1
          },
          "master": {
            "title": "Master curve outputs at 0,64,128,192,255",
            "type": "string",
            "maxLength": 40,
            "default": "0,64,128,192,255"
          },
          "red": {
            "title": "Red curve outputs at 0,64,128,192,255",
            "type": "string",
            "maxLength": 40,
            "default": "0,64,128,192,255"
          },
          "green": {
            "title": "Green curve outputs at 0,64,128,192,255",
            "type": "string",
            "maxLength": 40,
            "default": "0,64,128,192,255"
          },
          "blue": {
            "title": "Blue curve outputs at 0,64,128,192,255",
            "type": "string",
            "maxLength": 40,
            "default": "0,64,128,192,255"
          }
        },
        "required": [
          "image"
        ]
      },
      "LuminaToneProcessorResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "artifact",
              "profile",
              "analysis"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "profile": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "name",
                  "adjustments",
                  "development"
                ],
                "properties": {
                  "format": {
                    "const": "lumina-profile"
                  },
                  "version": {
                    "const": 1
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "adjustments": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "exposure",
                      "contrast",
                      "highlights",
                      "shadows",
                      "temperature",
                      "tint",
                      "saturation",
                      "vibrance",
                      "fade",
                      "vignette",
                      "sharpen",
                      "denoise"
                    ],
                    "properties": {
                      "exposure": {
                        "type": "number",
                        "minimum": -3,
                        "maximum": 3
                      },
                      "contrast": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "highlights": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "shadows": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "temperature": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "tint": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "saturation": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "vibrance": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "fade": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "vignette": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "sharpen": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "denoise": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      }
                    }
                  },
                  "development": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "black",
                      "white",
                      "gamma",
                      "gains",
                      "curves"
                    ],
                    "properties": {
                      "black": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 254
                      },
                      "white": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 255
                      },
                      "gamma": {
                        "type": "number",
                        "minimum": 0.2,
                        "maximum": 5
                      },
                      "gains": {
                        "type": "array",
                        "minItems": 3,
                        "maxItems": 3,
                        "items": {
                          "type": "number",
                          "minimum": 0.25,
                          "maximum": 4
                        }
                      },
                      "curves": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "master",
                          "red",
                          "green",
                          "blue"
                        ],
                        "properties": {
                          "master": {
                            "type": "array",
                            "minItems": 5,
                            "maxItems": 5,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 255
                            }
                          },
                          "red": {
                            "type": "array",
                            "minItems": 5,
                            "maxItems": 5,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 255
                            }
                          },
                          "green": {
                            "type": "array",
                            "minItems": 5,
                            "maxItems": 5,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 255
                            }
                          },
                          "blue": {
                            "type": "array",
                            "minItems": 5,
                            "maxItems": 5,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 255
                            }
                          }
                        }
                      },
                      "hsl": {
                        "type": "object",
                        "additionalProperties": false,
                        "description": "Optional HSL color mixer. Bands: reds, oranges, yellows, greens, aquas, blues, purples, magentas. Hue ±100 = ±30°, saturation and luminance −100…100.",
                        "properties": {
                          "hue": {
                            "type": "array",
                            "minItems": 8,
                            "maxItems": 8,
                            "items": {
                              "type": "number",
                              "minimum": -100,
                              "maximum": 100
                            }
                          },
                          "saturation": {
                            "type": "array",
                            "minItems": 8,
                            "maxItems": 8,
                            "items": {
                              "type": "number",
                              "minimum": -100,
                              "maximum": 100
                            }
                          },
                          "luminance": {
                            "type": "array",
                            "minItems": 8,
                            "maxItems": 8,
                            "items": {
                              "type": "number",
                              "minimum": -100,
                              "maximum": 100
                            }
                          }
                        }
                      }
                    }
                  }
                }
              },
              "analysis": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "bins",
                  "visible",
                  "transparent",
                  "mean",
                  "shadow",
                  "highlight",
                  "shadowPercent",
                  "highlightPercent",
                  "p01",
                  "median",
                  "p99"
                ],
                "properties": {
                  "bins": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "luminance",
                      "red",
                      "green",
                      "blue"
                    ],
                    "properties": {
                      "luminance": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "red": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "green": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      },
                      "blue": {
                        "type": "array",
                        "minItems": 256,
                        "maxItems": 256,
                        "items": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "visible": {
                    "type": "integer"
                  },
                  "transparent": {
                    "type": "integer"
                  },
                  "mean": {
                    "type": "number"
                  },
                  "shadow": {
                    "type": "integer"
                  },
                  "highlight": {
                    "type": "integer"
                  },
                  "shadowPercent": {
                    "type": "number"
                  },
                  "highlightPercent": {
                    "type": "number"
                  },
                  "p01": {
                    "type": "integer"
                  },
                  "median": {
                    "type": "integer"
                  },
                  "p99": {
                    "type": "integer"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplinePixelQuantizerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "image": {
            "title": "Image (PNG, JPEG or WebP)",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 3906 KiB. Accepts .png,.jpg,.jpeg,.webp.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "width": {
            "title": "Pixel canvas width",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "height": {
            "title": "Pixel canvas height",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "colors": {
            "title": "Automatic palette colors",
            "type": "integer",
            "minimum": 1,
            "maximum": 32,
            "default": 16
          },
          "palette": {
            "title": "Custom HEX, GPL or JASC-PAL palette (optional)",
            "type": "string",
            "maxLength": 16384,
            "default": ""
          },
          "alpha": {
            "title": "Alpha threshold",
            "type": "integer",
            "minimum": 1,
            "maximum": 255,
            "default": 128
          },
          "scale": {
            "title": "PNG integer scale",
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "default": 8
          },
          "fit": {
            "title": "Image fit",
            "type": "string",
            "enum": [
              "stretch",
              "contain",
              "cover"
            ],
            "default": "stretch"
          },
          "dither": {
            "title": "Dithering",
            "type": "string",
            "enum": [
              "none",
              "bayer",
              "floyd"
            ],
            "default": "none"
          },
          "strength": {
            "title": "Dither strength (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 75
          },
          "brightness": {
            "title": "Brightness",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "contrast": {
            "title": "Contrast",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "gamma": {
            "title": "Gamma",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 1
          },
          "saturation": {
            "title": "Saturation (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 200,
            "default": 100
          },
          "red": {
            "title": "Red shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "green": {
            "title": "Green shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "blue": {
            "title": "Blue shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          }
        },
        "required": [
          "image"
        ]
      },
      "FliplinePixelQuantizerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "palette",
              "artifact",
              "project",
              "recipe"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "palette": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "project": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "name",
                  "width",
                  "height",
                  "fps",
                  "background",
                  "palette",
                  "frames",
                  "layers"
                ],
                "properties": {
                  "format": {
                    "const": "flipline-pixels"
                  },
                  "version": {
                    "const": 1
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "width": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 128
                  },
                  "height": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 128
                  },
                  "fps": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "background": {
                    "type": "string",
                    "pattern": "^#[a-fA-F0-9]{6}$"
                  },
                  "palette": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 32,
                    "items": {
                      "type": "string",
                      "pattern": "^#[a-fA-F0-9]{6}$"
                    }
                  },
                  "frames": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 24,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "hold",
                        "label"
                      ],
                      "properties": {
                        "hold": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 10
                        },
                        "label": {
                          "type": "string",
                          "maxLength": 80
                        }
                      }
                    }
                  },
                  "layers": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 4,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "name",
                        "visible",
                        "locked",
                        "opacity",
                        "cels"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 80
                        },
                        "visible": {
                          "type": "boolean"
                        },
                        "locked": {
                          "type": "boolean"
                        },
                        "opacity": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "cels": {
                          "type": "array",
                          "minItems": 1,
                          "maxItems": 24,
                          "items": {
                            "type": "array",
                            "minItems": 1,
                            "maxItems": 16384,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 32
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "description": "Palette indices: 0 is transparent; 1–32 refer to palette entries. Every cel has width × height pixels and every layer has one cel per frame. Indices cannot exceed palette length. Maximum 524288 total stored cel pixels. Layers are front to back. Files: .flipline-pixels.json."
              },
              "recipe": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "settings"
                ],
                "properties": {
                  "format": {
                    "const": "flipline-retro"
                  },
                  "version": {
                    "const": 1
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "width",
                      "height",
                      "scale",
                      "fit",
                      "colors",
                      "palette",
                      "dither",
                      "strength",
                      "brightness",
                      "contrast",
                      "gamma",
                      "saturation",
                      "red",
                      "green",
                      "blue",
                      "alpha"
                    ],
                    "properties": {
                      "width": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "height": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "scale": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 8
                      },
                      "fit": {
                        "enum": [
                          "contain",
                          "cover",
                          "stretch"
                        ]
                      },
                      "colors": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 32
                      },
                      "palette": {
                        "type": "array",
                        "maxItems": 32,
                        "items": {
                          "type": "string",
                          "pattern": "^#[a-fA-F0-9]{6}$"
                        }
                      },
                      "dither": {
                        "enum": [
                          "none",
                          "bayer",
                          "floyd"
                        ]
                      },
                      "strength": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "brightness": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "contrast": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "gamma": {
                        "type": "number",
                        "minimum": 0.2,
                        "maximum": 3
                      },
                      "saturation": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 200
                      },
                      "red": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "green": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "blue": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "alpha": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 255
                      }
                    }
                  }
                },
                "description": "A settings-only retro conversion recipe. No images are embedded. Empty palette selects frequent adjusted image colors; batch exports resolve it from the first image and share that palette. Dithering runs in native output pixel coordinates, independent of display zoom and export scale."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplinePixelTransformInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "rows": {
            "title": "Pixel rows (. = transparent; 1–9 then A–W = palette entries)",
            "type": "string",
            "maxLength": 16640,
            "default": "..1..\n.121.\n12321\n.121.\n..1.."
          },
          "palette": {
            "title": "HEX palette (comma separated)",
            "type": "string",
            "maxLength": 300,
            "default": "#213b45,#65a87b,#e6af61"
          },
          "operation": {
            "title": "Operation",
            "type": "string",
            "enum": [
              "flip-x",
              "flip-y",
              "rotate",
              "shift"
            ],
            "default": "rotate"
          },
          "dx": {
            "title": "Shift X (pixels)",
            "type": "integer",
            "minimum": -128,
            "maximum": 128,
            "default": 1
          },
          "dy": {
            "title": "Shift Y (pixels)",
            "type": "integer",
            "minimum": -128,
            "maximum": 128,
            "default": 0
          },
          "wrap": {
            "title": "Wrap shifted pixels",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "FliplinePixelTransformResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "rows",
              "project"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "rows": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "project": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "name",
                  "width",
                  "height",
                  "fps",
                  "background",
                  "palette",
                  "frames",
                  "layers"
                ],
                "properties": {
                  "format": {
                    "const": "flipline-pixels"
                  },
                  "version": {
                    "const": 1
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "width": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 128
                  },
                  "height": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 128
                  },
                  "fps": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 30
                  },
                  "background": {
                    "type": "string",
                    "pattern": "^#[a-fA-F0-9]{6}$"
                  },
                  "palette": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 32,
                    "items": {
                      "type": "string",
                      "pattern": "^#[a-fA-F0-9]{6}$"
                    }
                  },
                  "frames": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 24,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "hold",
                        "label"
                      ],
                      "properties": {
                        "hold": {
                          "type": "integer",
                          "minimum": 1,
                          "maximum": 10
                        },
                        "label": {
                          "type": "string",
                          "maxLength": 80
                        }
                      }
                    }
                  },
                  "layers": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 4,
                    "items": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "name",
                        "visible",
                        "locked",
                        "opacity",
                        "cels"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "maxLength": 80
                        },
                        "visible": {
                          "type": "boolean"
                        },
                        "locked": {
                          "type": "boolean"
                        },
                        "opacity": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1
                        },
                        "cels": {
                          "type": "array",
                          "minItems": 1,
                          "maxItems": 24,
                          "items": {
                            "type": "array",
                            "minItems": 1,
                            "maxItems": 16384,
                            "items": {
                              "type": "integer",
                              "minimum": 0,
                              "maximum": 32
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "description": "Palette indices: 0 is transparent; 1–32 refer to palette entries. Every cel has width × height pixels and every layer has one cel per frame. Indices cannot exceed palette length. Maximum 524288 total stored cel pixels. Layers are front to back. Files: .flipline-pixels.json."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplinePaletteConverterInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "text": {
            "title": "Palette text (HEX, GIMP GPL or JASC-PAL)",
            "type": "string",
            "maxLength": 16384,
            "default": "#172c2f, #416350, #93ab69, #e3e6b1"
          },
          "name": {
            "title": "Palette name",
            "type": "string",
            "maxLength": 80,
            "default": "My pixel palette"
          }
        },
        "required": []
      },
      "FliplinePaletteConverterResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "format",
              "colors",
              "hex",
              "gpl",
              "jasc"
            ],
            "properties": {
              "format": {
                "type": "string"
              },
              "colors": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "hex": {
                "type": "string"
              },
              "gpl": {
                "type": "string"
              },
              "jasc": {
                "type": "string"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplineRetroBatchInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "images": {
            "title": "ZIP of PNG, JPEG or WebP images",
            "type": "object",
            "description": "Upload as {filename, contentType?, contentBase64}. Max 15625 KiB. Accepts .zip.",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "width": {
            "title": "Pixel canvas width",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "height": {
            "title": "Pixel canvas height",
            "type": "integer",
            "minimum": 1,
            "maximum": 128,
            "default": 32
          },
          "colors": {
            "title": "Automatic palette colors",
            "type": "integer",
            "minimum": 1,
            "maximum": 32,
            "default": 16
          },
          "palette": {
            "title": "Custom HEX, GPL or JASC-PAL palette (optional)",
            "type": "string",
            "maxLength": 16384,
            "default": ""
          },
          "alpha": {
            "title": "Alpha threshold",
            "type": "integer",
            "minimum": 1,
            "maximum": 255,
            "default": 128
          },
          "scale": {
            "title": "PNG integer scale",
            "type": "integer",
            "minimum": 1,
            "maximum": 8,
            "default": 8
          },
          "fit": {
            "title": "Image fit",
            "type": "string",
            "enum": [
              "stretch",
              "contain",
              "cover"
            ],
            "default": "stretch"
          },
          "dither": {
            "title": "Dithering",
            "type": "string",
            "enum": [
              "none",
              "bayer",
              "floyd"
            ],
            "default": "none"
          },
          "strength": {
            "title": "Dither strength (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 75
          },
          "brightness": {
            "title": "Brightness",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "contrast": {
            "title": "Contrast",
            "type": "number",
            "minimum": -100,
            "maximum": 100,
            "default": 0
          },
          "gamma": {
            "title": "Gamma",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 1
          },
          "saturation": {
            "title": "Saturation (%)",
            "type": "number",
            "minimum": 0,
            "maximum": 200,
            "default": 100
          },
          "red": {
            "title": "Red shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "green": {
            "title": "Green shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          },
          "blue": {
            "title": "Blue shift (RGB units)",
            "type": "number",
            "minimum": -64,
            "maximum": 64,
            "default": 0
          }
        },
        "required": [
          "images"
        ]
      },
      "FliplineRetroBatchResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "artifact",
              "files",
              "recipe"
            ],
            "properties": {
              "artifact": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "artifactId",
                  "filename",
                  "mime",
                  "bytes",
                  "sha256",
                  "path"
                ],
                "properties": {
                  "artifactId": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "mime": {
                    "type": "string"
                  },
                  "bytes": {
                    "type": "integer"
                  },
                  "sha256": {
                    "type": "string"
                  },
                  "path": {
                    "type": "string",
                    "description": "Download path on the same site (valid for about 2 hours)."
                  }
                }
              },
              "files": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "source",
                    "width",
                    "height"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  }
                }
              },
              "recipe": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "format",
                  "version",
                  "settings"
                ],
                "properties": {
                  "format": {
                    "const": "flipline-retro"
                  },
                  "version": {
                    "const": 1
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "width",
                      "height",
                      "scale",
                      "fit",
                      "colors",
                      "palette",
                      "dither",
                      "strength",
                      "brightness",
                      "contrast",
                      "gamma",
                      "saturation",
                      "red",
                      "green",
                      "blue",
                      "alpha"
                    ],
                    "properties": {
                      "width": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "height": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 128
                      },
                      "scale": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 8
                      },
                      "fit": {
                        "enum": [
                          "contain",
                          "cover",
                          "stretch"
                        ]
                      },
                      "colors": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 32
                      },
                      "palette": {
                        "type": "array",
                        "maxItems": 32,
                        "items": {
                          "type": "string",
                          "pattern": "^#[a-fA-F0-9]{6}$"
                        }
                      },
                      "dither": {
                        "enum": [
                          "none",
                          "bayer",
                          "floyd"
                        ]
                      },
                      "strength": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 100
                      },
                      "brightness": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "contrast": {
                        "type": "number",
                        "minimum": -100,
                        "maximum": 100
                      },
                      "gamma": {
                        "type": "number",
                        "minimum": 0.2,
                        "maximum": 3
                      },
                      "saturation": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 200
                      },
                      "red": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "green": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "blue": {
                        "type": "number",
                        "minimum": -64,
                        "maximum": 64
                      },
                      "alpha": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 255
                      }
                    }
                  }
                },
                "description": "A settings-only retro conversion recipe. No images are embedded. Empty palette selects frequent adjusted image colors; batch exports resolve it from the first image and share that palette. Dithering runs in native output pixel coordinates, independent of display zoom and export scale."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "FliplineSpriteSheetInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "count": {
            "title": "Drawing count",
            "type": "integer",
            "minimum": 1,
            "maximum": 24,
            "default": 12
          },
          "width": {
            "title": "Frame width (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 1600,
            "default": 256
          },
          "height": {
            "title": "Frame height (px)",
            "type": "integer",
            "minimum": 1,
            "maximum": 1600,
            "default": 256
          },
          "columns": {
            "title": "Columns",
            "type": "integer",
            "minimum": 1,
            "maximum": 24,
            "default": 4
          },
          "padding": {
            "title": "Padding (px)",
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 2
          }
        },
        "required": []
      },
      "FliplineSpriteSheetResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "width",
              "height",
              "columns",
              "rows",
              "slots"
            ],
            "properties": {
              "width": {
                "type": "integer"
              },
              "height": {
                "type": "integer"
              },
              "columns": {
                "type": "integer"
              },
              "rows": {
                "type": "integer"
              },
              "slots": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "drawing",
                    "x",
                    "y",
                    "width",
                    "height"
                  ],
                  "properties": {
                    "drawing": {
                      "type": "integer"
                    },
                    "x": {
                      "type": "integer"
                    },
                    "y": {
                      "type": "integer"
                    },
                    "width": {
                      "type": "integer"
                    },
                    "height": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ThreadsmithDesignStatsInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "file": {
            "title": "Embroidery file",
            "description": "Tajima .dst, Brother .pes, Janome .jef or Melco .exp. Upload as {filename, contentType?, contentBase64}. Max 6144 KiB. Accepts .dst,.exp,.pes,.jef.",
            "type": "object",
            "additionalProperties": false,
            "required": [
              "filename",
              "contentBase64"
            ],
            "properties": {
              "filename": {
                "type": "string"
              },
              "contentType": {
                "type": "string"
              },
              "contentBase64": {
                "type": "string"
              }
            }
          },
          "stitches": {
            "title": "Or: stitch list (JSON)",
            "description": "Same format as threadsmith-stitch-file.",
            "type": "string",
            "maxLength": 8000000
          },
          "colors": {
            "title": "Thread colours in sewing order",
            "description": "Optional #rrggbb per colour block (DST/EXP files store no colours; PES/JEF colours are read from the file otherwise).",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 60
          },
          "spm": {
            "title": "Machine speed",
            "type": "integer",
            "minimum": 100,
            "maximum": 1500,
            "default": 700,
            "x-unit": "stitches/min"
          },
          "trimSeconds": {
            "title": "Time per trim",
            "type": "number",
            "minimum": 0,
            "maximum": 60,
            "default": 4,
            "x-unit": "s"
          },
          "colorSeconds": {
            "title": "Time per colour change",
            "type": "number",
            "minimum": 0,
            "maximum": 600,
            "default": 20,
            "x-unit": "s"
          },
          "hoop": {
            "title": "Hoop",
            "type": "string",
            "enum": [
              "50x50",
              "100x100",
              "130x180",
              "150x150",
              "160x260",
              "200x200",
              "200x300",
              "240x150",
              "360x200"
            ],
            "default": "130x180"
          },
          "densityThreshold": {
            "title": "Too-dense limit",
            "description": "Thread coverage per 1 mm cell above which the design is flagged (2.6 ≈ underlay + top + some overlap).",
            "type": "number",
            "minimum": 1,
            "maximum": 6,
            "default": 2.6,
            "x-unit": "thread layers"
          }
        },
        "required": []
      },
      "ThreadsmithDesignStatsResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "format",
              "stitches",
              "jumps",
              "trims",
              "colorChanges",
              "widthMm",
              "heightMm",
              "sewingMinutes",
              "sewingTime",
              "topThreadM",
              "bobbinThreadM",
              "longestStitchMm",
              "hoop",
              "colors",
              "density",
              "hotspots",
              "warnings"
            ],
            "properties": {
              "format": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "header": {
                "type": "object"
              },
              "stitches": {
                "type": "integer"
              },
              "jumps": {
                "type": "integer"
              },
              "trims": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "widthMm": {
                "type": "number"
              },
              "heightMm": {
                "type": "number"
              },
              "sewingMinutes": {
                "type": "number"
              },
              "sewingTime": {
                "type": "string"
              },
              "topThreadM": {
                "type": "number"
              },
              "bobbinThreadM": {
                "type": "number"
              },
              "longestStitchMm": {
                "type": "number"
              },
              "hoop": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "fits": {
                    "type": "boolean"
                  },
                  "overflowMm": {
                    "type": "number"
                  }
                },
                "required": [
                  "id",
                  "fits",
                  "overflowMm"
                ]
              },
              "colors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "hex": {
                      "type": "string"
                    },
                    "stitches": {
                      "type": "integer"
                    },
                    "topThreadM": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "name",
                    "hex",
                    "stitches",
                    "topThreadM"
                  ]
                }
              },
              "density": {
                "type": "object",
                "properties": {
                  "maxLayers": {
                    "type": "number"
                  },
                  "areaOverMm2": {
                    "type": "number"
                  },
                  "threshold": {
                    "type": "number"
                  }
                },
                "required": [
                  "maxLayers",
                  "areaOverMm2",
                  "threshold"
                ]
              },
              "hotspots": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "x": {
                      "type": "number"
                    },
                    "y": {
                      "type": "number"
                    },
                    "layers": {
                      "type": "number"
                    }
                  }
                }
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ThreadsmithDigitizeInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "source": {
            "title": "What to digitize",
            "type": "string",
            "enum": [
              "shape",
              "path",
              "svg",
              "text",
              "project"
            ],
            "default": "shape"
          },
          "shape": {
            "title": "Shape",
            "type": "string",
            "enum": [
              "ellipse",
              "rect",
              "star",
              "hexagon",
              "heart"
            ],
            "default": "ellipse"
          },
          "widthMm": {
            "title": "Shape width",
            "type": "number",
            "minimum": 2,
            "maximum": 400,
            "default": 40,
            "x-unit": "mm"
          },
          "heightMm": {
            "title": "Shape height",
            "type": "number",
            "minimum": 2,
            "maximum": 400,
            "default": 30,
            "x-unit": "mm"
          },
          "pathD": {
            "title": "Path data",
            "description": "SVG path syntax in millimetres (y down, 0,0 = hoop centre). Closed paths (Z) become fills, open paths become lines.",
            "type": "string",
            "maxLength": 200000
          },
          "svg": {
            "title": "SVG markup",
            "description": "paths, rect, circle, ellipse, line, polyline, polygon and transforms are imported; text, images and gradients are skipped with a warning.",
            "type": "string",
            "maxLength": 1000000
          },
          "fitWidth": {
            "title": "Scale SVG to width",
            "description": "Optional. Default: the SVG’s own physical size (px at 96 dpi).",
            "type": "number",
            "minimum": 5,
            "maximum": 400,
            "x-unit": "mm"
          },
          "text": {
            "title": "Text",
            "description": "A–Z, a–z, 0–9 and . , ! ? - _ ' \" : ; ( ) / & + # * =",
            "type": "string",
            "maxLength": 200
          },
          "textSize": {
            "title": "Letter height (capitals)",
            "type": "number",
            "minimum": 3,
            "maximum": 200,
            "default": 12,
            "x-unit": "mm"
          },
          "textArc": {
            "title": "Text on arc (radius)",
            "description": "0 = straight. Positive arches the baseline upwards (top of a badge), negative curves it downwards (bottom of a badge).",
            "type": "number",
            "minimum": -500,
            "maximum": 500,
            "default": 0,
            "x-unit": "mm"
          },
          "font": {
            "title": "Font style",
            "type": "string",
            "enum": [
              "single",
              "outline"
            ],
            "default": "single"
          },
          "project": {
            "title": "Project JSON",
            "description": "A .threadsmith.json project (see the app’s project schema).",
            "type": "string",
            "maxLength": 2000000
          },
          "stitchType": {
            "title": "Stitch type",
            "type": "string",
            "enum": [
              "auto",
              "running",
              "satin",
              "tatami",
              "contour",
              "motif"
            ],
            "default": "auto"
          },
          "angle": {
            "title": "Fill angle",
            "type": "number",
            "minimum": -180,
            "maximum": 180,
            "default": 45,
            "x-unit": "°"
          },
          "spacing": {
            "title": "Row spacing / satin density",
            "type": "number",
            "minimum": 0.2,
            "maximum": 3,
            "default": 0.4,
            "x-unit": "mm"
          },
          "stitchLength": {
            "title": "Stitch length",
            "type": "number",
            "minimum": 1,
            "maximum": 7,
            "default": 3.5,
            "x-unit": "mm"
          },
          "satinWidth": {
            "title": "Satin column width (lines)",
            "type": "number",
            "minimum": 0.8,
            "maximum": 10,
            "default": 3,
            "x-unit": "mm"
          },
          "pullComp": {
            "title": "Pull compensation",
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 0.2,
            "x-unit": "mm"
          },
          "underlay": {
            "title": "Underlay",
            "type": "string",
            "enum": [
              "auto",
              "none",
              "center",
              "edge",
              "center-edge",
              "edge-tatami",
              "zigzag",
              "full"
            ],
            "default": "auto"
          },
          "color": {
            "title": "Thread",
            "description": "#rrggbb or a built-in thread name such as ruby, emerald, gold, navy.",
            "type": "string",
            "maxLength": 30,
            "default": "sapphire"
          },
          "hoop": {
            "title": "Hoop",
            "type": "string",
            "enum": [
              "50x50",
              "100x100",
              "130x180",
              "150x150",
              "160x260",
              "200x200",
              "200x300",
              "240x150",
              "360x200"
            ],
            "default": "130x180"
          },
          "spm": {
            "title": "Machine speed",
            "type": "integer",
            "minimum": 100,
            "maximum": 1500,
            "default": 700,
            "x-unit": "stitches/min"
          },
          "includeStitches": {
            "title": "Include the stitch list in the result",
            "description": "Adds result.stitchList ([x, y, type] in mm) for chaining into threadsmith-stitch-file.",
            "type": "boolean",
            "default": false
          }
        },
        "required": []
      },
      "ThreadsmithDigitizeResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "objects",
              "order",
              "stitches",
              "jumps",
              "trims",
              "colorChanges",
              "widthMm",
              "heightMm",
              "sewingMinutes",
              "sewingTime",
              "topThreadM",
              "bobbinThreadM",
              "longestStitchMm",
              "hoop",
              "colors",
              "density",
              "warnings",
              "project",
              "openInApp"
            ],
            "properties": {
              "objects": {
                "type": "integer"
              },
              "order": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "stitches": {
                "type": "integer"
              },
              "jumps": {
                "type": "integer"
              },
              "trims": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "widthMm": {
                "type": "number"
              },
              "heightMm": {
                "type": "number"
              },
              "sewingMinutes": {
                "type": "number"
              },
              "sewingTime": {
                "type": "string"
              },
              "topThreadM": {
                "type": "number"
              },
              "bobbinThreadM": {
                "type": "number"
              },
              "longestStitchMm": {
                "type": "number"
              },
              "hoop": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "fits": {
                    "type": "boolean"
                  },
                  "overflowMm": {
                    "type": "number"
                  }
                },
                "required": [
                  "id",
                  "fits",
                  "overflowMm"
                ]
              },
              "colors": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "hex": {
                      "type": "string"
                    },
                    "stitches": {
                      "type": "integer"
                    },
                    "topThreadM": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "name",
                    "hex",
                    "stitches",
                    "topThreadM"
                  ]
                }
              },
              "density": {
                "type": "object",
                "properties": {
                  "maxLayers": {
                    "type": "number"
                  },
                  "areaOverMm2": {
                    "type": "number"
                  },
                  "threshold": {
                    "type": "number"
                  }
                },
                "required": [
                  "maxLayers",
                  "areaOverMm2",
                  "threshold"
                ]
              },
              "warnings": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "project": {
                "type": "object",
                "description": "Normalised Threadsmith project (open it in the app or pass it to threadsmith-stitch-file)."
              },
              "openInApp": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Threadsmith app link with the project in the #project= fragment (null when the project is too large for a URL)."
              },
              "stitchList": {
                "type": "array",
                "items": {
                  "type": "array"
                },
                "description": "[x, y, type] in mm, y down; type = stitch | jump | trim | color | end."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "ThreadsmithStitchFileInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "format": {
            "title": "File format",
            "type": "string",
            "enum": [
              "dst",
              "pes",
              "jef",
              "exp"
            ],
            "default": "dst"
          },
          "stitches": {
            "title": "Stitch list (JSON)",
            "description": "[[x, y], [x, y, \"jump\"], [x, y, \"trim\"], [x, y, \"color\"], …] in mm (y down), or {\"cmds\": […], \"colors\": [{\"name\", \"hex\"}]} from threadsmith-digitize.",
            "type": "string",
            "maxLength": 8000000
          },
          "project": {
            "title": "Or: Threadsmith project JSON",
            "description": "If given, the project is digitized first.",
            "type": "string",
            "maxLength": 2000000
          },
          "label": {
            "title": "Design label (DST/PES header, 8 characters)",
            "type": "string",
            "maxLength": 16,
            "default": "DESIGN"
          }
        },
        "required": []
      },
      "ThreadsmithStitchFileResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "format",
              "filename",
              "bytes",
              "records",
              "stitchRecords",
              "jumpRecords",
              "colorChanges",
              "splitStitches",
              "verified",
              "maxErrorMm",
              "sha256",
              "artifactUrl",
              "base64",
              "firstRecords"
            ],
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "dst",
                  "exp",
                  "pes",
                  "jef"
                ]
              },
              "filename": {
                "type": "string"
              },
              "bytes": {
                "type": "integer"
              },
              "records": {
                "type": "integer"
              },
              "stitchRecords": {
                "type": "integer"
              },
              "jumpRecords": {
                "type": "integer"
              },
              "colorChanges": {
                "type": "integer"
              },
              "splitStitches": {
                "type": "integer",
                "description": "Stitches longer than the format limit that were split into several records."
              },
              "verified": {
                "type": "boolean",
                "description": "The written file was decoded again and every stitch landed within 0.08 mm of the input."
              },
              "maxErrorMm": {
                "type": "number"
              },
              "sha256": {
                "type": "string"
              },
              "artifactUrl": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "base64": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "File content (only when no artifact store is available or the file is ≤ 64 KiB)."
              },
              "header": {
                "type": "object"
              },
              "firstRecords": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "machineThreads": {
                "type": "array",
                "description": "PES/JEF: the machine palette colour chosen for every colour block (nearest match).",
                "items": {
                  "type": "object",
                  "properties": {
                    "index": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    },
                    "hex": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksBottleneckAnalysisInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "model": {
            "title": "Model or project JSON",
            "description": "A Twinworks project (.twinworks.json, format \"twinworks-project\") or a bare model with \"nodes\" and \"links\". Times in seconds, lengths in metres. Leave empty and pick a template instead.",
            "type": "string",
            "maxLength": 400000
          },
          "template": {
            "title": "Or start from a template",
            "type": "string",
            "enum": [
              "",
              "three-station-line",
              "line-with-failures",
              "assembly-two-feeders",
              "job-shop-routings",
              "bottleneck-buffer-sizing",
              "conveyor-packaging",
              "mm1-validation"
            ],
            "default": ""
          },
          "replications": {
            "title": "Replications",
            "type": "integer",
            "minimum": 2,
            "maximum": 20,
            "default": 5
          },
          "horizonHours": {
            "title": "Run length per replication",
            "description": "Overrides the model setting.",
            "type": "number",
            "minimum": 0.1,
            "maximum": 2400,
            "x-unit": "h"
          },
          "whatIf": {
            "title": "Simulate improvement options for the top bottleneck",
            "type": "boolean",
            "default": true
          }
        },
        "required": []
      },
      "TwinworksBottleneckAnalysisResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "primary",
              "ranking",
              "recommendations",
              "baselineThroughput",
              "whatIf"
            ],
            "properties": {
              "primary": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "primaryName": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "ranking": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "rank",
                    "id",
                    "name",
                    "sole",
                    "shifting",
                    "total",
                    "activePct",
                    "utilization",
                    "dominantState",
                    "votes"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer"
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "sole": {
                      "type": "number"
                    },
                    "shifting": {
                      "type": "number"
                    },
                    "total": {
                      "type": "number"
                    },
                    "activePct": {
                      "type": "number"
                    },
                    "utilization": {
                      "type": "number"
                    },
                    "dominantState": {
                      "type": "string"
                    },
                    "votes": {
                      "type": "integer"
                    }
                  }
                }
              },
              "recommendations": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "baselineThroughput": {
                "type": "object",
                "required": [
                  "mean",
                  "lo",
                  "hi",
                  "halfWidth",
                  "n"
                ],
                "properties": {
                  "mean": {
                    "type": "number"
                  },
                  "lo": {
                    "type": "number"
                  },
                  "hi": {
                    "type": "number"
                  },
                  "halfWidth": {
                    "type": "number"
                  },
                  "n": {
                    "type": "integer"
                  }
                }
              },
              "whatIf": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "label",
                    "throughput",
                    "gain",
                    "gainPct"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string"
                    },
                    "throughput": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "gain": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "gainPct": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksBufferSweepInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "model": {
            "title": "Model or project JSON",
            "description": "A Twinworks project (.twinworks.json, format \"twinworks-project\") or a bare model with \"nodes\" and \"links\". Times in seconds, lengths in metres. Leave empty and pick a template instead.",
            "type": "string",
            "maxLength": 400000
          },
          "template": {
            "title": "Or start from a template",
            "type": "string",
            "enum": [
              "",
              "three-station-line",
              "line-with-failures",
              "assembly-two-feeders",
              "job-shop-routings",
              "bottleneck-buffer-sizing",
              "conveyor-packaging",
              "mm1-validation"
            ],
            "default": ""
          },
          "target": {
            "title": "Object id",
            "description": "Id of the buffer/machine/conveyor/source to change, or pool:<id> for an operator pool. Default for the bottleneck template: B2.",
            "type": "string",
            "maxLength": 60
          },
          "parameter": {
            "title": "Parameter",
            "type": "string",
            "enum": [
              "capacity",
              "servers",
              "cycle.value",
              "cycle.mean",
              "batchSize",
              "interval.mean",
              "interval.value",
              "speed",
              "length",
              "failure.mttr.mean",
              "failure.mtbf.mean",
              "setup.value",
              "scrapPct",
              "count"
            ],
            "default": "capacity"
          },
          "from": {
            "title": "From",
            "type": "number",
            "minimum": 0,
            "maximum": 10000000,
            "default": 1
          },
          "to": {
            "title": "To",
            "type": "number",
            "minimum": 0,
            "maximum": 10000000,
            "default": 10
          },
          "step": {
            "title": "Step",
            "type": "number",
            "minimum": 0.0001,
            "maximum": 10000000,
            "default": 1
          },
          "replications": {
            "title": "Replications per value",
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 4
          },
          "horizonHours": {
            "title": "Run length per replication",
            "description": "Overrides the model setting.",
            "type": "number",
            "minimum": 0.1,
            "maximum": 2400,
            "x-unit": "h"
          }
        },
        "required": []
      },
      "TwinworksBufferSweepResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "target",
              "parameter",
              "points",
              "knee",
              "bestThroughput"
            ],
            "properties": {
              "target": {
                "type": "string"
              },
              "parameter": {
                "type": "string"
              },
              "points": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "value",
                    "throughput",
                    "leadTime",
                    "wip"
                  ],
                  "properties": {
                    "value": {
                      "type": "number"
                    },
                    "throughput": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "leadTime": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "wip": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "costPerGoodPart": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "bottleneck": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "knee": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "Smallest value reaching 98 % of the best mean throughput."
              },
              "bestThroughput": {
                "type": "number"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksBufferAllocationInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "model": {
            "title": "Model or project JSON",
            "description": "A Twinworks project (.twinworks.json, format \"twinworks-project\") or a bare model with \"nodes\" and \"links\". Times in seconds, lengths in metres. Leave empty and pick a template instead.",
            "type": "string",
            "maxLength": 400000
          },
          "template": {
            "title": "Or start from a template",
            "type": "string",
            "enum": [
              "",
              "three-station-line",
              "line-with-failures",
              "assembly-two-feeders",
              "job-shop-routings",
              "bottleneck-buffer-sizing",
              "conveyor-packaging",
              "mm1-validation"
            ],
            "default": ""
          },
          "total": {
            "title": "Total buffer places",
            "description": "Places to distribute over the buffers. Default: the sum of the current capacities (same space, better placement).",
            "type": "integer",
            "minimum": 1,
            "maximum": 2000
          },
          "minCapacity": {
            "title": "Minimum per buffer",
            "type": "integer",
            "minimum": 1,
            "maximum": 1000,
            "default": 1
          },
          "buffers": {
            "title": "Buffer ids",
            "description": "Comma-separated ids (e.g. B1,B2,B3). Default: every buffer with a finite capacity.",
            "type": "string",
            "maxLength": 300
          },
          "replications": {
            "title": "Replications per candidate",
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "default": 3
          },
          "horizonHours": {
            "title": "Run length per replication",
            "description": "Overrides the model setting.",
            "type": "number",
            "minimum": 0.1,
            "maximum": 2400,
            "x-unit": "h"
          }
        },
        "required": []
      },
      "TwinworksBufferAllocationResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "buffers",
              "total",
              "current",
              "best",
              "gain",
              "steps",
              "evaluations"
            ],
            "properties": {
              "buffers": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "total": {
                "type": "integer"
              },
              "minCapacity": {
                "type": "integer"
              },
              "current": {
                "type": "object",
                "properties": {
                  "capacities": {
                    "type": "object"
                  },
                  "throughput": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "leadTime": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "wip": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  }
                }
              },
              "best": {
                "type": "object",
                "properties": {
                  "capacities": {
                    "type": "object"
                  },
                  "throughput": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "leadTime": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "wip": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  }
                }
              },
              "gain": {
                "type": "object",
                "required": [
                  "mean",
                  "lo",
                  "hi",
                  "halfWidth",
                  "n"
                ],
                "properties": {
                  "mean": {
                    "type": "number"
                  },
                  "lo": {
                    "type": "number"
                  },
                  "hi": {
                    "type": "number"
                  },
                  "halfWidth": {
                    "type": "number"
                  },
                  "n": {
                    "type": "integer"
                  }
                }
              },
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "step": {
                      "type": "integer"
                    },
                    "added": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "capacities": {
                      "type": "object"
                    },
                    "throughput": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              },
              "placesForCurrentThroughput": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Smallest total of well-placed buffer places that reaches the current throughput."
              },
              "evaluations": {
                "type": "integer"
              },
              "model": {
                "type": "object",
                "description": "The model with the optimised capacities."
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksSimulateInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "model": {
            "title": "Model or project JSON",
            "description": "A Twinworks project (.twinworks.json, format \"twinworks-project\") or a bare model with \"nodes\" and \"links\". Times in seconds, lengths in metres. Leave empty and pick a template instead.",
            "type": "string",
            "maxLength": 400000
          },
          "template": {
            "title": "Or start from a template",
            "type": "string",
            "enum": [
              "",
              "three-station-line",
              "line-with-failures",
              "assembly-two-feeders",
              "job-shop-routings",
              "bottleneck-buffer-sizing",
              "conveyor-packaging",
              "mm1-validation"
            ],
            "default": ""
          },
          "horizonHours": {
            "title": "Run length per replication",
            "description": "Overrides the model setting (includes the warm-up).",
            "type": "number",
            "minimum": 0.01,
            "maximum": 2400,
            "x-unit": "h"
          },
          "warmupMinutes": {
            "title": "Warm-up",
            "description": "Statistics are reset after the warm-up. Overrides the model setting.",
            "type": "number",
            "minimum": 0,
            "maximum": 144000,
            "x-unit": "min"
          },
          "seed": {
            "title": "Random seed",
            "description": "Same seed → same result. Replication r uses a seed derived from it.",
            "type": "integer",
            "minimum": 0,
            "maximum": 4294967295
          },
          "replications": {
            "title": "Replications",
            "description": "Independent runs with different seeds; used for the confidence intervals.",
            "type": "integer",
            "minimum": 1,
            "maximum": 30
          },
          "confidence": {
            "title": "Confidence level",
            "type": "string",
            "enum": [
              "",
              "0.9",
              "0.95",
              "0.99"
            ],
            "default": ""
          }
        },
        "required": []
      },
      "TwinworksSimulateResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "modelName",
              "replications",
              "confidence",
              "horizonSeconds",
              "warmupSeconds",
              "throughputPerHour",
              "leadTime",
              "wip",
              "little",
              "good",
              "scrap",
              "stations",
              "bottleneck",
              "cost"
            ],
            "properties": {
              "modelName": {
                "type": "string"
              },
              "replications": {
                "type": "integer"
              },
              "confidence": {
                "type": "number"
              },
              "horizonSeconds": {
                "type": "number"
              },
              "warmupSeconds": {
                "type": "number"
              },
              "seeds": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "throughputPerHour": {
                "type": "object",
                "required": [
                  "mean",
                  "lo",
                  "hi",
                  "halfWidth",
                  "n"
                ],
                "properties": {
                  "mean": {
                    "type": "number"
                  },
                  "lo": {
                    "type": "number"
                  },
                  "hi": {
                    "type": "number"
                  },
                  "halfWidth": {
                    "type": "number"
                  },
                  "n": {
                    "type": "integer"
                  }
                }
              },
              "leadTime": {
                "type": "object",
                "required": [
                  "mean",
                  "p50",
                  "p90",
                  "p95",
                  "max",
                  "meanCi"
                ],
                "properties": {
                  "mean": {
                    "type": "number"
                  },
                  "p50": {
                    "type": "number"
                  },
                  "p90": {
                    "type": "number"
                  },
                  "p95": {
                    "type": "number"
                  },
                  "max": {
                    "type": "number"
                  },
                  "meanCi": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  }
                }
              },
              "wip": {
                "type": "object",
                "required": [
                  "avg",
                  "max",
                  "avgCi"
                ],
                "properties": {
                  "avg": {
                    "type": "number"
                  },
                  "max": {
                    "type": "number"
                  },
                  "avgCi": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  }
                }
              },
              "little": {
                "type": "object",
                "required": [
                  "L",
                  "lambdaPerHour",
                  "W",
                  "lambdaW",
                  "relError",
                  "ok"
                ],
                "properties": {
                  "L": {
                    "type": "number"
                  },
                  "lambdaPerHour": {
                    "type": "number"
                  },
                  "W": {
                    "type": "number"
                  },
                  "lambdaW": {
                    "type": "number"
                  },
                  "relError": {
                    "type": "number"
                  },
                  "ok": {
                    "type": "boolean"
                  }
                }
              },
              "good": {
                "type": "integer"
              },
              "scrap": {
                "type": "integer"
              },
              "scrapPct": {
                "type": "number"
              },
              "stations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "id",
                    "name",
                    "type",
                    "servers",
                    "processed",
                    "throughputPerHour",
                    "utilization",
                    "states",
                    "availability",
                    "performance",
                    "quality",
                    "oee"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "servers": {
                      "type": "integer"
                    },
                    "processed": {
                      "type": "integer"
                    },
                    "throughputPerHour": {
                      "type": "number"
                    },
                    "utilization": {
                      "type": "number"
                    },
                    "utilizationCi": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "states": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "availability": {
                      "type": "number"
                    },
                    "performance": {
                      "type": "number"
                    },
                    "quality": {
                      "type": "number"
                    },
                    "oee": {
                      "type": "number"
                    },
                    "oeeCi": {
                      "type": "object",
                      "required": [
                        "mean",
                        "lo",
                        "hi",
                        "halfWidth",
                        "n"
                      ],
                      "properties": {
                        "mean": {
                          "type": "number"
                        },
                        "lo": {
                          "type": "number"
                        },
                        "hi": {
                          "type": "number"
                        },
                        "halfWidth": {
                          "type": "number"
                        },
                        "n": {
                          "type": "integer"
                        }
                      }
                    },
                    "failures": {
                      "type": "integer"
                    },
                    "setups": {
                      "type": "integer"
                    }
                  }
                }
              },
              "buffers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string"
                    },
                    "capacity": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "avgContents": {
                      "type": "number"
                    },
                    "maxContents": {
                      "type": "number"
                    },
                    "avgWait": {
                      "type": "number"
                    }
                  }
                }
              },
              "bottleneck": {
                "type": "object",
                "required": [
                  "primary",
                  "ranking",
                  "recommendations"
                ],
                "properties": {
                  "primary": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "ranking": {
                    "type": "array"
                  },
                  "recommendations": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "votes": {
                    "type": "object"
                  }
                }
              },
              "cost": {
                "type": "object",
                "required": [
                  "currency",
                  "total",
                  "costPerGoodPart"
                ],
                "properties": {
                  "currency": {
                    "type": "string"
                  },
                  "total": {
                    "type": "number"
                  },
                  "costPerGoodPart": {
                    "type": "number"
                  },
                  "costPerGoodPartCi": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "scrapCost": {
                    "type": "number"
                  },
                  "labour": {
                    "type": "number"
                  },
                  "machineTime": {
                    "type": "number"
                  },
                  "material": {
                    "type": "number"
                  },
                  "variable": {
                    "type": "number"
                  },
                  "overhead": {
                    "type": "number"
                  },
                  "revenue": {
                    "type": "number"
                  },
                  "profit": {
                    "type": "number"
                  }
                }
              },
              "openInAppUrl": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksLineBalanceInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "tasks": {
            "title": "Tasks",
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "id": {
                  "title": "Task id",
                  "type": "string",
                  "maxLength": 20
                },
                "name": {
                  "title": "Description",
                  "type": "string",
                  "maxLength": 80
                },
                "time": {
                  "title": "Time (s)",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100000
                },
                "predecessors": {
                  "title": "Predecessors (comma-separated ids)",
                  "type": "string",
                  "maxLength": 400
                }
              },
              "required": [
                "id",
                "time"
              ]
            }
          },
          "taktTime": {
            "title": "Takt time",
            "description": "Leave empty to compute it from available time ÷ demand.",
            "type": "number",
            "minimum": 0.001,
            "maximum": 10000000,
            "x-unit": "s"
          },
          "availableMinutes": {
            "title": "Available production time",
            "description": "Per period, e.g. 450 min per shift.",
            "type": "number",
            "minimum": 1,
            "maximum": 1000000,
            "x-unit": "min"
          },
          "demand": {
            "title": "Demand in that period",
            "type": "number",
            "minimum": 1,
            "maximum": 1000000000,
            "x-unit": "units"
          },
          "method": {
            "title": "Heuristic",
            "type": "string",
            "enum": [
              "best",
              "rpw",
              "lcr"
            ],
            "default": "best"
          }
        },
        "required": [
          "tasks"
        ]
      },
      "TwinworksLineBalanceResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "method",
              "taktTime",
              "totalWork",
              "theoreticalMin",
              "stationCount",
              "stations",
              "efficiency",
              "balanceDelay",
              "smoothnessIndex",
              "cycleTime",
              "maxThroughputPerHour",
              "model"
            ],
            "properties": {
              "method": {
                "type": "string"
              },
              "taktTime": {
                "type": "number"
              },
              "totalWork": {
                "type": "number"
              },
              "theoreticalMin": {
                "type": "integer"
              },
              "stationCount": {
                "type": "integer"
              },
              "stations": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": [
                    "station",
                    "tasks",
                    "time",
                    "idle",
                    "utilization"
                  ],
                  "properties": {
                    "station": {
                      "type": "integer"
                    },
                    "tasks": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "time": {
                      "type": "number"
                    },
                    "idle": {
                      "type": "number"
                    },
                    "utilization": {
                      "type": "number"
                    }
                  }
                }
              },
              "efficiency": {
                "type": "number"
              },
              "balanceDelay": {
                "type": "number"
              },
              "smoothnessIndex": {
                "type": "number"
              },
              "cycleTime": {
                "type": "number"
              },
              "maxThroughputPerHour": {
                "type": "number"
              },
              "positionalWeights": {
                "type": "object",
                "additionalProperties": {
                  "type": "number"
                }
              },
              "alternative": {
                "type": [
                  "object",
                  "null"
                ]
              },
              "model": {
                "type": "object"
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      },
      "TwinworksQueueFormulasInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "queue": {
            "title": "Queue model",
            "type": "string",
            "enum": [
              "mm1",
              "mmc",
              "md1",
              "mg1",
              "gg1"
            ],
            "default": "mm1"
          },
          "arrivalsPerHour": {
            "title": "Arrival rate",
            "type": "number",
            "minimum": 0.0001,
            "maximum": 10000000,
            "default": 60,
            "x-unit": "parts/h"
          },
          "serviceTime": {
            "title": "Mean service (cycle) time",
            "type": "number",
            "minimum": 0.0001,
            "maximum": 10000000,
            "default": 48,
            "x-unit": "s"
          },
          "servers": {
            "title": "Servers (M/M/c)",
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "default": 2
          },
          "serviceScv": {
            "title": "Service SCV (variance ÷ mean²)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 1
          },
          "arrivalScv": {
            "title": "Inter-arrival SCV (G/G/1)",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 1
          },
          "verify": {
            "title": "Verify by simulation",
            "description": "Runs the same station in Twinworks (M/M/1, M/M/c, M/D/1 only).",
            "type": "boolean",
            "default": false
          },
          "replications": {
            "title": "Replications for the check",
            "type": "integer",
            "minimum": 3,
            "maximum": 20,
            "default": 10
          }
        },
        "required": [
          "arrivalsPerHour",
          "serviceTime"
        ]
      },
      "TwinworksQueueFormulasResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "required": [
              "model",
              "rho",
              "stable",
              "Wq",
              "W",
              "Lq",
              "L",
              "exact",
              "verification"
            ],
            "properties": {
              "model": {
                "type": "string"
              },
              "rho": {
                "type": "number"
              },
              "stable": {
                "type": "boolean"
              },
              "Wq": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "W": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "Lq": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "L": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "pWait": {
                "type": "number"
              },
              "exact": {
                "type": "boolean"
              },
              "verification": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "WqSim": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "utilizationSim": {
                    "type": "object",
                    "required": [
                      "mean",
                      "lo",
                      "hi",
                      "halfWidth",
                      "n"
                    ],
                    "properties": {
                      "mean": {
                        "type": "number"
                      },
                      "lo": {
                        "type": "number"
                      },
                      "hi": {
                        "type": "number"
                      },
                      "halfWidth": {
                        "type": "number"
                      },
                      "n": {
                        "type": "integer"
                      }
                    }
                  },
                  "insideCi": {
                    "type": "boolean"
                  },
                  "replications": {
                    "type": "integer"
                  },
                  "horizonSeconds": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}