---
name: multi-leg-flight-planner
description: >
  Parses natural-language multi-leg trip descriptions and produces a rich, interactive single-file HTML flight planner. Use this skill whenever a traveler describes a multi-city or multi-leg trip in conversational language (e.g. "I want to fly New York to London in mid-June, then London to Lisbon about a week later, then Lisbon back to New York in early July"). Also triggers for "plan my trip", "search flights for my multi-city trip", "I'm doing a multi-leg trip", "flights for my journey", or any request combining 2+ city pairs with travel dates (exact, approximate, or descriptive like 'mid-June'). Always call this skill when 2 or more flight legs are described — even if dates are vague — to produce the full interactive HTML output. If NO dates are given at all, ask one clarifying question before proceeding.
compatibility:
  tools:
    - Kiwi.com:search-flight
---

# Multi-Leg Flight Planner Skill

Turns a traveler's natural-language trip description into a fully self-contained interactive HTML flight-search report, powered by live Kiwi.com data.

**Prerequisite:** The Kiwi.com MCP connector must be enabled on your Claude account. Enable it at **Settings → Connectors** in Claude and search for "Kiwi.com". See `https://docs.claude.com` for current guidance.

---

## Step 0 — Date Guard

Before doing anything else, check whether the user has given **at least approximate dates** for each leg (exact dates, month names, phrases like "mid-June", "early July", "around the 15th" all count).

- **If no dates at all are present**: Ask exactly one question — _"What dates are you thinking for each leg?"_ — and wait for the answer before proceeding.
- **If approximate dates are present**: Proceed. Map them to specific calendar dates using the rules in Step 1 and record your interpretations for the Parsed Assumptions panel.

---

## Step 1 — Parse the Natural Language Request

Extract from the user's message:

| Field | Inference rule |
|---|---|
| **Legs** | Each "City A → City B" pair is one leg. Order matters. |
| **Dates** | "mid-[month]" → 15th; "early [month]" → 5th; "late [month]" → 25th; "end of [month]" → last day; "around the Nth" → Nth. Year defaults to the nearest future occurrence. |
| **Cabin class** | "business" → C, "first" → F, "premium economy" → W, default → M (economy) |
| **Passengers** | Default 1 adult unless stated |
| **Currency** | Default USD unless user specifies otherwise |
| **Flexibility** | If user says "around", "flexible", "±N days" → set departureDateFlexRange 1–3 accordingly; otherwise 0 |

Record **every inference** — you'll surface them in the Parsed Assumptions panel.

---

## Step 2 — Search Kiwi.com for Each Leg

