{
  "openapi": "3.1.1",
  "info": {
    "title": "Home, travel & daily life 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://hometravel.usefulweb.site"
    }
  ],
  "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": [
                "food-storage-guide",
                "household-maintenance-calendar",
                "jet-lag-planner",
                "leftover-chef",
                "moving-box-planner",
                "room-layout-optimizer",
                "travel-connection-checker",
                "will-it-fit"
              ]
            }
          }
        ],
        "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/food-storage-guide/calculate": {
      "post": {
        "operationId": "calculateFoodStorageGuide",
        "summary": "How long is food safe in the fridge or freezer? Dates from the official FoodSafety.gov chart.",
        "description": "Choose a food and the date you stored or opened it. The guide shows the refrigerator and freezer limits exactly as published in the FoodSafety.gov Cold Food Storage Chart, the resulting dates, and whether the food is fine, should be used soon, or should be discarded.\n\nMethodology:\n- Storage limits are copied verbatim from the FoodSafety.gov Cold Food Storage Chart (refrigerator at 40°F (4°C) or below, freezer at 0°F (-18°C) or below).\n- Text periods are converted to days with 1 week = 7 days, 1 month = 30 days, 1 year = 365 days; \"use immediately after thawing\" = 0 days.\n- Dates = date stored + lower limit (use-by earliest) and + upper limit (use-by latest).\n- Status (refrigerator/pantry): fine before the lower limit; use-soon from the lower limit up to the upper limit (for single values: on the last day); discard after the upper limit.\n- Freezer: the chart says freezer times are for quality only and food kept continuously at 0°F (-18°C) or below can be kept indefinitely, so the status after the upper limit is \"quality-past\", not \"discard\".\n- Items the chart marks \"Do not freeze\", \"Does not freeze well\" or \"Not recommended\" get status not-recommended.\n\nWhen in doubt, throw it out. Times assume correct storage temperatures throughout.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "food_storage_guide",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FoodStorageGuideInput"
              },
              "examples": {
                "example1": {
                  "summary": "Leftover chicken cooked 3 days ago",
                  "value": {
                    "food": "leftover-meat-poultry",
                    "storage": "fridge",
                    "dateStored": "2026-10-09",
                    "today": "2026-10-12"
                  }
                },
                "example2": {
                  "summary": "Ground beef in the freezer since summer",
                  "value": {
                    "food": "ground-meat",
                    "storage": "freezer",
                    "dateStored": "2026-06-01",
                    "today": "2026-10-12"
                  }
                },
                "example3": {
                  "summary": "Opened deli meat after a week",
                  "value": {
                    "food": "luncheon-meat-opened",
                    "storage": "fridge",
                    "dateStored": "2026-10-05",
                    "today": "2026-10-12"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FoodStorageGuideResult"
                }
              }
            }
          },
          "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/household-maintenance-calendar/calculate": {
      "post": {
        "operationId": "calculateHouseholdMaintenanceCalendar",
        "summary": "A dated home maintenance schedule for your appliances and features, as a calendar file.",
        "description": "Tick the features of your home — heating, alarms, appliances, gutters, garden, pool, car — and get a dated maintenance plan for up to 24 months, grouped by month, with safety intervals backed by official guidance and a recurring-event calendar file to import.\n\nMethodology:\n- Tasks come from an editorial catalog of generic maintenance best practice; a task is included if it applies to every home, to your home type, or to a feature you ticked.\n- Interval tasks (every N months): the first date is the start date for monthly tasks, and start date + (catalog position × 3 mod 28) days for longer intervals so that work is spread out; then every N months. A first date on the 29th–31st moves to the 28th of the following month so the calendar recurrence is valid in every month.\n- Day-interval tasks (e.g. weekly pool checks) repeat every N days from the start date.\n- Seasonal tasks fall on the 1st of their months (northern hemisphere months; +6 months in the southern hemisphere).\n- Only dates from the start date to the day before start + N months are listed. The .ics file has one all-day recurring VEVENT per task (RRULE FREQ=MONTHLY/DAILY/YEARLY with UNTIL), CRLF line endings, folded lines and UIDs derived from the inputs.\n- Safety intervals (smoke and CO alarm tests and batteries, chimney inspection, septic inspection, tyre pressure) follow the cited official guidance; all other intervals are editorial — your manufacturer’s manual takes precedence.\n\nGeneric reminders, not a substitute for your appliance manuals, local regulations or professional inspections. Do not climb on roofs; hire professionals for gas, electrical and roof work.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "household_maintenance_calendar",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HouseholdMaintenanceCalendarInput"
              },
              "examples": {
                "example1": {
                  "summary": "Apartment, 12 months",
                  "value": {
                    "homeType": "apartment",
                    "features": [
                      "smoke-detectors",
                      "co-detectors",
                      "gas-heating",
                      "dishwasher",
                      "washing-machine",
                      "fridge"
                    ],
                    "hemisphere": "north",
                    "startDate": "2026-11-01",
                    "months": 12
                  }
                },
                "example2": {
                  "summary": "House with garden, pool and car, 24 months",
                  "value": {
                    "homeType": "house",
                    "features": [
                      "smoke-detectors",
                      "heat-pump",
                      "water-heater",
                      "dryer",
                      "gutters",
                      "chimney",
                      "garden",
                      "pool",
                      "septic",
                      "solar",
                      "car"
                    ],
                    "hemisphere": "north",
                    "startDate": "2026-10-15",
                    "months": 24
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HouseholdMaintenanceCalendarResult"
                }
              }
            }
          },
          "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/jet-lag-planner/calculate": {
      "post": {
        "operationId": "calculateJetLagPlanner",
        "summary": "A day-by-day sleep, light and caffeine plan to beat jet lag, with calendar export.",
        "description": "Pick your home and destination time zones, travel date and usual sleep times. The planner computes the real time difference for that date (including daylight saving), shifts your sleep before departure, and lists when to seek or avoid bright light and when to stop caffeine until you are adapted. Download it as a calendar file.\n\nMethodology:\n- Time difference = destination UTC offset (at noon UTC on the arrival date) − home UTC offset (at noon UTC on the departure date), via the Intl time-zone database, so daylight saving is included. It is normalised to −12…+12 h; positive = eastward (clock must advance), negative = westward (delay).\n- Pre-flight: sleep and wake move 1 hour per day toward destination time (earlier for east, later for west), as suggested in the CDC Yellow Book jet-lag chapter for the 2–3 days before travel; days beyond what is needed are skipped.\n- After arrival you sleep on local time; the body clock is assumed to catch up 1 h/day after eastward and 1.5 h/day after westward travel (common planning rule of thumb, not an individual prediction).\n- Core body temperature minimum (CBTmin) is estimated at 2.5 h before the body’s habitual wake time (2 h for early birds, 3 h for night owls).\n- Light rule (phase-response curve): to advance (east) seek bright light in the 3 h after CBTmin and avoid it in the 3 h before; to delay (west) seek light in the 3 h before CBTmin and avoid it in the 3 h after. If a window falls while you sleep, use only its waking part.\n- Caffeine cut-off = 6 h before planned bedtime (CDC Yellow Book example).\n- The .ics file contains bedtime, light and caffeine events in UTC with UIDs derived from a hash of your inputs, CRLF line endings and folded lines.\n\nGeneral planning guidance, not medical advice. Ask a clinician before using melatonin or sleep medication.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "jet_lag_planner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JetLagPlannerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Berlin → New York (westward, 6 h)",
                  "value": {
                    "homeZone": "Europe/Berlin",
                    "destinationZone": "America/New_York",
                    "departureDate": "2026-11-10",
                    "bedtime": "23:00",
                    "wakeTime": "07:00",
                    "daysBefore": 3,
                    "chronotype": "intermediate"
                  }
                },
                "example2": {
                  "summary": "London → Tokyo (eastward, 9 h)",
                  "value": {
                    "homeZone": "Europe/London",
                    "destinationZone": "Asia/Tokyo",
                    "departureDate": "2027-04-02",
                    "arrivalDate": "2027-04-03",
                    "bedtime": "23:30",
                    "wakeTime": "07:30",
                    "daysBefore": 3,
                    "chronotype": "late"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JetLagPlannerResult"
                }
              }
            }
          },
          "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/leftover-chef/calculate": {
      "post": {
        "operationId": "calculateLeftoverChef",
        "summary": "Turn leftovers and fridge odds and ends into 5 recipe ideas, using what expires first.",
        "description": "List what is in your fridge and cupboard, mark what must be used soon, and set diet, equipment and time. Leftover Chef matches your ingredients against 37 flexible recipe templates (frittata, fried rice, soup, curry…) and ranks the five that use the most of what you have with the fewest missing items.\n\nMethodology:\n- Each ingredient line is normalised: lower-cased, quantities/units and words like \"leftover\", \"fresh\", \"chopped\" removed, plurals singularised, synonyms mapped (courgette → zucchini, garbanzo → chickpea…), then classified (vegetable, protein, grain, dairy…) with diet flags. \"Vegan …\", \"dairy-free …\", \"gluten-free …\" prefixes clear the matching flags.\n- Templates are editorial: each has core requirements (one of several alternatives), flexible slots (e.g. 1–4 vegetables, optional cheese), equipment alternatives and a total time. Salt, pepper, cooking oil, water and dried spices are assumed available.\n- A template is eligible if its time ≤ your maximum, you have one of its equipment options, every unmet core requirement still has a diet-compatible alternative, at least one of your ingredients is used and at most 3 items are missing.\n- Core requirements are filled first, then slots; use-soon items are always tried first and each ingredient is used once per recipe.\n- Score = 50 × share of core requirements you have + 30 × share of your use-soon items used + 3 × core requirements you have + 4 × number of your ingredients used (max 8) − 12 × missing items. Ties: fewer missing, shorter time, then template id.\n\nGeneric cooking templates, not tested recipes. Check that leftovers are still safe to eat and cook meat, fish and eggs thoroughly.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "leftover_chef",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LeftoverChefInput"
              },
              "examples": {
                "example1": {
                  "summary": "Eggs, rice and wilting veg",
                  "value": {
                    "ingredients": [
                      "4 eggs",
                      "leftover rice",
                      "half a courgette",
                      "spinach",
                      "cheddar",
                      "spring onions",
                      "soy sauce"
                    ],
                    "useSoon": [
                      "spinach",
                      "courgette"
                    ],
                    "equipment": [
                      "stove",
                      "oven"
                    ],
                    "maxMinutes": 30
                  }
                },
                "example2": {
                  "summary": "Vegan pantry dinner",
                  "value": {
                    "ingredients": [
                      "chickpeas",
                      "tinned tomatoes",
                      "onion",
                      "garlic",
                      "coconut milk",
                      "curry paste",
                      "rice",
                      "sweet potato",
                      "kale"
                    ],
                    "useSoon": [
                      "kale"
                    ],
                    "diet": [
                      "vegan"
                    ],
                    "equipment": [
                      "stove",
                      "oven"
                    ],
                    "maxMinutes": 45
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeftoverChefResult"
                }
              }
            }
          },
          "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/moving-box-planner/calculate": {
      "post": {
        "operationId": "calculateMovingBoxPlanner",
        "summary": "How many moving boxes, how much tape, which truck and when to start packing.",
        "description": "List your rooms and how much stuff you own. The planner estimates boxes by size, packing supplies, load volume, a suitable truck class, crew size and hours, optional cost, and a packing schedule counting back from moving day. All counts are transparent rules of thumb you can check.\n\nMethodology:\n- Boxes per room = editorial base count for a medium room at average density × room size factor (small 0.7, medium 1, large 1.4) × stuff factor (minimalist 0.7, average 1, collector 1.4), rounded up per box type. One extra wardrobe box per person for hanging clothes.\n- Box sizes: small 40×30×30 cm (0.036 m³, books/heavy), medium 50×40×40 cm (0.08 m³), large 60×45×45 cm (0.1215 m³, light/bulky), wardrobe 50×50×120 cm (0.3 m³).\n- Furniture allowance per room (medium): bedroom 4, living 6, kitchen 2, bathroom 0.3, office 2.5, kids 3, garage 3, storage 1.5 m³, scaled by room size and stuff factor. Load volume = (boxes + furniture) × 1.15 for imperfect stacking.\n- Truck: smallest vehicle class whose nominal volume ≥ load (panel van 12, Luton 20, 7.5 t 35, 12 t 50, 18 t 70 m³); larger loads need multiple trips of the 18 t class.\n- Crew: 2 people up to 20 m³, 3 up to 40 m³, 4 above. Loading and unloading each at 2.5 m³ per person-hour, +10% per floor without elevator; driving at 60 km/h average one way.\n- Supplies: 3 m of tape per box on 50 m rolls (+1 spare roll), packing paper and bubble wrap per box by room type (kitchen and living highest), 1 marker per 2 people (min 2).\n- Packing pace: 10 boxes per day; rooms are packed in the order storage → garage → office → kids → living → bedroom → bathroom → kitchen, finishing the day before the move.\n\nRules of thumb, not a quote. Professional movers survey your home for a binding estimate.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "moving_box_planner",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MovingBoxPlannerInput"
              },
              "examples": {
                "example1": {
                  "summary": "2-bedroom flat, couple",
                  "value": {
                    "rooms": [
                      {
                        "type": "bedroom",
                        "size": "medium"
                      },
                      {
                        "type": "bedroom",
                        "size": "small"
                      },
                      {
                        "type": "living",
                        "size": "medium"
                      },
                      {
                        "type": "kitchen",
                        "size": "medium"
                      },
                      {
                        "type": "bathroom",
                        "size": "small"
                      }
                    ],
                    "people": 2,
                    "density": "average",
                    "distanceKm": 15,
                    "floorsWithoutElevator": 3,
                    "moveDate": "2026-11-28",
                    "currency": "EUR"
                  }
                },
                "example2": {
                  "summary": "Family house with movers",
                  "value": {
                    "rooms": [
                      {
                        "type": "bedroom",
                        "size": "large"
                      },
                      {
                        "type": "kids",
                        "size": "medium"
                      },
                      {
                        "type": "kids",
                        "size": "medium"
                      },
                      {
                        "type": "living",
                        "size": "large"
                      },
                      {
                        "type": "kitchen",
                        "size": "large"
                      },
                      {
                        "type": "office",
                        "size": "medium"
                      },
                      {
                        "type": "bathroom",
                        "size": "medium"
                      },
                      {
                        "type": "garage",
                        "size": "medium"
                      }
                    ],
                    "people": 4,
                    "density": "collector",
                    "distanceKm": 250,
                    "floorsWithoutElevator": 1,
                    "moveDate": "2027-03-15",
                    "moverHourlyRate": 45,
                    "truckHourlyRate": 30,
                    "currency": "EUR"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MovingBoxPlannerResult"
                }
              }
            }
          },
          "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/room-layout-optimizer/calculate": {
      "post": {
        "operationId": "calculateRoomLayoutOptimizer",
        "summary": "Auto-arrange furniture in a room around doors, windows and a clear walkway.",
        "description": "Give the room size, doors, windows and your furniture with the clearance each piece needs. A deterministic grid search places everything inside the room without overlaps, keeps door swings and a walkway clear, prefers walls and windows where requested and returns coordinates plus a text floor plan.\n\nMethodology:\n- The room is rasterised into grid cells (default 10 cm); furniture sizes, clearances and door zones are rounded up to whole cells, the room size down.\n- Each door gets a keep-clear square (door width × door width) inside the room in front of it, approximating the quarter-circle swing.\n- Each item can face any of the 4 directions; its body must lie inside the room and not overlap other bodies, other clearance zones or door squares; its front clearance zone must lie inside the room and not overlap bodies. \"Back against wall\" items must touch the wall behind them.\n- Walkway: the free floor is eroded by a square of the walkway width; squares connected to a door square form the reachable walkway. Every item’s clearance zone (or the cell strip in front of it) must touch that walkway.\n- Search: items are placed largest-area first with a beam search (4 partial layouts kept). For each layout, every valid grid position is pre-scored (wall hugging, corner use, distance to the nearest window for \"prefer near window\" items); the 25 best are checked for walkway access and scored.\n- Score = 1000 × items placed + 10 × reachable walkway m² + 2 per wall item in a corner − 3 × distance (m) of free-standing items from the nearest wall − 5 × distance (m) from window-loving items to the nearest window. Ties break by input order, then north-to-south, west-to-east position.\n\nRectangles only; radiators, sockets, sloped ceilings and personal taste are not considered. Treat the result as a starting point.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "room_layout_optimizer",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoomLayoutOptimizerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Bedroom with desk by the window",
                  "value": {
                    "roomWidth": 400,
                    "roomDepth": 350,
                    "gridCm": 10,
                    "walkwayCm": 60,
                    "openings": [
                      {
                        "kind": "door",
                        "wall": "south",
                        "offset": 20,
                        "width": 80
                      },
                      {
                        "kind": "window",
                        "wall": "north",
                        "offset": 150,
                        "width": 120
                      }
                    ],
                    "furniture": [
                      {
                        "name": "Double bed",
                        "width": 160,
                        "depth": 200,
                        "needsWall": true,
                        "clearanceFront": 60,
                        "nearWindow": false
                      },
                      {
                        "name": "Wardrobe",
                        "width": 150,
                        "depth": 60,
                        "needsWall": true,
                        "clearanceFront": 70,
                        "nearWindow": false
                      },
                      {
                        "name": "Desk",
                        "width": 120,
                        "depth": 60,
                        "needsWall": true,
                        "clearanceFront": 70,
                        "nearWindow": true
                      }
                    ]
                  }
                },
                "example2": {
                  "summary": "Living room",
                  "value": {
                    "roomWidth": 500,
                    "roomDepth": 420,
                    "gridCm": 10,
                    "walkwayCm": 70,
                    "openings": [
                      {
                        "kind": "door",
                        "wall": "west",
                        "offset": 30,
                        "width": 90
                      },
                      {
                        "kind": "window",
                        "wall": "east",
                        "offset": 120,
                        "width": 180
                      }
                    ],
                    "furniture": [
                      {
                        "name": "Sofa",
                        "width": 220,
                        "depth": 95,
                        "needsWall": true,
                        "clearanceFront": 80,
                        "nearWindow": false
                      },
                      {
                        "name": "TV unit",
                        "width": 180,
                        "depth": 45,
                        "needsWall": true,
                        "clearanceFront": 50,
                        "nearWindow": false
                      },
                      {
                        "name": "Armchair",
                        "width": 85,
                        "depth": 85,
                        "needsWall": false,
                        "clearanceFront": 50,
                        "nearWindow": true
                      },
                      {
                        "name": "Bookshelf",
                        "width": 100,
                        "depth": 35,
                        "needsWall": true,
                        "clearanceFront": 50,
                        "nearWindow": false
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoomLayoutOptimizerResult"
                }
              }
            }
          },
          "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/travel-connection-checker/calculate": {
      "post": {
        "operationId": "calculateTravelConnectionChecker",
        "summary": "Estimate your odds of making a flight connection, step by step.",
        "description": "Enter your arrival and onward departure times and what you have to do in between (terminal change, passport control, security, baggage). The checker adds up transparent time ranges for each step, compares them with your connection time and estimates the chance of making it under a simple inbound-delay model. It is an estimate, never a guarantee.\n\nMethodology:\n- Available time = scheduled departure − scheduled arrival − gate-closure minutes.\n- Required time = deplaning + walking (+ terminal change) + optional immigration, security and baggage re-check, each an editorial [best, typical, worst] range by airport size; walking and terminal transfer are multiplied by 1.4 (slow) / 1.0 / 0.8 (fast).\n- Buffer = available − required, for each of best/typical/worst.\n- Inbound delay model: on time with probability p, otherwise delay ~ Exponential(mean delay). P(miss) = (1 − p)·e^(−buffer/mean) if buffer ≥ 0, else 100%. P(make) = 1 − P(miss). Early arrivals and onward-flight delays are ignored (both would help).\n- Overall chance: total processing time is treated as a triangular distribution between the best, typical and worst totals, and P(make) is averaged over it numerically (400 slices). The best/worst columns show the chance if processing is at its fastest/slowest.\n- Verdict from the overall chance: ≥ 95% comfortable, ≥ 85% ok, ≥ 70% tight, ≥ 50% risky, below that very risky.\n\nA planning estimate from simplified assumptions — not a guarantee. Airline, airport and border procedures and queues vary widely.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "travel_connection_checker",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TravelConnectionCheckerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Schengen → non-Schengen at a large hub, 70 min",
                  "value": {
                    "arrival": "2026-11-03T09:40",
                    "departure": "2026-11-03T10:50",
                    "terminalChange": true,
                    "immigration": true,
                    "securityRecheck": false,
                    "baggageRecheck": false,
                    "airportSize": "large",
                    "walkingSpeed": "average",
                    "gateClosesMinutes": 15,
                    "onTimeProbability": 75,
                    "meanDelayMinutes": 35,
                    "officialMct": 50
                  }
                },
                "example2": {
                  "summary": "US arrival with baggage recheck, 2 h 30",
                  "value": {
                    "arrival": "2026-12-18T14:05",
                    "departure": "2026-12-18T16:35",
                    "terminalChange": true,
                    "immigration": true,
                    "securityRecheck": true,
                    "baggageRecheck": true,
                    "airportSize": "mega",
                    "walkingSpeed": "slow",
                    "gateClosesMinutes": 15,
                    "onTimeProbability": 75,
                    "meanDelayMinutes": 35
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TravelConnectionCheckerResult"
                }
              }
            }
          },
          "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/will-it-fit/calculate": {
      "post": {
        "operationId": "calculateWillItFit",
        "summary": "Check if a sofa, wardrobe or box fits through doors, corners, stairs and lifts.",
        "description": "Enter the item’s length, width and height and the measurements along the delivery path. Will It Fit? tries every orientation, in-plane diagonal tilts and the classic moving-sofa corner calculation, then reports pass/fail per checkpoint with spare centimetres and how to carry the item.\n\nMethodology:\n- The item is treated as a rigid rectangular box; the safety clearance is subtracted from every opening dimension.\n- Doors, stairs and elevator doors: for each of the 3 choices of travel axis, the remaining two sides form the cross-section; it is rotated in the opening plane from 0° to 90° in 0.25° steps (bounding box p·cosθ+q·sinθ by p·sinθ+q·cosθ) and the best margin is kept — this covers the \"diagonal\" tilt through a door.\n- Corner: the classic moving-sofa formula for a right-angle turn between corridors of widths a and b gives the longest flat length L_max(w) = min over θ of a/sinθ + b/cosθ − w/(sinθ·cosθ), evaluated numerically in 0.05° steps.\n- Corner, 3D: every side may stand vertical if it fits under the ceiling; the item may also be tilted upward by φ (constant during the turn) so its plan length becomes L·cosφ + h·sinφ while L·sinφ + h·cosφ stays under the ceiling. Fixed-tilt is a conservative approximation of real manoeuvring.\n- Elevator cabin and room: one side stays horizontal while the other two stand in a vertical plane tilted by φ = 0…90° (1° steps): height p·sinφ + q·cosφ must fit under the ceiling and the footprint (p·cosφ + q·sinφ) × side is rotated on the floor (0.25° steps) to fit the plan — this covers upright placement, standing diagonally in the cabin and leaning.\n- Stairs are modelled as an opening of stair width × headroom; landings and turns of the stairwell are not modelled.\n\nGeometric estimate for rigid boxes. Real items have legs, curves and soft parts; real people need grip room. Measure twice.",
        "tags": [
          "home-travel"
        ],
        "x-mcp-tool": "will_it_fit",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WillItFitInput"
              },
              "examples": {
                "example1": {
                  "summary": "3-seat sofa through a flat door and corridor",
                  "value": {
                    "itemLength": 210,
                    "itemWidth": 90,
                    "itemHeight": 85,
                    "clearance": 2,
                    "doorWidth": 80,
                    "doorHeight": 200,
                    "hallwayWidthBefore": 100,
                    "hallwayWidthAfter": 100,
                    "ceilingHeight": 250,
                    "roomWidth": 400,
                    "roomDepth": 350,
                    "roomHeight": 250
                  }
                },
                "example2": {
                  "summary": "Wardrobe box via elevator and stairs",
                  "value": {
                    "itemLength": 200,
                    "itemWidth": 60,
                    "itemHeight": 50,
                    "clearance": 3,
                    "doorWidth": 76,
                    "doorHeight": 198,
                    "ceilingHeight": 250,
                    "stairsWidth": 90,
                    "stairsHeadroom": 200,
                    "elevatorDoorWidth": 80,
                    "elevatorDoorHeight": 200,
                    "elevatorCabinWidth": 110,
                    "elevatorCabinDepth": 140,
                    "elevatorCabinHeight": 210
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WillItFitResult"
                }
              }
            }
          },
          "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"
                }
              }
            }
          }
        }
      },
      "FoodStorageGuideInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "food": {
            "title": "Food",
            "type": "string",
            "enum": [
              "salads",
              "hot-dogs-opened",
              "hot-dogs-unopened",
              "luncheon-meat-opened",
              "luncheon-meat-unopened",
              "bacon",
              "sausage-raw",
              "sausage-cooked",
              "sausage-purchased-frozen",
              "ground-meat",
              "steaks",
              "chops",
              "roasts",
              "ham-fresh-uncooked",
              "ham-fresh-cooked",
              "ham-cured-uncooked",
              "ham-vacuum-sealed",
              "ham-cooked-whole",
              "ham-cooked-sliced",
              "country-ham-cooked",
              "canned-ham-keep-refrigerated",
              "canned-ham-shelf-stable-opened",
              "canned-ham-shelf-stable-unopened",
              "prosciutto",
              "poultry-whole",
              "poultry-pieces",
              "fatty-fish",
              "lean-fish-cod",
              "lean-fish-pollock",
              "crab-meat",
              "lobster",
              "live-crab-lobster",
              "live-clams",
              "shrimp",
              "shucked-clams",
              "squid",
              "eggs-in-shell",
              "egg-whites-yolks",
              "egg-frozen-in-shell",
              "hard-cooked-eggs",
              "egg-substitute-liquid-unopened",
              "egg-substitute-liquid-opened",
              "egg-substitute-frozen-unopened",
              "egg-substitute-frozen-opened",
              "egg-casseroles",
              "eggnog-commercial",
              "eggnog-homemade",
              "pie-pumpkin-pecan",
              "pie-custard-chiffon",
              "quiche",
              "soups-stews",
              "leftover-meat-poultry",
              "chicken-nuggets",
              "pizza"
            ],
            "default": "leftover-meat-poultry"
          },
          "storage": {
            "title": "Where is it stored?",
            "type": "string",
            "enum": [
              "fridge",
              "freezer",
              "pantry"
            ],
            "default": "fridge"
          },
          "dateStored": {
            "title": "Date stored / opened / cooked",
            "description": "Leave empty to use today.",
            "type": "string",
            "format": "date"
          },
          "today": {
            "title": "Today (optional)",
            "description": "Defaults to the current date.",
            "type": "string",
            "format": "date"
          }
        },
        "required": []
      },
      "FoodStorageGuideResult": {
        "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": [
              "food",
              "type",
              "storage",
              "dateStored",
              "today",
              "daysElapsed",
              "status",
              "methods"
            ],
            "properties": {
              "food": {
                "type": "string"
              },
              "type": {
                "type": "string"
              },
              "storage": {
                "type": "string",
                "enum": [
                  "fridge",
                  "freezer",
                  "pantry"
                ]
              },
              "dateStored": {
                "type": "string",
                "format": "date"
              },
              "today": {
                "type": "string",
                "format": "date"
              },
              "daysElapsed": {
                "type": "integer"
              },
              "status": {
                "type": "string",
                "enum": [
                  "fine",
                  "use-soon",
                  "discard",
                  "quality-past",
                  "not-recommended",
                  "see-note",
                  "no-data"
                ]
              },
              "methods": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "method",
                    "published",
                    "minDays",
                    "maxDays",
                    "useByEarliest",
                    "useByLatest",
                    "condition",
                    "orUseByDate",
                    "status",
                    "qualityOnly"
                  ],
                  "properties": {
                    "method": {
                      "type": "string",
                      "enum": [
                        "fridge",
                        "freezer",
                        "pantry"
                      ]
                    },
                    "published": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Verbatim chart text."
                    },
                    "minDays": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "maxDays": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "useByEarliest": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date",
                      "description": "Stored date + lower limit."
                    },
                    "useByLatest": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date",
                      "description": "Stored date + upper limit."
                    },
                    "condition": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "e.g. \"After baking\" – the period starts after that event."
                    },
                    "orUseByDate": {
                      "type": "boolean",
                      "description": "Chart says to follow the package \"use by\" date as alternative."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "fine",
                        "use-soon",
                        "discard",
                        "quality-past",
                        "not-recommended",
                        "see-note",
                        "no-data"
                      ]
                    },
                    "qualityOnly": {
                      "type": "boolean",
                      "description": "True for freezer times: the chart states they are for quality only."
                    }
                  }
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      },
      "HouseholdMaintenanceCalendarInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "homeType": {
            "title": "Home type",
            "type": "string",
            "enum": [
              "apartment",
              "house",
              "townhouse"
            ],
            "default": "apartment"
          },
          "features": {
            "title": "Your home has",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "gas-heating",
                "heat-pump",
                "ac",
                "smoke-detectors",
                "co-detectors",
                "water-heater",
                "dishwasher",
                "washing-machine",
                "dryer",
                "fridge",
                "gutters",
                "chimney",
                "garden",
                "pool",
                "septic",
                "solar",
                "car"
              ]
            },
            "default": [
              "smoke-detectors",
              "dishwasher",
              "washing-machine",
              "fridge"
            ]
          },
          "hemisphere": {
            "title": "Hemisphere (for seasons)",
            "type": "string",
            "enum": [
              "north",
              "south"
            ],
            "default": "north"
          },
          "startDate": {
            "title": "Start date",
            "type": "string",
            "format": "date"
          },
          "months": {
            "title": "Months to plan",
            "type": "integer",
            "minimum": 1,
            "maximum": 24,
            "default": 12
          }
        },
        "required": [
          "startDate"
        ]
      },
      "HouseholdMaintenanceCalendarResult": {
        "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": [
              "startDate",
              "endDate",
              "taskCount",
              "occurrenceCount",
              "tasks",
              "occurrences"
            ],
            "properties": {
              "startDate": {
                "type": "string",
                "format": "date"
              },
              "endDate": {
                "type": "string",
                "format": "date"
              },
              "taskCount": {
                "type": "integer",
                "minimum": 0
              },
              "occurrenceCount": {
                "type": "integer",
                "minimum": 0
              },
              "tasks": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "title",
                    "category",
                    "interval",
                    "rrule"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string"
                    },
                    "interval": {
                      "type": "string"
                    },
                    "rrule": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string",
                      "format": "uri"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              },
              "occurrences": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "date",
                    "taskId",
                    "title",
                    "category"
                  ],
                  "properties": {
                    "date": {
                      "type": "string",
                      "format": "date"
                    },
                    "taskId": {
                      "type": "string"
                    },
                    "title": {
                      "type": "string"
                    },
                    "category": {
                      "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"
              }
            }
          }
        }
      },
      "JetLagPlannerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "homeZone": {
            "title": "Home time zone",
            "type": "string",
            "enum": [
              "Pacific/Honolulu",
              "America/Anchorage",
              "America/Los_Angeles",
              "America/Denver",
              "America/Phoenix",
              "America/Chicago",
              "America/Mexico_City",
              "America/New_York",
              "America/Toronto",
              "America/Bogota",
              "America/Lima",
              "America/Santiago",
              "America/Sao_Paulo",
              "America/Argentina/Buenos_Aires",
              "Atlantic/Reykjavik",
              "Europe/London",
              "Europe/Lisbon",
              "Europe/Dublin",
              "Europe/Paris",
              "Europe/Berlin",
              "Europe/Madrid",
              "Europe/Rome",
              "Europe/Zurich",
              "Europe/Vienna",
              "Europe/Amsterdam",
              "Europe/Stockholm",
              "Europe/Warsaw",
              "Europe/Athens",
              "Europe/Istanbul",
              "Europe/Moscow",
              "Africa/Cairo",
              "Africa/Johannesburg",
              "Africa/Lagos",
              "Africa/Nairobi",
              "Asia/Dubai",
              "Asia/Karachi",
              "Asia/Kolkata",
              "Asia/Bangkok",
              "Asia/Singapore",
              "Asia/Shanghai",
              "Asia/Hong_Kong",
              "Asia/Seoul",
              "Asia/Tokyo",
              "Australia/Perth",
              "Australia/Sydney",
              "Pacific/Auckland"
            ],
            "default": "Europe/Berlin"
          },
          "destinationZone": {
            "title": "Destination time zone",
            "type": "string",
            "enum": [
              "Pacific/Honolulu",
              "America/Anchorage",
              "America/Los_Angeles",
              "America/Denver",
              "America/Phoenix",
              "America/Chicago",
              "America/Mexico_City",
              "America/New_York",
              "America/Toronto",
              "America/Bogota",
              "America/Lima",
              "America/Santiago",
              "America/Sao_Paulo",
              "America/Argentina/Buenos_Aires",
              "Atlantic/Reykjavik",
              "Europe/London",
              "Europe/Lisbon",
              "Europe/Dublin",
              "Europe/Paris",
              "Europe/Berlin",
              "Europe/Madrid",
              "Europe/Rome",
              "Europe/Zurich",
              "Europe/Vienna",
              "Europe/Amsterdam",
              "Europe/Stockholm",
              "Europe/Warsaw",
              "Europe/Athens",
              "Europe/Istanbul",
              "Europe/Moscow",
              "Africa/Cairo",
              "Africa/Johannesburg",
              "Africa/Lagos",
              "Africa/Nairobi",
              "Asia/Dubai",
              "Asia/Karachi",
              "Asia/Kolkata",
              "Asia/Bangkok",
              "Asia/Singapore",
              "Asia/Shanghai",
              "Asia/Hong_Kong",
              "Asia/Seoul",
              "Asia/Tokyo",
              "Australia/Perth",
              "Australia/Sydney",
              "Pacific/Auckland"
            ],
            "default": "America/New_York"
          },
          "homeZoneCustom": {
            "title": "Other home zone (IANA name)",
            "description": "Overrides the list if filled in.",
            "type": "string",
            "maxLength": 64
          },
          "destinationZoneCustom": {
            "title": "Other destination zone (IANA name)",
            "description": "Overrides the list if filled in.",
            "type": "string",
            "maxLength": 64
          },
          "departureDate": {
            "title": "Departure date",
            "type": "string",
            "format": "date"
          },
          "arrivalDate": {
            "title": "Arrival date (local)",
            "description": "Defaults to the departure date.",
            "type": "string",
            "format": "date"
          },
          "bedtime": {
            "title": "Usual bedtime",
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "default": "23:00"
          },
          "wakeTime": {
            "title": "Usual wake time",
            "type": "string",
            "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$",
            "default": "07:00"
          },
          "daysBefore": {
            "title": "Days to pre-adjust before departure",
            "type": "integer",
            "minimum": 0,
            "maximum": 7,
            "default": 3
          },
          "chronotype": {
            "title": "Chronotype",
            "type": "string",
            "enum": [
              "early",
              "intermediate",
              "late"
            ],
            "default": "intermediate"
          }
        },
        "required": [
          "departureDate"
        ]
      },
      "JetLagPlannerResult": {
        "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": [
              "homeZone",
              "destinationZone",
              "offsetMinutes",
              "direction",
              "preShiftMinutes",
              "daysToAdapt",
              "schedule"
            ],
            "properties": {
              "homeZone": {
                "type": "string"
              },
              "destinationZone": {
                "type": "string"
              },
              "offsetMinutes": {
                "type": "integer",
                "description": "Destination UTC offset − home UTC offset on the travel dates, normalised to (−720, 720]."
              },
              "direction": {
                "type": "string",
                "enum": [
                  "east",
                  "west",
                  "none"
                ]
              },
              "preShiftMinutes": {
                "type": "integer",
                "minimum": 0
              },
              "daysToAdapt": {
                "type": "integer",
                "minimum": 0,
                "description": "Days after arrival until the body clock is estimated to match local time."
              },
              "schedule": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "date",
                    "phase",
                    "clock",
                    "bedtime",
                    "wakeTime",
                    "bodyTempMinimum",
                    "seekLight",
                    "avoidLight",
                    "caffeineCutoff",
                    "remainingShiftHours"
                  ],
                  "properties": {
                    "date": {
                      "type": "string",
                      "format": "date"
                    },
                    "phase": {
                      "type": "string",
                      "enum": [
                        "pre-flight",
                        "travel-day",
                        "post-arrival"
                      ]
                    },
                    "clock": {
                      "type": "string",
                      "enum": [
                        "home",
                        "destination"
                      ],
                      "description": "Which local time the times are expressed in."
                    },
                    "bedtime": {
                      "type": "string",
                      "pattern": "^\\d{2}:\\d{2}$"
                    },
                    "wakeTime": {
                      "type": "string",
                      "pattern": "^\\d{2}:\\d{2}$"
                    },
                    "bodyTempMinimum": {
                      "type": "string",
                      "pattern": "^\\d{2}:\\d{2}$"
                    },
                    "seekLight": {
                      "type": "string"
                    },
                    "avoidLight": {
                      "type": "string"
                    },
                    "caffeineCutoff": {
                      "type": "string",
                      "pattern": "^\\d{2}:\\d{2}$"
                    },
                    "remainingShiftHours": {
                      "type": "number",
                      "minimum": 0
                    }
                  }
                }
              }
            }
          },
          "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"
              }
            }
          }
        }
      },
      "LeftoverChefInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ingredients": {
            "title": "Ingredients you have",
            "description": "One per line, e.g. \"2 eggs\", \"leftover rice\", \"half a courgette\".",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 60
          },
          "useSoon": {
            "title": "Must use soon",
            "description": "Items close to expiry get priority (added to the ingredients automatically).",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 20
          },
          "diet": {
            "title": "Diet",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "vegetarian",
                "vegan",
                "gluten-free",
                "dairy-free"
              ]
            }
          },
          "equipment": {
            "title": "Equipment available",
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "stove",
                "oven",
                "microwave",
                "blender"
              ]
            },
            "default": [
              "stove",
              "oven",
              "microwave"
            ]
          },
          "maxMinutes": {
            "title": "Maximum time",
            "type": "integer",
            "minimum": 5,
            "maximum": 240,
            "default": 45,
            "x-unit": "min"
          }
        },
        "required": [
          "ingredients"
        ]
      },
      "LeftoverChefResult": {
        "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": [
              "recognized",
              "recipes",
              "templatesConsidered",
              "templatesEligible"
            ],
            "properties": {
              "recognized": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "input",
                    "normalized",
                    "category",
                    "known"
                  ],
                  "properties": {
                    "input": {
                      "type": "string"
                    },
                    "normalized": {
                      "type": "string"
                    },
                    "category": {
                      "type": "string"
                    },
                    "known": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "recipes": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "rank",
                    "id",
                    "name",
                    "score",
                    "timeMinutes",
                    "uses",
                    "useSoonUsed",
                    "missing",
                    "optionalExtras",
                    "steps"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer",
                      "minimum": 1
                    },
                    "id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "score": {
                      "type": "number"
                    },
                    "timeMinutes": {
                      "type": "integer"
                    },
                    "uses": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "useSoonUsed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "missing": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "optionalExtras": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "steps": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "templatesConsidered": {
                "type": "integer"
              },
              "templatesEligible": {
                "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"
              }
            }
          }
        }
      },
      "MovingBoxPlannerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "rooms": {
            "title": "Rooms to pack",
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "type": {
                  "title": "Room",
                  "type": "string",
                  "enum": [
                    "bedroom",
                    "living",
                    "kitchen",
                    "bathroom",
                    "office",
                    "kids",
                    "garage",
                    "storage"
                  ]
                },
                "size": {
                  "title": "Size / fullness",
                  "type": "string",
                  "enum": [
                    "small",
                    "medium",
                    "large"
                  ],
                  "default": "medium"
                }
              },
              "required": [
                "type"
              ]
            }
          },
          "people": {
            "title": "People in household",
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 2
          },
          "density": {
            "title": "How much stuff?",
            "type": "string",
            "enum": [
              "minimalist",
              "average",
              "collector"
            ],
            "default": "average"
          },
          "distanceKm": {
            "title": "Driving distance",
            "type": "number",
            "minimum": 0,
            "maximum": 5000,
            "default": 20,
            "x-unit": "km"
          },
          "floorsWithoutElevator": {
            "title": "Floors without elevator (old + new home)",
            "type": "integer",
            "minimum": 0,
            "maximum": 20,
            "default": 0
          },
          "moveDate": {
            "title": "Moving day",
            "type": "string",
            "format": "date"
          },
          "moverHourlyRate": {
            "title": "Mover rate per person-hour",
            "description": "Optional; cost is only calculated if given.",
            "type": "number",
            "minimum": 0,
            "maximum": 1000
          },
          "truckHourlyRate": {
            "title": "Truck rate per hour",
            "description": "Optional.",
            "type": "number",
            "minimum": 0,
            "maximum": 1000
          },
          "currency": {
            "title": "Currency",
            "type": "string",
            "maxLength": 8,
            "default": "EUR"
          }
        },
        "required": [
          "rooms",
          "moveDate"
        ]
      },
      "MovingBoxPlannerResult": {
        "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": [
              "boxes",
              "totalBoxes",
              "supplies",
              "volume",
              "truck",
              "crew",
              "cost",
              "timeline"
            ],
            "properties": {
              "boxes": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "small",
                  "medium",
                  "large",
                  "wardrobe"
                ],
                "properties": {
                  "small": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "medium": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "large": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "wardrobe": {
                    "type": "integer",
                    "minimum": 0
                  }
                }
              },
              "totalBoxes": {
                "type": "integer",
                "minimum": 0
              },
              "perRoom": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "room",
                    "size",
                    "small",
                    "medium",
                    "large",
                    "wardrobe",
                    "furnitureM3"
                  ],
                  "properties": {
                    "room": {
                      "type": "string"
                    },
                    "size": {
                      "type": "string"
                    },
                    "small": {
                      "type": "integer"
                    },
                    "medium": {
                      "type": "integer"
                    },
                    "large": {
                      "type": "integer"
                    },
                    "wardrobe": {
                      "type": "integer"
                    },
                    "furnitureM3": {
                      "type": "number"
                    }
                  }
                }
              },
              "supplies": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "tapeRolls",
                  "packingPaperKg",
                  "bubbleWrapM",
                  "markers"
                ],
                "properties": {
                  "tapeRolls": {
                    "type": "integer"
                  },
                  "packingPaperKg": {
                    "type": "number"
                  },
                  "bubbleWrapM": {
                    "type": "number"
                  },
                  "markers": {
                    "type": "integer"
                  }
                }
              },
              "volume": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "boxesM3",
                  "furnitureM3",
                  "loadM3"
                ],
                "properties": {
                  "boxesM3": {
                    "type": "number"
                  },
                  "furnitureM3": {
                    "type": "number"
                  },
                  "loadM3": {
                    "type": "number",
                    "description": "(boxes + furniture) × 1.15 stowage factor."
                  }
                }
              },
              "truck": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "vehicle",
                  "vehicleM3",
                  "trips"
                ],
                "properties": {
                  "vehicle": {
                    "type": "string"
                  },
                  "vehicleM3": {
                    "type": "number"
                  },
                  "trips": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              },
              "crew": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "people",
                  "loadingHours",
                  "drivingHours",
                  "totalHours"
                ],
                "properties": {
                  "people": {
                    "type": "integer"
                  },
                  "loadingHours": {
                    "type": "number"
                  },
                  "drivingHours": {
                    "type": "number"
                  },
                  "totalHours": {
                    "type": "number"
                  }
                }
              },
              "cost": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "calculated",
                  "labour",
                  "truck",
                  "total",
                  "currency"
                ],
                "properties": {
                  "calculated": {
                    "type": "boolean"
                  },
                  "labour": {
                    "type": "number"
                  },
                  "truck": {
                    "type": "number"
                  },
                  "total": {
                    "type": "number"
                  },
                  "currency": {
                    "type": "string"
                  }
                }
              },
              "timeline": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "date",
                    "task"
                  ],
                  "properties": {
                    "date": {
                      "type": "string",
                      "format": "date"
                    },
                    "task": {
                      "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"
              }
            }
          }
        }
      },
      "RoomLayoutOptimizerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "roomWidth": {
            "title": "Room width (west → east)",
            "type": "number",
            "minimum": 100,
            "maximum": 2000,
            "x-unit": "cm"
          },
          "roomDepth": {
            "title": "Room depth (north → south)",
            "type": "number",
            "minimum": 100,
            "maximum": 2000,
            "x-unit": "cm"
          },
          "gridCm": {
            "title": "Grid size",
            "description": "Positions snap to this grid; furniture sizes are rounded up to it.",
            "type": "integer",
            "minimum": 5,
            "maximum": 50,
            "default": 10,
            "x-unit": "cm"
          },
          "walkwayCm": {
            "title": "Minimum walkway width",
            "type": "integer",
            "minimum": 30,
            "maximum": 150,
            "default": 60,
            "x-unit": "cm"
          },
          "openings": {
            "title": "Doors and windows",
            "description": "Offset is measured along the wall from its west end (north/south walls) or north end (east/west walls).",
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "kind": {
                  "title": "Type",
                  "type": "string",
                  "enum": [
                    "door",
                    "window"
                  ]
                },
                "wall": {
                  "title": "Wall",
                  "type": "string",
                  "enum": [
                    "north",
                    "east",
                    "south",
                    "west"
                  ]
                },
                "offset": {
                  "title": "Offset",
                  "type": "number",
                  "minimum": 0,
                  "x-unit": "cm"
                },
                "width": {
                  "title": "Width",
                  "type": "number",
                  "minimum": 30,
                  "maximum": 500,
                  "x-unit": "cm"
                }
              },
              "required": [
                "kind",
                "wall",
                "offset",
                "width"
              ]
            }
          },
          "furniture": {
            "title": "Furniture",
            "description": "Width = side along its back; depth = front to back. Clearance = free space needed in front (to open drawers, get into bed, sit).",
            "type": "array",
            "minItems": 1,
            "maxItems": 15,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "name": {
                  "title": "Name",
                  "type": "string",
                  "maxLength": 40
                },
                "width": {
                  "title": "Width",
                  "type": "number",
                  "minimum": 10,
                  "maximum": 1000,
                  "x-unit": "cm"
                },
                "depth": {
                  "title": "Depth",
                  "type": "number",
                  "minimum": 10,
                  "maximum": 1000,
                  "x-unit": "cm"
                },
                "needsWall": {
                  "title": "Back against wall",
                  "type": "boolean",
                  "default": true
                },
                "clearanceFront": {
                  "title": "Clearance in front",
                  "type": "number",
                  "minimum": 0,
                  "maximum": 300,
                  "default": 60,
                  "x-unit": "cm"
                },
                "nearWindow": {
                  "title": "Prefer near window",
                  "type": "boolean",
                  "default": false
                }
              },
              "required": [
                "name",
                "width",
                "depth"
              ]
            }
          }
        },
        "required": [
          "roomWidth",
          "roomDepth",
          "furniture"
        ]
      },
      "RoomLayoutOptimizerResult": {
        "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": [
              "roomWidthCm",
              "roomDepthCm",
              "gridCm",
              "placed",
              "unplaced",
              "walkwayAreaM2",
              "freeFloorAreaM2",
              "score"
            ],
            "properties": {
              "roomWidthCm": {
                "type": "number"
              },
              "roomDepthCm": {
                "type": "number"
              },
              "gridCm": {
                "type": "integer"
              },
              "placed": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "label",
                    "name",
                    "xCm",
                    "yCm",
                    "sizeXCm",
                    "sizeYCm",
                    "facing",
                    "againstWall",
                    "accessible"
                  ],
                  "properties": {
                    "label": {
                      "type": "string",
                      "description": "Letter used in the text plan."
                    },
                    "name": {
                      "type": "string"
                    },
                    "xCm": {
                      "type": "number",
                      "description": "Distance of the item’s west edge from the west wall."
                    },
                    "yCm": {
                      "type": "number",
                      "description": "Distance of the item’s north edge from the north wall."
                    },
                    "sizeXCm": {
                      "type": "number"
                    },
                    "sizeYCm": {
                      "type": "number"
                    },
                    "facing": {
                      "type": "string",
                      "enum": [
                        "north",
                        "east",
                        "south",
                        "west"
                      ],
                      "description": "Direction the front of the item faces."
                    },
                    "againstWall": {
                      "type": "string",
                      "enum": [
                        "north",
                        "east",
                        "south",
                        "west",
                        "none"
                      ]
                    },
                    "accessible": {
                      "type": "boolean"
                    }
                  }
                }
              },
              "unplaced": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "name",
                    "reason"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              },
              "walkwayAreaM2": {
                "type": "number",
                "minimum": 0
              },
              "freeFloorAreaM2": {
                "type": "number",
                "minimum": 0
              },
              "score": {
                "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"
              }
            }
          }
        }
      },
      "TravelConnectionCheckerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "arrival": {
            "title": "Scheduled arrival (local)",
            "type": "string",
            "description": "Local date-time YYYY-MM-DDTHH:mm"
          },
          "departure": {
            "title": "Scheduled onward departure (local, same airport)",
            "type": "string",
            "description": "Local date-time YYYY-MM-DDTHH:mm"
          },
          "terminalChange": {
            "title": "Terminal change",
            "type": "boolean",
            "default": false
          },
          "immigration": {
            "title": "Passport / immigration check",
            "type": "boolean",
            "default": false
          },
          "securityRecheck": {
            "title": "Security screening again",
            "type": "boolean",
            "default": false
          },
          "baggageRecheck": {
            "title": "Collect and re-check baggage",
            "type": "boolean",
            "default": false
          },
          "airportSize": {
            "title": "Airport size",
            "type": "string",
            "enum": [
              "small",
              "medium",
              "large",
              "mega"
            ],
            "default": "large"
          },
          "walkingSpeed": {
            "title": "Walking speed",
            "type": "string",
            "enum": [
              "slow",
              "average",
              "fast"
            ],
            "default": "average"
          },
          "gateClosesMinutes": {
            "title": "Gate closes before departure",
            "description": "Check your boarding pass; often 10–20 minutes.",
            "type": "integer",
            "minimum": 0,
            "maximum": 60,
            "default": 15,
            "x-unit": "min"
          },
          "onTimeProbability": {
            "title": "Inbound flight on-time probability",
            "description": "Planning default; replace with the route’s on-time record if you know it.",
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "default": 75,
            "x-unit": "%"
          },
          "meanDelayMinutes": {
            "title": "Average delay when late",
            "type": "number",
            "minimum": 1,
            "maximum": 600,
            "default": 35,
            "x-unit": "min"
          },
          "officialMct": {
            "title": "Official minimum connection time (if known)",
            "description": "From the airline/airport for your connection type.",
            "type": "integer",
            "minimum": 0,
            "maximum": 600,
            "x-unit": "min"
          }
        },
        "required": [
          "arrival",
          "departure"
        ]
      },
      "TravelConnectionCheckerResult": {
        "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": [
              "connectionMinutes",
              "availableMinutes",
              "requiredMinutes",
              "bufferMinutes",
              "overallProbabilityPercent",
              "probabilityPercent",
              "verdict",
              "belowOfficialMct",
              "steps"
            ],
            "properties": {
              "connectionMinutes": {
                "type": "number",
                "description": "Scheduled departure − scheduled arrival."
              },
              "availableMinutes": {
                "type": "number",
                "description": "Connection minus gate-closure time."
              },
              "requiredMinutes": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "best",
                  "typical",
                  "worst"
                ],
                "properties": {
                  "best": {
                    "type": "number"
                  },
                  "typical": {
                    "type": "number"
                  },
                  "worst": {
                    "type": "number"
                  }
                }
              },
              "bufferMinutes": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "best",
                  "typical",
                  "worst"
                ],
                "properties": {
                  "best": {
                    "type": "number"
                  },
                  "typical": {
                    "type": "number"
                  },
                  "worst": {
                    "type": "number"
                  }
                }
              },
              "overallProbabilityPercent": {
                "type": "number",
                "minimum": 0,
                "maximum": 100,
                "description": "Chance averaged over a triangular distribution of total processing time."
              },
              "probabilityPercent": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "best",
                  "typical",
                  "worst"
                ],
                "description": "Model chance of reaching the gate before it closes, for best/typical/worst processing times.",
                "properties": {
                  "best": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100
                  },
                  "typical": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100
                  },
                  "worst": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 100
                  }
                }
              },
              "verdict": {
                "type": "string",
                "enum": [
                  "comfortable",
                  "ok",
                  "tight",
                  "risky",
                  "very-risky"
                ]
              },
              "belowOfficialMct": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "steps": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "step",
                    "best",
                    "typical",
                    "worst"
                  ],
                  "properties": {
                    "step": {
                      "type": "string"
                    },
                    "best": {
                      "type": "number"
                    },
                    "typical": {
                      "type": "number"
                    },
                    "worst": {
                      "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"
              }
            }
          }
        }
      },
      "WillItFitInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "itemLength": {
            "title": "Item length",
            "type": "number",
            "minimum": 1,
            "maximum": 2000,
            "x-unit": "cm"
          },
          "itemWidth": {
            "title": "Item width/depth",
            "type": "number",
            "minimum": 1,
            "maximum": 2000,
            "x-unit": "cm"
          },
          "itemHeight": {
            "title": "Item height",
            "type": "number",
            "minimum": 1,
            "maximum": 2000,
            "x-unit": "cm"
          },
          "clearance": {
            "title": "Safety clearance",
            "description": "Subtracted from every opening dimension (hands, padding, wobble).",
            "type": "number",
            "minimum": 0,
            "maximum": 30,
            "default": 2,
            "x-unit": "cm"
          },
          "doorWidth": {
            "title": "Door clear width",
            "description": "Measure between door stops with the door fully open.",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "doorHeight": {
            "title": "Door clear height",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "hallwayWidthBefore": {
            "title": "Hallway width before corner",
            "description": "Leave empty to skip the corner check.",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "hallwayWidthAfter": {
            "title": "Hallway width after corner",
            "description": "Defaults to the width before the corner.",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "ceilingHeight": {
            "title": "Hallway ceiling height",
            "description": "Used to tilt the item upward in the corner.",
            "type": "number",
            "minimum": 50,
            "maximum": 1000,
            "default": 250,
            "x-unit": "cm"
          },
          "stairsWidth": {
            "title": "Stair width",
            "description": "Leave empty to skip stairs.",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "stairsHeadroom": {
            "title": "Stair headroom (perpendicular to pitch)",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "elevatorDoorWidth": {
            "title": "Elevator door width",
            "description": "Leave elevator fields empty to skip.",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "elevatorDoorHeight": {
            "title": "Elevator door height",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "elevatorCabinWidth": {
            "title": "Elevator cabin width",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "elevatorCabinDepth": {
            "title": "Elevator cabin depth",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "elevatorCabinHeight": {
            "title": "Elevator cabin height",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          },
          "roomWidth": {
            "title": "Room width",
            "description": "Leave empty to skip the room check.",
            "type": "number",
            "minimum": 10,
            "maximum": 5000,
            "x-unit": "cm"
          },
          "roomDepth": {
            "title": "Room depth",
            "type": "number",
            "minimum": 10,
            "maximum": 5000,
            "x-unit": "cm"
          },
          "roomHeight": {
            "title": "Room height",
            "type": "number",
            "minimum": 10,
            "maximum": 1000,
            "x-unit": "cm"
          }
        },
        "required": [
          "itemLength",
          "itemWidth",
          "itemHeight",
          "doorWidth",
          "doorHeight"
        ]
      },
      "WillItFitResult": {
        "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": [
              "item",
              "clearanceCm",
              "overallFits",
              "bottleneck",
              "checkpoints"
            ],
            "properties": {
              "item": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "lengthCm",
                  "widthCm",
                  "heightCm",
                  "diagonalCm"
                ],
                "properties": {
                  "lengthCm": {
                    "type": "number"
                  },
                  "widthCm": {
                    "type": "number"
                  },
                  "heightCm": {
                    "type": "number"
                  },
                  "diagonalCm": {
                    "type": "number"
                  }
                }
              },
              "clearanceCm": {
                "type": "number",
                "minimum": 0
              },
              "overallFits": {
                "type": "boolean"
              },
              "bottleneck": {
                "type": "string",
                "description": "Checkpoint id with the smallest margin."
              },
              "checkpoints": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "id",
                    "name",
                    "fits",
                    "marginCm",
                    "orientation",
                    "detail"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "enum": [
                        "door",
                        "corner",
                        "stairs",
                        "elevator-door",
                        "elevator-cabin",
                        "room"
                      ]
                    },
                    "name": {
                      "type": "string"
                    },
                    "fits": {
                      "type": "boolean"
                    },
                    "marginCm": {
                      "type": "number",
                      "description": "Spare centimetres after clearance; negative = too big by that much."
                    },
                    "orientation": {
                      "type": "string"
                    },
                    "detail": {
                      "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"
              }
            }
          }
        }
      }
    }
  }
}