This is a CMA-CGM booking confirmation. The carrier uses MULTIPLE layouts depending on the issuing agent — extract by KEYWORD, not by position. Observed layouts:

- Polish (GDY) email: 'From: Polish United Baltic Corp Ltd', structured 'Booking Confirmation' page with named sections (PARTIES / ROUTE / CUT-OFF / CARGO INFORMATION / BOOKING REMARKS).
- PFR0717_001 form (header 'PFR0717_001 v5.xxx'): key/value rows — Booking Number, Vessel/Voyage, Receipt, Port Of Loading, Loading Terminal, Transhipment, Connecting Vessel/Voyage, Port Of Discharge, Final Place Of Delivery, cut-offs, ETD, ETA. Used by Vietnam/Italy/Hamburg/Qingdao; Qingdao also labels in Chinese.
- Ningbo bilingual: 'CMA CGM Ningbo' header, EN/中文 labels; uses 'Shipping Order No.' instead of 'Booking Number'.
- Web 'Booking Request' (rare): explicitly states 'This is not a booking confirmation'. Best-effort; cut-offs and ETD/ETA may be absent.

Chinese label map: 订舱号=Booking Number, 船名/航次=Vessel/Voyage, 装货港=POL, 卸货港=POD, 中转港=Transhipment, 装货码头=Loading Terminal, 截关 日期/时间=Port Cut-Off, 首选堆场=Pick up Place, 报价=Quote, 数量=Quantity.

carrierName = the issuing agent/branch entity printed on the form, and it MUST carry the parent brand 'CMA CGM'. If the printed entity already contains 'CMA CGM' (e.g. 'CMA CGM ITALY', 'CMA CGM VIETNAM JSC', 'CMA CGM Ningbo', 'CMA CGM Deutschland GmbH Shipping Agency'), use it verbatim. If it does NOT (a local agent name such as 'Polish United Baltic Corp Ltd'), prefix it as 'CMA CGM - <entity>' (e.g. 'CMA CGM - Polish United Baltic Corp Ltd'); the generic parent 'CMA CGM SA' is an acceptable fallback.

transportLegs rules:
- Ocean only. 'Receipt'/'Place Of Receipt' (pre-carriage) and 'Final Place Of Delivery'/'Place of Delivery' (post-ocean inland) MUST NOT be emitted as legs, even when they differ from POL/POD.
- Single leg (default): emit 1 leg when no Transhipment city is named. portOfLoading=POL city, portOfDischarge=POD city, vessel/voyage from 'Vessel/Voyage:', etd from the POL-row 'ETD:', eta from the POD-row 'ETA:'. (The Polish/GDY layout has no 'ETD:'/'ETA:' rows and sources etd/eta differently — see the Polish rule below.)
- Two legs (PFR0717 form ONLY, when BOTH a 'Transhipment' city AND a 'Connecting Vessel / Voyage' are populated):
  - Leg 1 POL → Transhipment: vessel/voyage from 'Vessel/Voyage:', etd = POL-row 'ETD:', eta = FIRST 'ETA:' in the right column (arrival at T/S).
  - Leg 2 Transhipment → POD: vessel/voyage from 'Connecting Vessel / Voyage:', etd = null (PFR0717 does not print the connecting vessel's ETD — do NOT guess), eta = SECOND 'ETA:' (arrival at POD).
- Polish (GDY) layout: always emit a SINGLE leg POL → POD. The transhipment port is NEVER named (even when the page-2 route graphic shows 'X Days Transhipment') — do NOT invent a T/S city. Source etd/eta from the page-2 route graphic, which prints a date next to 'POL <city>' (the vessel's sailing/departure date) and a date next to 'POD <city>' (its arrival date), separated by the 'X Days Transhipment' transit count: etd = the 'POL <city>' date, eta = the 'POD <city>' date. Do NOT use the page-3 'Load ETA' / 'Discharge ETA' values — those are the vessel's ETA at the load/discharge port and are typically a couple of days off the actual sailing/arrival schedule, NOT the leg's etd/eta.
- Strip UN/LOCODE prefix from port cells: 'CNNGB (NINGBO)' → 'NINGBO', 'DEHAM' → 'HAMBURG'.
- Vessel/voyage cell: format '<VESSEL> / <VOYAGE>' or '<VESSEL> / <VOYAGE1> / <VOYAGE2>'. vesselName = everything BEFORE the first ' / '; voyage = the FIRST token after the vessel name (drop the optional 2nd voyage — it's the local call voyage). Example: 'BANGKOK / 0YDC0S1MA / 0YDC0S1NC' → vessel 'BANGKOK', voyage '0YDC0S1MA'.
- imoNumber is never printed — always null.
- Time integrity: each leg's eta MUST be >= its etd (when both present) and <= the next leg's etd.

containers rules:
- Quantity is printed inline as 'N x TYPE' next to 'Container Quantity' / 'Quantity' / '数量'. Repeat the entry N times. Examples: '2 x 40\'HC' → 2x "40'HC"; '10 x 40 OT' → 10x "40 OT".
- pickUpDepot: only emit a real depot/terminal name. The 'Preliminary Empty Pickup Location' / 'Pick up Place' / '首选堆场' field often contains just a city (e.g. 'GDANSK', 'VUNG TAU', 'NINGBO') — return null in that case.

contractNumber priority: prefer 'Service Contract' or 'Contract No(SQ/SC)' when populated; otherwise fall back to 'Quote' / '报价'. Do NOT use 'Bkg Pty Ref' or 'Electronic Ref'.