For each leg, call `Kiwi.com:search-flight` with:
- `flyFrom` / `flyTo`: city or airport name as stated
- `departureDate`: dd/mm/yyyy
- `cabinClass`: inferred code
- `curr`: inferred currency
- `passengers`: inferred object
- `departureDateFlexRange`: 0–3 as appropriate
- `sort`: "price" (we'll surface all sort orders client-side)

Run legs **sequentially** (the tool does not support parallel calls in this environment).

If a leg call throws an error or returns zero results, record that leg as `ERROR` — you will render the error card for it.

---

## Step 3 — Build the HTML Output

Once all leg data is collected, construct **one self-contained HTML file** matching the full specification below. Output it using `create_file` to `/mnt/user-data/outputs/trip-planner.html`, then call `present_files`.

---

## HTML Output Specification

### Design system
- **Fonts**: Load from Google Fonts CDN. Use `DM Sans` for UI / body text (weights 400, 500, 600) and `Fraunces` for the trip title (italic display). Replace with your preferred fonts if rebranding.
- **Colors**:
  - Brand accent: `#1D9E75` (teal) — header bar, active buttons, "Book" CTA
  - Background: `#F8F9FA` page, `#FFFFFF` cards
  - Text: `#1A1A2E` primary, `#6B7280` secondary
  - Price badge green: `#16A34A` bg / white text (cheapest for leg)
  - Price badge amber: `#D97706` bg / white text (within 20% of cheapest)
  - Price badge red: `#DC2626` bg / white text (>20% above cheapest)
  - Borders: `#E5E7EB`
- **Layout**: Cards only — no tables. CSS Grid + Flexbox. Mobile-responsive (`max-width: 900px` centered, fluid on mobile).
- **Shadows**: `0 1px 3px rgba(0,0,0,0.08)` for cards, `0 4px 16px rgba(0,0,0,0.12)` for sticky header.

---

### Page structure (in order)

#### 1. Header bar (sticky, teal `#1D9E75`)
- Left: Plane icon ✈ + "Trip Planner" in white `Fraunces` italic
- Right: small "Powered by Kiwi.com" attribution in white 80% opacity

#### 2. Trip summary bar (white card, shadow)
- **Horizontal timeline**: render each city as a node connected by `→` arrows. Below each arrow show the date and, between consecutive legs, a "gap days" chip (e.g. `+7 days`). Use `overflow-x: auto` so it scrolls on mobile.
- **Total trip cost panel** (right-aligned, or below on mobile): Large teal number showing the summed cheapest option across all legs. Label: "Cheapest combination". If any leg errored, show "Partial — N leg(s) unavailable".

#### 3. Parsed Assumptions panel (collapsible `<details>` element)
- Summary row: "ℹ Parsed assumptions — click to expand"
- Inside: bulleted list of every inference made. Example items:
  - `Interpreted "mid-June" as 15 June 2026`
  - `Cabin: Economy (M)`
  - `1 adult passenger`
  - `Currency: USD`
  - `Flex range: ±0 days`

#### 4. Per-leg sections (one `<section>` per leg)

**Section heading**: `Leg N — Origin → Destination · [Date]`

**Toggle bar** (three pill buttons, active state teal):
- Cheapest | Fastest | Best Quality
- Clicking reorders the cards client-side (no API re-call). Cheapest sorts by price ASC. Fastest by totalDurationInSeconds ASC. Best Quality by Kiwi quality score if available, else by (price + duration combined score).

**Flight cards** (one per result, up to 5 results shown initially with a "Show more" button for the rest):

Each card contains:
- **Route line**: e.g. `JFK → LHR` with layover airports shown as `JFK → DXB → LHR`
- **Times**: Departure time (local) `→` Arrival time (local), each with date if different day
- **Duration**: `Xh Ym` formatted from `durationInSeconds`
- **Stops badge**: `Direct` (green pill), `1 stop` (grey pill), `2 stops` (amber pill), `3+ stops` (red pill)
- **Price badge**: colored per rules above — green/amber/red
- **Price amount**: large, bold
- **Book button**: teal, opens Kiwi deep-link in new tab

**Error card** (shown if Kiwi returned no results for this leg):
> ⚠ No results found for this leg — try adjusting the date by ±3 days.
> [Search manually on Kiwi.com →] (link to `https://www.kiwi.com/en/search/results/{origin}/{destination}/{date}`)

#### 5. Footer

**Recommendation box** (teal left-border card):
- Heading: "✦ Recommended Combination"
- For each leg: one-line summary of the recommended flight (route, time, price)
- **Total cost**: large bold number
- **Reasoning paragraph**: 2–3 sentences explaining the pick — balancing total price, total journey time, and number of stops.

**Disclaimer** (small grey text):
> "Prices are live at time of search via Kiwi.com and may change. Booking completes on Kiwi's website."

---

### Client-side JavaScript requirements

All JS must be vanilla — no frameworks, no CDN JS imports (Google Fonts CSS only allowed).

1. **Sort toggle**: Each leg section stores its flight data as a JS array on the DOM (`data-flights` attribute, JSON-encoded). Clicking Cheapest/Fastest/Best Quality re-renders that leg's cards by sorting the array.
2. **Show more**: Initially render 5 cards. "Show more (N remaining)" button appends the rest.
3. **Parsed assumptions toggle**: Native `<details>`/`<summary>` — no JS required.
4. **Price badge coloring**: Computed once on page load. Cheapest price for the leg = P₀. Any price ≤ P₀×1.20 → amber. Any price > P₀×1.20 → red. Cheapest card → green.

---

## Data Mapping (Kiwi response → card fields)

From each item in the Kiwi results array:
- `flyFrom` / `flyTo` → origin/destination codes
- `route[].cityFrom`, `route[].cityTo` → layover chain
- `local_departure`, `local_arrival` → departure/arrival times (already local)
- `duration.departure` (seconds) → flight duration display
- `duration.total` (seconds) → totalDurationInSeconds for sorting
- `price` → price amount
- `deep_link` → Book button href
- `quality` → quality sort score (if present)
- Number of stops = `route.length - 1`

---

## Step 4 — Recommendation Logic

After collecting all leg results:

1. For each leg, find the `cheapest_option` (lowest price) and `fastest_option` (lowest totalDurationInSeconds) and `best_quality_option` (highest quality score, fallback to cheapest).
2. Compute three combinations:
   - **All cheapest**: sum of all `cheapest_option` prices
   - **All fastest**: sum of all `fastest_option` prices
   - **Balanced**: for each leg, pick the option whose `price ≤ cheapest × 1.15` AND has the fewest stops AND shortest duration (i.e. a composite "value" score).
3. Pick the **Balanced** combination as the recommendation unless it costs more than 25% above **All cheapest**, in which case recommend **All cheapest**.
4. Generate the recommendation reasoning paragraph accordingly.

---

## Quality Checklist (verify before outputting)

- [ ] All Kiwi calls made and data captured (or error states recorded)
- [ ] No external JS dependencies — only Google Fonts CSS CDN
- [ ] Price badges colored correctly (green/amber/red)
- [ ] Sort toggles work client-side only
- [ ] Deep-links all open in `target="_blank" rel="noopener"`
- [ ] Mobile responsive (tested mentally: cards stack vertically, timeline scrolls)
- [ ] Total cost panel reflects cheapest per leg summed
- [ ] Recommendation box references the correct flights
- [ ] Parsed assumptions panel lists every inference
- [ ] Disclaimer present in footer
- [ ] File saved to `/mnt/user-data/outputs/trip-planner.html` and presented

---

## Example Trigger Inputs

- "New York to London on June 15th, then London to Lisbon June 22nd, then Lisbon back to New York July 5th — economy, 1 adult, USD"
- "I need to fly JFK → DXB around the 10th of April, then DXB → LHR about 5 days later, business class"
- "Plan a trip: Singapore to Tokyo mid-March, Tokyo to Sydney end of March, Sydney back to Singapore early April"
- "Multi-city: NYC to Paris on March 20th, Paris to Rome March 27th, Rome to NYC April 3rd. Two adults, budget currency EUR"

If user input is too vague (no dates anywhere), ask: _"What dates are you thinking for each leg?"_ — then proceed once answered.
