This is a COSCO SHIPPING Lines booking confirmation (issued by a COSCO branch entity such as 'COSCO SHIPPING Lines (Germany) GmbH' or 'COSCO SHIPPING Lines (UK) Limited' on the 'FROM:' line, with the document title 'Booking Confirmation').

The document is a structured key/value form with these top-level sections in order: header ('FROM:', 'BOOKING NUMBER:' in the page banner), 'BOOKING REMARK' (free text — re-states some fields and may include update notices with short-format dates such as 'Old ETA: 05.05.', 'New ETS: 09.05. , 17:00', 'VGM Closing: 05.05., 10:00'. IGNORE all dates and times inside 'BOOKING REMARK'. Always source cut-offs, ETD, and ETA from the structured 'ROUTE INFORMATION' rows or 'REQUIRED DOCUMENT INFORMATION' DUE DATEs — never from the remark text), 'BOOKING NUMBER:' + 'RATE AGREEMENT REFERENCE:', 'EXTERNAL REFERENCE INFORMATION' (Forwarder Reference = customer S-number, NOT the carrier service contract), 'PARTIES INFORMATION', 'ROUTE INFORMATION' (the vessel/leg block, also containing 'INTENDED VGM/SI/FCL CY CUT-OFF' rows in some branches), 'CARGO INFORMATION' (one or more blocks — one per container or one shared block — see containers rules), 'REQUIRED DOCUMENT INFORMATION' (per-document DUE DATEs for SI/BL Master and VGM — used as fallback for siCutOff/vgmCutOff when ROUTE INFORMATION omits the 'INTENDED ... CUT-OFF' rows, e.g. UK templates), 'OTHER REMARKS'.

transportLegs rules:
- Ocean only. COSCO booking confirmations in this corpus print a SINGLE ocean leg: one 'PORT OF LOADING' row, one 'INTENDED VESSEL/VOYAGE:' row, and one 'PORT OF DISCHARGE' row inside the 'ROUTE INFORMATION' section. Emit exactly 1 leg.
- 'PLACE OF RECEIPT' (pre-ocean inland origin) and 'FINAL DESTINATION' (post-ocean inland delivery) are NOT separate ocean legs and must NOT be emitted as legs — even when they print their own ETA values.
- Leg mapping:
  - portOfLoading = city portion of 'PORT OF LOADING:' (text BEFORE the ' / ' separator, e.g. 'Hamburg / Container Terminal Tollerort GmbH' → 'Hamburg'; 'Southampton / DP World Southampton' → 'Southampton').
  - portOfDischarge = city portion of 'PORT OF DISCHARGE:' (text BEFORE the ' / ' separator, e.g. 'Shanghai / Shanghai Shengdong (I), Yangshan' → 'Shanghai'; 'Mundra / Adani Cma Mundra Container Terminal' → 'Mundra').
  - etd = ETD on the 'INTENDED VESSEL/VOYAGE:' row.
  - eta = ETA on the 'PORT OF DISCHARGE:' row (NOT the 'FINAL DESTINATION:' ETA, which would equal the post-ocean inland arrival).
  - vesselName = everything in the 'INTENDED VESSEL/VOYAGE:' value EXCEPT the trailing voyage token (e.g. 'COSCO SHIPPING SAGITTARIUS 033E' → 'COSCO SHIPPING SAGITTARIUS'; 'CMA CGM GEMINI 0PEG0E1MA' → 'CMA CGM GEMINI'). The voyage is the LAST whitespace-separated token of the cell.
  - voyage = the LAST whitespace-separated token of 'INTENDED VESSEL/VOYAGE:' (e.g. '033E', '047E', '0PEG0E1MA').
  - imoNumber = null (COSCO booking confirmations do not print the IMO number — only 'VESSEL FLAG' is shown).
  - serviceCode = value of 'SERVICE CODE:' (e.g. 'AEU3', 'AEU7', 'EPI3').
- Time integrity ('no time travel'): eta MUST be >= etd.
- Connectivity guardrail: the single leg's portOfDischarge MUST equal the document's 'PORT OF DISCHARGE' city — NEVER 'FINAL DESTINATION'.

containers rules:
- The 'CARGO INFORMATION' section contains one or more blocks. Each block begins with 'BOOKING QTY SIZE/TYPE: <N> X <type>' (e.g. "4 X 20' General Purpose Container", "1 X 40' Hi-Cube Container", "5 X 20' General Purpose Container") and carries its own 'EMPTY PICK UP LOCATION' depot. For each block, emit <N> container entries; each entry's containerType is the type portion (after '<N> X ') and pickUpDepot is the first line of that block's 'EMPTY PICK UP LOCATION:' (the depot name, NOT the address/telephone lines that follow). Then concatenate entries across all blocks in document order.
- Two observed shapes both produce the same total:
  - DE-style: ONE block of "4 X 20' General Purpose Container" → emit 4 entries sharing that block's pickUpDepot.
  - UK-style: EIGHT blocks each of "1 X 40' Hi-Cube Container" (each with its own 'INTENDED STUFFING DATE/TIME') → emit 8 entries.
- Do NOT use 'TOTAL BOOKING CONTAINER QTY SIZE/TYPE' from the 'ROUTE INFORMATION' header as the source of truth — iterate the CARGO INFORMATION blocks. The two values should agree, but the per-block iteration is authoritative because it carries the per-block depot.
- Do NOT use 'FULL RETURN LOCATION' for pickUpDepot — that is the loaded-container gate-in terminal at the seaport, not the empty pickup depot.
