/* Shared data, primitives, and field components for EMMA CRS Rate Load Request
   Used by all three variations. Exposes everything to window. */

// ---- Mock data ----------------------------------------------------------
const MOCK_CUSTOMERS = [
  { id: "C-1042", name: "Meridian Travel Group", segment: "Corporate", country: "DE", email: "rfp@meridiantravel.de" },
  { id: "C-2310", name: "Northwind Logistics Inc.", segment: "Corporate", country: "US", email: "travel@northwind.com" },
  { id: "C-3781", name: "Aurora Conferences Ltd.", segment: "Group / MICE", country: "UK", email: "events@aurora-conf.co.uk" },
  { id: "C-4112", name: "Bluepeak Consulting GmbH", segment: "Corporate", country: "DE", email: "ops@bluepeak.de" },
  { id: "C-5023", name: "Pacific Crest Tours", segment: "Tour Operator", country: "AU", email: "rates@pacificcrest.au" },
  { id: "C-6090", name: "Lumen Pharma Holdings", segment: "Corporate", country: "CH", email: "tmc@lumenpharma.ch" },
  { id: "C-6512", name: "Stride Athletic Federation", segment: "Group / Sports", country: "FR", email: "team@strideaf.fr" },
  { id: "C-7314", name: "Helix Wholesale Travel", segment: "Wholesaler", country: "ES", email: "contracts@helixwt.es" },
];

const MOCK_CONTRACTS = [
  { id: "BLPK-25-NEG", name: "Bluepeak Consulting — 2025 Negotiated", customer: "Bluepeak Consulting GmbH", rateType: "Corporate Negotiated", currency: "EUR", expires: "31 Dec 2025" },
  { id: "MERI-25-COR", name: "Meridian Travel — 2025 Corporate", customer: "Meridian Travel Group", rateType: "Corporate Negotiated", currency: "EUR", expires: "31 Dec 2025" },
  { id: "AURO-26-MIC", name: "Aurora Conferences — 2026 MICE Master", customer: "Aurora Conferences Ltd.", rateType: "Group / MICE", currency: "GBP", expires: "31 Dec 2026" },
  { id: "NWND-26-COR", name: "Northwind — 2026 Global Corporate", customer: "Northwind Logistics Inc.", rateType: "Corporate Negotiated", currency: "USD", expires: "31 Dec 2026" },
  { id: "PCRT-25-TO", name: "Pacific Crest — 2025 Tour Operator", customer: "Pacific Crest Tours", rateType: "Tour Operator", currency: "AUD", expires: "31 Mar 2026" },
  { id: "HLXW-26-WHL", name: "Helix Wholesale — 2026 Net Rates", customer: "Helix Wholesale Travel", rateType: "Wholesale", currency: "EUR", expires: "31 Dec 2026" },
];

const MOCK_HOTELS = [
  { id: "GBEDLONBER", name: "Bond Street" },
  { id: "GBEDLONBSH", name: "Marlborough (Bloomsbury)" },
  { id: "GBEDLONGRA", name: "Euston Square" },
  { id: "GBEDLONHAM", name: "Leicester Square" },
  { id: "GBEDLONKEN", name: "Tottenham Court Road" },
  { id: "GBEDLONMER", name: "Mountbatten (Mercer Street)" },
  { id: "GBEDLONNPW", name: "Canary Wharf" },
  { id: "GBEDLONSUS", name: "Marble Arch" },
  { id: "GBEDLONVAN", name: "Kensington (South Kensington)" },
];

const ROOM_TYPES = [
  { code: "STD", label: "Standard" },
  { code: "PRM", label: "Premium" },
  { code: "DLX", label: "Deluxe" },
  { code: "JRS", label: "Junior Suite" },
  { code: "EXS", label: "Executive Suite" },
];

const MEAL_PLANS = [
  { code: "RO", label: "Room Only" },
  { code: "BB", label: "Bed & Breakfast" },
];

const CURRENCIES = ["EUR", "USD", "GBP", "CHF", "AUD", "JPY"];
const RATE_TYPES = [
  "Corporate Negotiated",
  "Group Business",
  "Group Leisure",
  "Wholesale / FIT",
  "Tour Operator",
  "Travel Agency / TMC",
  "Online (OTA)",
  "GDS",
  "Tactical",
  "Crew",
  "Layover",
  "Loyalty",
  "Other",
];
const PRICE_CODE_TYPES = ["Dynamic Rate", "Static Rate"];

const MARKET_SEGMENTS = [
  "Business Groups with M&E",
  "Business Groups Room Only",
  "Corporate",
  "Crew & Layovers",
  "Tour Operator & Wholesalers",
  "Flexible",
  "Leisure Groups",
  "Other",
  "Prepaid",
  "Tactical & Other Discounts",
];

// ---- Cancellation policies (mock superset — replace with per-hotel data later)
const CANCELLATION_POLICIES = [
  { code: "FLEX24", label: "Flexible — 24h before arrival" },
  { code: "FLEX48", label: "Flexible — 48h before arrival" },
  { code: "FLEX72", label: "Flexible — 72h before arrival" },
  { code: "FLEX7D", label: "Flexible — 7 days before arrival" },
  { code: "FLEX14D", label: "Flexible — 14 days before arrival" },
  { code: "NR", label: "Non-refundable (no cancellation)" },
  { code: "GROUP", label: "Group cancellation T&Cs (per contract)" },
  { code: "DEPOSIT1N", label: "1-night deposit, balance on arrival" },
  { code: "PREPAID", label: "Prepaid (no cancellation, full charge)" },
];

// ---- Per-hotel room-type & cancellation-policy availability --------------
//   Mock data — replace with real per-property mapping when available.
//   Each hotel lists the ROOM_TYPES.code values it sells and the
//   CANCELLATION_POLICIES.code values that have been pre-approved.
const HOTEL_PROFILE = {
  GBEDLONBER: { rooms: ["STD", "PRM", "DLX", "JRS", "EXS"], cancel: ["FLEX24", "FLEX48", "NR", "GROUP", "DEPOSIT1N"] },
  GBEDLONBSH: { rooms: ["STD", "DLX", "JRS"],               cancel: ["FLEX24", "FLEX48", "NR", "GROUP"] },
  GBEDLONGRA: { rooms: ["STD", "PRM", "DLX"],               cancel: ["FLEX24", "FLEX72", "NR", "GROUP"] },
  GBEDLONHAM: { rooms: ["STD", "PRM", "DLX", "JRS"],        cancel: ["FLEX24", "FLEX48", "FLEX72", "NR", "GROUP", "PREPAID"] },
  GBEDLONKEN: { rooms: ["STD", "DLX"],                      cancel: ["FLEX24", "NR", "GROUP"] },
  GBEDLONMER: { rooms: ["STD", "PRM", "DLX", "JRS", "EXS"], cancel: ["FLEX24", "FLEX48", "FLEX7D", "NR", "GROUP", "DEPOSIT1N"] },
  GBEDLONNPW: { rooms: ["STD", "PRM", "DLX", "EXS"],        cancel: ["FLEX24", "FLEX48", "NR", "GROUP", "PREPAID"] },
  GBEDLONSUS: { rooms: ["STD", "PRM", "DLX", "JRS"],        cancel: ["FLEX24", "FLEX48", "FLEX14D", "NR", "GROUP"] },
  GBEDLONVAN: { rooms: ["STD", "DLX", "JRS", "EXS"],        cancel: ["FLEX24", "FLEX48", "NR", "GROUP", "DEPOSIT1N", "PREPAID"] },
};

// ---- Nomenclature helper (per "Nomenclature 01 Aug 25" doc) -------------
//   Given the form state, returns suggested prefixes/codes:
//     { contractPrefix, ratePrefix, priceCodePrefix, suggestedRateCode,
//       suggestedContractCode, suggestedPriceCodeName, hint }
const last6 = (s) => String(s || "").replace(/[^0-9]/g, "").slice(-6).padStart(6, "0");
const cleanNick = (s) => String(s || "").toUpperCase().replace(/[^A-Z0-9]/g, "").slice(0, 8);
const NOMENCLATURE = {
  "Corporate Negotiated":  { contract: "C_",   rate: { LRA: "CL", "Non-LRA Static": "CS", "Non-LRA Dynamic": "CD" }, price: "C_",   pmsHint: "Q- (LRA) / T- (NLRA Static) / G-,T- (NLRA Dyn)" },
  "Group Business":        { contract: "GB_",  rate: "GB",  price: "G_",  pmsHint: "M- / N-" },
  "Group Leisure":         { contract: "GL_",  rate: "GL",  price: "G_",  pmsHint: "U- / P-" },
  "Wholesale / FIT":       { contract: "FW_",  rate: "FW",  price: "FW_", pmsHint: "V- / X-" },
  "Tour Operator":         { contract: "FW_",  rate: "FW",  price: "FW_", pmsHint: "U- / V- / X-" },
  "Travel Agency / TMC":   { contract: "F_",   rate: "TA_", price: "F_",  pmsHint: "F-" },
  "Online (OTA)":          { contract: "IO_",  rate: "IO_", price: "IO_", pmsHint: "Y-" },
  "GDS":                   { contract: "GDS_", rate: "GDS_",price: "GDS_",pmsHint: "R-" },
  "Tactical":              { contract: "T",    rate: "T",   price: "T",   pmsHint: "H- / I-" },
  "Crew":                  { contract: "CR_",  rate: "CR",  price: "CR_", pmsHint: "K-" },
  "Layover":               { contract: "LY",   rate: "LY_", price: "LY",  pmsHint: "L-" },
  "Loyalty":               { contract: "RR_",  rate: "RR_", price: "RR_", pmsHint: "H- / I- / R-" },
  "Other":                 { contract: "OR_",  rate: "OR",  price: "OR_", pmsHint: "J- / Z-" },
};
const suggestCodes = (form) => {
  const def = NOMENCLATURE[form.rateType] || {};
  const isCorp = form.rateType === "Corporate Negotiated";
  const ratePart = isCorp ? (def.rate?.[form.lraType] || "C") : (def.rate || "");
  const corpId6 = isCorp && form.customerId ? last6(form.customerId) : "";
  const nick = cleanNick(form.contractName) || "NICK";
  const isFixed = form.priceCodeType === "Static Rate";
  const fixedAmt = String(form.seasons?.[0]?.basePrice || "").replace(/[^0-9]/g, "") || "AMT";
  const baseSupp = String(form.seasons?.[0]?.basePrice || "").replace(/[^0-9]/g, "") || "SUPP";
  return {
    contractPrefix: def.contract || "",
    ratePrefix: def.contract || "",
    priceCodePrefix: def.price || "",
    suggestedContractCode: isCorp ? `C_${corpId6 || "XXXXXX"}` : `${def.contract || ""}${nick}`,
    suggestedRateCode:     isCorp ? `${ratePart}${corpId6 || "XXXXXX"}` : `${ratePart}${nick}`,
    suggestedPriceCodeName: isFixed
      ? `${def.price || ""}${fixedAmt}`
      : `${def.price || ""}BASE_${baseSupp}`,
    pmsHint: def.pmsHint || "",
  };
};

// ---- Style tokens --------------------------------------------------------
const tokens = {
  bg: "#FAF8F4",
  surface: "#FFFFFF",
  surface2: "#F4F1EB",
  border: "#E5E0D6",
  borderStrong: "#C9C2B2",
  ink: "#1B1D1A",
  ink2: "#3D3F3A",
  muted: "#6B6B63",
  faint: "#9A968B",
  accent: "#0E5E55", // deep teal
  accentSoft: "#E4EFEC",
  accentInk: "#073A34",
  warn: "#B25A14",
  warnSoft: "#FBEEDF",
  ok: "#2E7D5B",
  okSoft: "#E2F0E8",
  danger: "#A8331F",
  dangerSoft: "#F6E1DC",
  mono: "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace",
  sans: "'Inter', system-ui, -apple-system, Segoe UI, sans-serif",
};

// ---- Tiny primitives -----------------------------------------------------
const Label = ({ children, required, hint, htmlFor }) => (
  <label htmlFor={htmlFor} style={{
    display: "flex", alignItems: "baseline", justifyContent: "space-between",
    fontSize: 12, fontWeight: 600, color: tokens.ink2, letterSpacing: 0.2,
    textTransform: "uppercase", marginBottom: 6,
  }}>
    <span>{children}{required && <span style={{ color: tokens.danger, marginLeft: 4 }}>*</span>}</span>
    {hint && <span style={{ fontWeight: 400, textTransform: "none", color: tokens.faint, fontSize: 11, letterSpacing: 0 }}>{hint}</span>}
  </label>
);

const inputBase = {
  width: "100%", boxSizing: "border-box",
  padding: "10px 12px", fontSize: 14, fontFamily: tokens.sans,
  color: tokens.ink, background: tokens.surface,
  border: `1px solid ${tokens.border}`, borderRadius: 6,
  outline: "none", transition: "border-color .15s, box-shadow .15s",
};

const TextField = ({ value, onChange, placeholder, mono, prefix, suffix, type = "text", id, error, ...rest }) => {
  const [focus, setFocus] = React.useState(false);
  const wrapStyle = {
    display: "flex", alignItems: "stretch",
    border: `1px solid ${error ? tokens.danger : (focus ? tokens.accent : tokens.border)}`,
    background: tokens.surface, borderRadius: 6,
    boxShadow: focus ? `0 0 0 3px ${tokens.accent}1A` : "none",
    transition: "all .15s",
  };
  return (
    <div>
      <div style={wrapStyle}>
        {prefix && <span style={{
          padding: "10px 10px 10px 12px", color: tokens.faint,
          fontFamily: mono ? tokens.mono : tokens.sans, fontSize: 13,
          borderRight: `1px solid ${tokens.border}`, background: tokens.surface2,
          borderTopLeftRadius: 5, borderBottomLeftRadius: 5,
        }}>{prefix}</span>}
        <input
          id={id} type={type} value={value || ""} placeholder={placeholder}
          onChange={(e) => onChange?.(e.target.value)}
          onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
          style={{
            ...inputBase, border: "none", boxShadow: "none",
            background: "transparent",
            fontFamily: mono ? tokens.mono : tokens.sans,
          }}
          {...rest}
        />
        {suffix && <span style={{
          padding: "10px 12px", color: tokens.faint, fontSize: 13,
          borderLeft: `1px solid ${tokens.border}`, background: tokens.surface2,
          alignSelf: "stretch", display: "flex", alignItems: "center",
          borderTopRightRadius: 5, borderBottomRightRadius: 5,
        }}>{suffix}</span>}
      </div>
      {error && <div style={{ color: tokens.danger, fontSize: 12, marginTop: 4 }}>{error}</div>}
    </div>
  );
};

const Select = ({ value, onChange, options, placeholder, id }) => {
  const [focus, setFocus] = React.useState(false);
  return (
    <div style={{ position: "relative" }}>
      <select
        id={id} value={value || ""} onChange={(e) => onChange?.(e.target.value)}
        onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
        style={{
          ...inputBase, appearance: "none",
          paddingRight: 36,
          borderColor: focus ? tokens.accent : tokens.border,
          boxShadow: focus ? `0 0 0 3px ${tokens.accent}1A` : "none",
          color: value ? tokens.ink : tokens.faint,
        }}
      >
        <option value="" disabled>{placeholder || "Select…"}</option>
        {options.map((o) => {
          const v = typeof o === "string" ? o : o.value;
          const l = typeof o === "string" ? o : o.label;
          return <option key={v} value={v}>{l}</option>;
        })}
      </select>
      <svg width="12" height="12" viewBox="0 0 12 12" style={{
        position: "absolute", right: 12, top: "50%", transform: "translateY(-50%)",
        pointerEvents: "none", color: tokens.muted,
      }}>
        <path d="M2 4l4 4 4-4" stroke="currentColor" fill="none" strokeWidth="1.5" strokeLinecap="round" />
      </svg>
    </div>
  );
};

const TextArea = ({ value, onChange, placeholder, rows = 3, id }) => {
  const [focus, setFocus] = React.useState(false);
  return (
    <textarea
      id={id} value={value || ""} placeholder={placeholder} rows={rows}
      onChange={(e) => onChange?.(e.target.value)}
      onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}
      style={{
        ...inputBase, resize: "vertical",
        borderColor: focus ? tokens.accent : tokens.border,
        boxShadow: focus ? `0 0 0 3px ${tokens.accent}1A` : "none",
        fontFamily: tokens.sans, lineHeight: 1.5,
      }}
    />
  );
};

const Checkbox = ({ checked, onChange, label, hint }) => (
  <label style={{ display: "flex", gap: 10, alignItems: "flex-start", cursor: "pointer", padding: "2px 0" }}>
    <span style={{
      width: 18, height: 18, borderRadius: 4, marginTop: 1,
      border: `1.5px solid ${checked ? tokens.accent : tokens.borderStrong}`,
      background: checked ? tokens.accent : tokens.surface,
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      flexShrink: 0, transition: "all .15s",
    }}>
      {checked && (
        <svg width="11" height="11" viewBox="0 0 11 11"><path d="M2 5.5l2.2 2L9 3" stroke="white" fill="none" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" /></svg>
      )}
    </span>
    <span style={{ fontSize: 13.5, color: tokens.ink, lineHeight: 1.4 }}>
      <input type="checkbox" checked={!!checked} onChange={(e) => onChange?.(e.target.checked)} style={{ display: "none" }} />
      {label}
      {hint && <div style={{ color: tokens.muted, fontSize: 12, marginTop: 2 }}>{hint}</div>}
    </span>
  </label>
);

const Radio = ({ checked, onChange, label, hint }) => (
  <label style={{ display: "flex", gap: 10, alignItems: "flex-start", cursor: "pointer", padding: "2px 0" }}>
    <span style={{
      width: 18, height: 18, borderRadius: "50%", marginTop: 1,
      border: `1.5px solid ${checked ? tokens.accent : tokens.borderStrong}`,
      background: tokens.surface,
      display: "inline-flex", alignItems: "center", justifyContent: "center",
      flexShrink: 0, transition: "all .15s",
    }}>
      {checked && <span style={{ width: 9, height: 9, borderRadius: "50%", background: tokens.accent }} />}
    </span>
    <span style={{ fontSize: 13.5, color: tokens.ink, lineHeight: 1.4 }}>
      <input type="radio" checked={!!checked} onChange={(e) => onChange?.(e.target.checked)} style={{ display: "none" }} />
      {label}
      {hint && <div style={{ color: tokens.muted, fontSize: 12, marginTop: 2 }}>{hint}</div>}
    </span>
  </label>
);

const Pill = ({ children, tone = "neutral", size = "md" }) => {
  const tones = {
    neutral: { bg: tokens.surface2, color: tokens.ink2, border: tokens.border },
    accent: { bg: tokens.accentSoft, color: tokens.accentInk, border: "#BFD8D2" },
    ok: { bg: tokens.okSoft, color: "#1F4D38", border: "#BCD9C8" },
    warn: { bg: tokens.warnSoft, color: "#7A3C0D", border: "#EDD2A9" },
    danger: { bg: tokens.dangerSoft, color: "#7A2415", border: "#E5BAAF" },
  };
  const t = tones[tone] || tones.neutral;
  const sizes = { sm: { pad: "2px 7px", fs: 10.5 }, md: { pad: "3px 9px", fs: 11.5 } };
  const s = sizes[size];
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 5,
      background: t.bg, color: t.color, border: `1px solid ${t.border}`,
      padding: s.pad, borderRadius: 999, fontSize: s.fs, fontWeight: 600,
      textTransform: "uppercase", letterSpacing: 0.4, whiteSpace: "nowrap",
    }}>{children}</span>
  );
};

const Button = ({ variant = "primary", size = "md", children, onClick, icon, disabled, type = "button" }) => {
  const variants = {
    primary: { bg: tokens.accent, color: "#fff", border: tokens.accent, hoverBg: tokens.accentInk },
    secondary: { bg: tokens.surface, color: tokens.ink, border: tokens.borderStrong, hoverBg: tokens.surface2 },
    ghost: { bg: "transparent", color: tokens.ink2, border: "transparent", hoverBg: tokens.surface2 },
    danger: { bg: tokens.surface, color: tokens.danger, border: "#E5BAAF", hoverBg: tokens.dangerSoft },
  };
  const v = variants[variant];
  const sizes = { sm: { pad: "6px 10px", fs: 12.5 }, md: { pad: "9px 14px", fs: 13.5 }, lg: { pad: "12px 18px", fs: 14 } };
  const s = sizes[size];
  const [hover, setHover] = React.useState(false);
  return (
    <button type={type} onClick={onClick} disabled={disabled}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        display: "inline-flex", alignItems: "center", gap: 7,
        padding: s.pad, fontSize: s.fs, fontWeight: 600, fontFamily: tokens.sans,
        background: hover && !disabled ? v.hoverBg : v.bg,
        color: v.color, border: `1px solid ${v.border}`, borderRadius: 6,
        cursor: disabled ? "not-allowed" : "pointer",
        opacity: disabled ? 0.55 : 1, transition: "all .15s",
        whiteSpace: "nowrap",
      }}>
      {icon}{children}
    </button>
  );
};

// ---- Contract lookup combobox (with free-type fallback) ----------------
const ContractLookup = ({ value, onChange, name, onNameChange, code, onCodeChange }) => {
  // value: a MOCK_CONTRACTS entry OR null (free-type mode)
  // name/code are used when free-typing
  const [q, setQ] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const [mode, setMode] = React.useState(value ? "lookup" : "free"); // "lookup" or "free"
  const ref = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h);
  }, []);
  const filtered = MOCK_CONTRACTS.filter(c =>
    !q || c.name.toLowerCase().includes(q.toLowerCase()) || c.id.toLowerCase().includes(q.toLowerCase()) || c.customer.toLowerCase().includes(q.toLowerCase())
  );

  if (mode === "free") {
    return (
      <div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
          <Pill tone="neutral" size="sm">New contract</Pill>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          <div>
            <Label>Contract name</Label>
            <TextField value={name} onChange={onNameChange} placeholder="e.g. Bluepeak Consulting — 2026 Negotiated" />
          </div>
          <div>
            <Label hint="Inherited by rate">Contract code</Label>
            <TextField value={code} onChange={onCodeChange} placeholder="BLPK-26-NEG" mono />
          </div>
        </div>
      </div>
    );
  }

  return (
    <div ref={ref} style={{ position: "relative" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
        <Pill tone="accent" size="sm">Existing contract</Pill>
        <button onClick={() => { setMode("free"); onChange(null); }} style={{
          background: "transparent", border: "none", color: tokens.accent, fontSize: 12, fontWeight: 600,
          cursor: "pointer", padding: 0, textDecoration: "underline",
        }}>Create new instead</button>
      </div>
      {value ? (
        <div style={{
          display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12,
          padding: "12px 14px", border: `1px solid ${tokens.border}`, background: tokens.surface,
          borderRadius: 6,
        }}>
          <div style={{ minWidth: 0, flex: 1 }}>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: tokens.ink }}>{value.name}</div>
            <div style={{ fontSize: 12, color: tokens.muted, marginTop: 3 }}>
              <span style={{ fontFamily: tokens.mono, color: tokens.accentInk, fontWeight: 500 }}>{value.id}</span>
              {" · "}{value.customer}{" · "}expires {value.expires}
            </div>
          </div>
          <Pill tone="neutral" size="sm">{value.rateType}</Pill>
          <button onClick={() => onChange(null)} style={{
            background: "transparent", border: "none", color: tokens.muted,
            cursor: "pointer", padding: "4px 8px", fontSize: 12, fontWeight: 600,
          }}>Change</button>
        </div>
      ) : (
        <>
          <div style={{
            display: "flex", alignItems: "stretch",
            border: `1px solid ${open ? tokens.accent : tokens.border}`,
            boxShadow: open ? `0 0 0 3px ${tokens.accent}1A` : "none",
            borderRadius: 6, background: tokens.surface, transition: "all .15s",
          }}>
            <span style={{
              display: "inline-flex", alignItems: "center", padding: "0 12px",
              color: tokens.faint, borderRight: `1px solid ${tokens.border}`, background: tokens.surface2,
              borderTopLeftRadius: 5, borderBottomLeftRadius: 5,
            }}>
              <svg width="14" height="14" viewBox="0 0 14 14"><circle cx="6" cy="6" r="4.5" stroke="currentColor" fill="none" strokeWidth="1.5" /><path d="M9.5 9.5L13 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /></svg>
            </span>
            <input
              value={q} onChange={(e) => { setQ(e.target.value); setOpen(true); }}
              onFocus={() => setOpen(true)}
              placeholder="Search contract name, code, or customer…"
              style={{ ...inputBase, border: "none", boxShadow: "none", background: "transparent" }}
            />
          </div>
          {open && (
            <div style={{
              position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, zIndex: 50,
              background: tokens.surface, border: `1px solid ${tokens.border}`,
              borderRadius: 6, boxShadow: "0 12px 32px rgba(20,18,12,.12)",
              maxHeight: 280, overflowY: "auto",
            }}>
              {filtered.length === 0 && (
                <div style={{ padding: 14, fontSize: 13, color: tokens.muted, textAlign: "center" }}>
                  No matching contracts.{" "}
                  <button onClick={() => { setMode("free"); setOpen(false); }} style={{
                    background: "transparent", border: "none", color: tokens.accent,
                    fontWeight: 600, cursor: "pointer", padding: 0, textDecoration: "underline",
                  }}>Create new contract</button>
                </div>
              )}
              {filtered.map(c => (
                <div key={c.id} onClick={() => { onChange(c); setOpen(false); setQ(""); }}
                  style={{
                    padding: "10px 12px", cursor: "pointer", borderBottom: `1px solid ${tokens.border}`,
                  }}
                  onMouseEnter={(e) => e.currentTarget.style.background = tokens.surface2}
                  onMouseLeave={(e) => e.currentTarget.style.background = tokens.surface}>
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12 }}>
                    <div style={{ minWidth: 0, flex: 1 }}>
                      <div style={{ fontSize: 13.5, fontWeight: 600, color: tokens.ink }}>{c.name}</div>
                      <div style={{ fontSize: 11.5, color: tokens.muted, marginTop: 2 }}>
                        <span style={{ fontFamily: tokens.mono, color: tokens.accentInk }}>{c.id}</span>
                        {" · "}{c.customer}
                      </div>
                    </div>
                    <div style={{ textAlign: "right", flexShrink: 0 }}>
                      <Pill tone="neutral" size="sm">{c.currency}</Pill>
                      <div style={{ fontSize: 10.5, color: tokens.faint, marginTop: 4 }}>exp {c.expires}</div>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </>
      )}
    </div>
  );
};

// ---- Customer lookup combobox -------------------------------------------
const CustomerLookup = ({ value, onChange }) => {
  const [q, setQ] = React.useState("");
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h);
  }, []);
  const filtered = MOCK_CUSTOMERS.filter(c =>
    !q || c.name.toLowerCase().includes(q.toLowerCase()) || c.id.toLowerCase().includes(q.toLowerCase())
  );
  const display = value ? `${value.name} · ${value.id}` : q;

  return (
    <div ref={ref} style={{ position: "relative" }}>
      <div style={{
        display: "flex", alignItems: "stretch", gap: 0,
        border: `1px solid ${open ? tokens.accent : tokens.border}`,
        boxShadow: open ? `0 0 0 3px ${tokens.accent}1A` : "none",
        borderRadius: 6, background: tokens.surface, transition: "all .15s",
      }}>
        <span style={{
          display: "inline-flex", alignItems: "center", padding: "0 12px",
          color: tokens.faint, borderRight: `1px solid ${tokens.border}`, background: tokens.surface2,
          borderTopLeftRadius: 5, borderBottomLeftRadius: 5,
        }}>
          <svg width="14" height="14" viewBox="0 0 14 14"><circle cx="6" cy="6" r="4.5" stroke="currentColor" fill="none" strokeWidth="1.5" /><path d="M9.5 9.5L13 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /></svg>
        </span>
        <input
          value={value ? display : q}
          onChange={(e) => { setQ(e.target.value); if (value) onChange(null); setOpen(true); }}
          onFocus={() => setOpen(true)}
          placeholder="Search customer name or account ID…"
          style={{ ...inputBase, border: "none", boxShadow: "none", background: "transparent" }}
        />
        {value && (
          <button onClick={() => { onChange(null); setQ(""); }} style={{
            background: "transparent", border: "none", color: tokens.muted, cursor: "pointer",
            padding: "0 12px", fontSize: 18, lineHeight: 1,
          }}>×</button>
        )}
      </div>
      {open && filtered.length > 0 && (
        <div style={{
          position: "absolute", top: "calc(100% + 4px)", left: 0, right: 0, zIndex: 50,
          background: tokens.surface, border: `1px solid ${tokens.border}`,
          borderRadius: 6, boxShadow: "0 12px 32px rgba(20,18,12,.12)",
          maxHeight: 280, overflowY: "auto",
        }}>
          {filtered.map(c => (
            <div key={c.id} onClick={() => { onChange(c); setQ(""); setOpen(false); }}
              style={{
                padding: "10px 12px", cursor: "pointer", borderBottom: `1px solid ${tokens.border}`,
                display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12,
              }}
              onMouseEnter={(e) => e.currentTarget.style.background = tokens.surface2}
              onMouseLeave={(e) => e.currentTarget.style.background = tokens.surface}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: tokens.ink }}>{c.name}</div>
                <div style={{ fontSize: 12, color: tokens.muted, marginTop: 2 }}>
                  <span style={{ fontFamily: tokens.mono }}>{c.id}</span> · {c.country} · {c.email}
                </div>
              </div>
              <Pill tone="accent" size="sm">{c.segment}</Pill>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

// ---- Date range picker (lightweight calendar) ----------------------------
const fmt = (d) => d ? d.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" }) : "";
const iso = (d) => d ? d.toISOString().slice(0, 10) : "";
const parseDate = (s) => { if (!s) return null; const d = new Date(s); return isNaN(d) ? null : d; };

const Calendar = ({ start, end, onChange, onClose }) => {
  const [view, setView] = React.useState(start ? new Date(start) : new Date());
  const [hover, setHover] = React.useState(null);
  const y = view.getFullYear(), m = view.getMonth();
  const first = new Date(y, m, 1);
  const startDay = (first.getDay() + 6) % 7; // Mon = 0
  const daysInMonth = new Date(y, m + 1, 0).getDate();
  const cells = [];
  for (let i = 0; i < startDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(y, m, d));
  while (cells.length % 7) cells.push(null);

  const inRange = (d) => {
    if (!d || !start) return false;
    const e = end || hover;
    if (!e) return d.toDateString() === start.toDateString();
    const lo = start < e ? start : e, hi = start < e ? e : start;
    return d >= lo && d <= hi;
  };

  const onPick = (d) => {
    if (!start || (start && end)) onChange(d, null);
    else if (start && !end) {
      if (d < start) onChange(d, start);
      else onChange(start, d);
    }
  };

  const monthName = view.toLocaleDateString("en-GB", { month: "long", year: "numeric" });

  return (
    <div style={{
      background: tokens.surface, border: `1px solid ${tokens.border}`,
      borderRadius: 8, boxShadow: "0 16px 40px rgba(20,18,12,.14)",
      padding: 14, width: 300,
    }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 10 }}>
        <button onClick={() => setView(new Date(y, m - 1))} style={navBtn}>‹</button>
        <div style={{ fontSize: 13, fontWeight: 600, color: tokens.ink }}>{monthName}</div>
        <button onClick={() => setView(new Date(y, m + 1))} style={navBtn}>›</button>
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 2 }}>
        {["M", "T", "W", "T", "F", "S", "S"].map((d, i) => (
          <div key={i} style={{ textAlign: "center", fontSize: 10.5, color: tokens.faint, fontWeight: 600, padding: "4px 0" }}>{d}</div>
        ))}
        {cells.map((d, i) => {
          if (!d) return <div key={i} />;
          const isStart = start && d.toDateString() === start.toDateString();
          const isEnd = end && d.toDateString() === end.toDateString();
          const sel = isStart || isEnd;
          const inR = inRange(d);
          return (
            <button key={i}
              onMouseEnter={() => setHover(d)} onMouseLeave={() => setHover(null)}
              onClick={() => onPick(d)}
              style={{
                padding: "7px 0", fontSize: 12.5, fontFamily: tokens.sans,
                background: sel ? tokens.accent : (inR ? tokens.accentSoft : "transparent"),
                color: sel ? "#fff" : (inR ? tokens.accentInk : tokens.ink),
                border: "none", borderRadius: 4, cursor: "pointer", fontWeight: sel ? 600 : 400,
              }}>{d.getDate()}</button>
          );
        })}
      </div>
      <div style={{ display: "flex", justifyContent: "space-between", marginTop: 10, paddingTop: 10, borderTop: `1px solid ${tokens.border}` }}>
        <button onClick={() => onChange(null, null)} style={{ background: "none", border: "none", color: tokens.muted, fontSize: 12, cursor: "pointer" }}>Clear</button>
        <button onClick={onClose} style={{ background: "none", border: "none", color: tokens.accent, fontWeight: 600, fontSize: 12, cursor: "pointer" }}>Done</button>
      </div>
    </div>
  );
};
const navBtn = {
  width: 24, height: 24, border: `1px solid ${tokens.border}`,
  background: tokens.surface, borderRadius: 4, cursor: "pointer",
  color: tokens.ink2, fontSize: 14, lineHeight: 1,
};

const DateRangeField = ({ start, end, onChange }) => {
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h); return () => document.removeEventListener("mousedown", h);
  }, []);
  return (
    <div ref={ref} style={{ position: "relative" }}>
      <div onClick={() => setOpen(!open)} style={{
        display: "flex", alignItems: "center", gap: 10,
        border: `1px solid ${open ? tokens.accent : tokens.border}`,
        borderRadius: 6, padding: "10px 12px", background: tokens.surface,
        cursor: "pointer",
        boxShadow: open ? `0 0 0 3px ${tokens.accent}1A` : "none",
      }}>
        <svg width="15" height="15" viewBox="0 0 15 15" style={{ color: tokens.muted, flexShrink: 0 }}>
          <rect x="1.5" y="3" width="12" height="10" rx="1.5" stroke="currentColor" fill="none" strokeWidth="1.2" />
          <path d="M1.5 6h12M5 1.5v3M10 1.5v3" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
        </svg>
        <span style={{ fontSize: 13.5, color: start ? tokens.ink : tokens.faint, flex: 1 }}>
          {start ? fmt(start) : "Start date"} <span style={{ color: tokens.faint, margin: "0 6px" }}>→</span> {end ? fmt(end) : "End date"}
        </span>
        {start && end && (
          <Pill tone="neutral" size="sm">
            {Math.round((end - start) / 86400000) + 1} nights
          </Pill>
        )}
      </div>
      {open && (
        <div style={{ position: "absolute", top: "calc(100% + 6px)", left: 0, zIndex: 50 }}>
          <Calendar start={start} end={end} onChange={onChange} onClose={() => setOpen(false)} />
        </div>
      )}
    </div>
  );
};

// ---- Attachments dropzone ------------------------------------------------
const Attachments = ({ files, onChange }) => {
  const [drag, setDrag] = React.useState(false);
  const inputRef = React.useRef(null);
  const add = (list) => {
    const arr = Array.from(list).map(f => ({
      name: f.name, size: f.size, type: f.type || "application/octet-stream",
      _file: f, // keep actual File object for upload
    }));
    onChange([...(files || []), ...arr]);
  };
  return (
    <div>
      <div
        onDragOver={(e) => { e.preventDefault(); setDrag(true); }}
        onDragLeave={() => setDrag(false)}
        onDrop={(e) => { e.preventDefault(); setDrag(false); add(e.dataTransfer.files); }}
        onClick={() => inputRef.current?.click()}
        style={{
          border: `1.5px dashed ${drag ? tokens.accent : tokens.borderStrong}`,
          background: drag ? tokens.accentSoft : tokens.surface2,
          borderRadius: 8, padding: "20px 16px", textAlign: "center", cursor: "pointer",
          transition: "all .15s",
        }}>
        <div style={{ display: "inline-flex", alignItems: "center", justifyContent: "center", width: 36, height: 36, borderRadius: "50%", background: tokens.surface, border: `1px solid ${tokens.border}`, marginBottom: 8 }}>
          <svg width="16" height="16" viewBox="0 0 16 16" style={{ color: tokens.accent }}>
            <path d="M8 11V3M4.5 6.5L8 3l3.5 3.5M3 13h10" stroke="currentColor" fill="none" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </div>
        <div style={{ fontSize: 13.5, color: tokens.ink, fontWeight: 500 }}>
          Drop files or <span style={{ color: tokens.accent, textDecoration: "underline" }}>browse</span>
        </div>
        <div style={{ fontSize: 11.5, color: tokens.muted, marginTop: 4 }}>Rate confirmations, signed contracts, RFP responses · PDF, DOCX, XLSX</div>
        <input ref={inputRef} type="file" multiple onChange={(e) => add(e.target.files)} style={{ display: "none" }} />
      </div>
      {files && files.length > 0 && (
        <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 6 }}>
          {files.map((f, i) => (
            <div key={i} style={{
              display: "flex", alignItems: "center", gap: 10,
              padding: "8px 10px", background: tokens.surface, border: `1px solid ${tokens.border}`,
              borderRadius: 6,
            }}>
              <div style={{
                width: 28, height: 28, borderRadius: 4, background: tokens.accentSoft,
                display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 9.5, fontWeight: 700, color: tokens.accentInk, fontFamily: tokens.mono,
              }}>{(f.name.split(".").pop() || "FILE").toUpperCase().slice(0, 4)}</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, color: tokens.ink, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{f.name}</div>
                <div style={{ fontSize: 11.5, color: tokens.muted }}>{(f.size / 1024).toFixed(1)} KB</div>
              </div>
              <button onClick={() => onChange(files.filter((_, j) => j !== i))} style={{
                background: "transparent", border: "none", color: tokens.muted, cursor: "pointer", fontSize: 16,
              }}>×</button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

// ---- Pricing matrix ------------------------------------------------------
const PricingMatrix = ({ priceCodeType, rooms, meals, matrix, onChange, currency = "GBP", modes = {}, onModesChange }) => {
  const isBase = priceCodeType === "Dynamic Rate";
  const [tab, setTab] = React.useState("prices");
  const [globalMode, setGlobalMode] = React.useState("AMT"); // "AMT" or "PCT"
  const set = (rt, mp, occ, val) => {
    const key = `${rt}|${mp}|${occ}`;
    onChange({ ...matrix, [key]: val });
  };
  const get = (rt, mp, occ) => matrix[`${rt}|${mp}|${occ}`] || "";
  const rowMode = (rt, mp) => modes[`${rt}|${mp}`] || "AMT";
  const setRowMode = (rt, mp, m) => {
    if (onModesChange) onModesChange({ ...modes, [`${rt}|${mp}`]: m });
  };

  if (rooms.length === 0 || meals.length === 0) {
    return (
      <div style={{ padding: 20, background: tokens.surface2, borderRadius: 6, textAlign: "center", color: tokens.muted, fontSize: 13 }}>
        Select at least one room type and meal plan to define pricing.
      </div>
    );
  }

  const currSymbol = currency === "GBP" ? "£" : currency === "EUR" ? "€" : currency === "USD" ? "$" : currency;
  const isPct = globalMode === "PCT";
  const occCols = [
    { key: "1", label: isPct ? "% 1AD *" : "Amount 1AD *" },
    { key: "2", label: isPct ? "% 2AD *" : "Amount 2AD *" },
    { key: "X", label: isPct ? "% Extra AD" : "Amount Extra AD" },
    { key: "C", label: isPct ? "% Child" : "Amount Child" },
  ];

  return (
    <div style={{ border: `1px solid ${tokens.border}`, borderRadius: 4, overflow: "hidden", background: tokens.surface }}>
      <div style={{ display: "flex", alignItems: "center", borderBottom: `1px solid ${tokens.border}`, background: tokens.surface2, padding: "8px 14px" }}>
        <div style={{ fontSize: 12, fontWeight: 600, color: "#32363A" }}>Prices</div>
        <div style={{ flex: 1 }}></div>
        {isBase && (
          <div style={{ display: "inline-flex", border: `1px solid ${tokens.border}`, borderRadius: 5, overflow: "hidden", marginRight: 10 }}>
            {[{ k: "AMT", l: currSymbol }, { k: "PCT", l: "%" }].map(m => {
              const a = globalMode === m.k;
              return (
                <button key={m.k} type="button" onClick={() => setGlobalMode(m.k)} style={{
                  padding: "4px 12px", fontSize: 12, fontWeight: a ? 700 : 500,
                  background: a ? tokens.accent : "transparent",
                  color: a ? "#fff" : tokens.muted,
                  border: "none", cursor: "pointer", minWidth: 32,
                }}>{m.l}</button>
              );
            })}
          </div>
        )}
        <div style={{ fontSize: 11, color: tokens.muted }}>
          {isPct ? "% adjustments (negative = discount)" : (isBase ? "+/− Supplements" : "Fixed amounts")} · {currency}
        </div>
      </div>
      <div style={{ overflowX: "auto" }}>
        <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 13 }}>
          <thead>
            <tr style={{ background: "#F7F7F7" }}>
              <th style={{ ...emmaTh, width: "20%" }}>Room Type</th>
              <th style={{ ...emmaTh, width: "22%" }}>Meal plan</th>
              {occCols.map(c => (
                <th key={c.key} style={{ ...emmaTh, textAlign: "right" }}>{c.label}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {rooms.flatMap(rt =>
              meals.map(mp => (
                <tr key={`${rt}-${mp}`} style={{ borderTop: `1px solid #EAEAEA` }}>
                  <td style={emmaTd}>
                    <DropdownCell value={ROOM_TYPES.find(r => r.code === rt)?.label || rt} />
                  </td>
                  <td style={emmaTd}>
                    <DropdownCell value={MEAL_PLANS.find(m => m.code === mp)?.label || mp} />
                  </td>
                  {occCols.map(c => (
                    <td key={c.key} style={{ ...emmaTd, padding: 0 }}>
                      <input
                        value={get(rt, mp, c.key)}
                        onChange={(e) => set(rt, mp, c.key, e.target.value)}
                        placeholder={isPct ? "± 0%" : (isBase ? "± 0.00" : "0.00")}
                        style={{
                          width: "100%", padding: "8px 14px", fontFamily: tokens.sans, fontSize: 13,
                          border: "none", outline: "none", textAlign: "right",
                          background: (!get(rt, mp, c.key) && (c.key === "1" || c.key === "2")) ? "#FFF5F5" : "transparent",
                          color: tokens.ink,
                        }}
                      />
                    </td>
                  ))}
                </tr>
              ))
            )}
            <tr><td colSpan={6} style={{ height: 32, background: "#FAFAFA", borderTop: `1px solid #EAEAEA` }}></td></tr>
          </tbody>
        </table>
      </div>
    </div>
  );
};
const DropdownCell = ({ value }) => (
  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, padding: "4px 0" }}>
    <span style={{ fontSize: 13, color: tokens.ink }}>{value}</span>
    <svg width="10" height="10" viewBox="0 0 10 10" style={{ color: "#0A6ED1", flexShrink: 0 }}><path d="M1.5 3.5L5 7l3.5-3.5" stroke="currentColor" strokeWidth="1.4" fill="none" strokeLinecap="round" strokeLinejoin="round"/></svg>
  </div>
);
const emmaTh = {
  padding: "10px 14px", textAlign: "left", fontSize: 12.5, fontWeight: 500,
  color: "#32363A", borderRight: "1px solid #EAEAEA",
};
const emmaTd = {
  padding: "6px 14px", fontSize: 13, color: tokens.ink, verticalAlign: "middle",
  borderRight: "1px solid #EAEAEA", background: tokens.surface,
};

// ---- Multi-select chips --------------------------------------------------
const ChipMultiSelect = ({ options, value = [], onChange }) => {
  const toggle = (v) => {
    if (value.includes(v)) onChange(value.filter(x => x !== v));
    else onChange([...value, v]);
  };
  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: 7 }}>
      {options.map(o => {
        const v = typeof o === "string" ? o : o.code;
        const l = typeof o === "string" ? o : o.label;
        const active = value.includes(v);
        return (
          <button key={v} onClick={() => toggle(v)} style={{
            display: "inline-flex", alignItems: "center", gap: 6,
            padding: "7px 11px", fontSize: 12.5, fontWeight: 500,
            background: active ? tokens.accent : tokens.surface,
            color: active ? "#fff" : tokens.ink,
            border: `1px solid ${active ? tokens.accent : tokens.border}`,
            borderRadius: 999, cursor: "pointer", transition: "all .15s",
          }}>
            <span style={{ fontFamily: tokens.mono, fontSize: 11, opacity: active ? 0.85 : 0.6 }}>{v}</span>
            {l}
          </button>
        );
      })}
    </div>
  );
};

// ---- Section heading -----------------------------------------------------
const SectionHeader = ({ num, title, subtitle, status }) => (
  <div style={{ display: "flex", alignItems: "flex-start", gap: 14, marginBottom: 18 }}>
    {num && (
      <div style={{
        width: 28, height: 28, borderRadius: "50%",
        background: tokens.accent, color: "#fff",
        display: "flex", alignItems: "center", justifyContent: "center",
        fontSize: 12, fontWeight: 700, fontFamily: tokens.mono, flexShrink: 0, marginTop: 2,
      }}>{num}</div>
    )}
    <div style={{ flex: 1 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <h3 style={{ margin: 0, fontSize: 22, fontWeight: 500, color: tokens.ink, letterSpacing: -0.3, fontFamily: "'Cormorant Garamond', 'EB Garamond', Georgia, serif" }}>{title}</h3>
        {status}
      </div>
      {subtitle && <div style={{ fontSize: 13, color: tokens.muted, marginTop: 4, lineHeight: 1.5 }}>{subtitle}</div>}
    </div>
  </div>
);

// ---- Default empty state for the form -----------------------------------
const emptyForm = () => ({
  // Requester
  requesterName: "",
  requesterEmail: "",
  requesterRegion: "EMEA",
  // Customer
  customer: null,
  customerIsNew: false,
  customerId: "",
  customerIdType: "",
  customerIds: [{ type: "", id: "" }],
  customerName: "",
  customerAddress: "",
  customerEmail: "",
  customerPhone: "",
  // Contract
  existingContract: null, // MOCK_CONTRACTS entry when reusing an existing contract
  contractName: "",
  contractCode: "",
  contractStart: null,
  contractEnd: null,
  contractCurrency: "GBP",
  contractTax: "Inclusive",
  rateType: "",
  lraType: "",
  hotels: [],
  hotelConfigs: {},
  channels: [],
  lanyonManaged: "",
  // Per-hotel detail (only used when hotels.length > 1)
  activeHotelTab: null,
  hotelMatrices: {},     // { [hotelId]: { [seasonOrFixed]: { cell: amount } } }
  matrixModes: {},        // { [seasonId|"row"|rt|mp]: "AMT" | "PCT" } — Base only; per-row mode (€ vs %)
  basedOnRate: "",        // Base PC: which rate the base derives from (e.g. BAR, FLEX)
  hotelCancellation: {}, // { [hotelId]: [policyCodes] }
  // Price code
  priceCodeType: "Dynamic Rate",
  priceCodeName: "",
  perPerson: true,
  mealPlanAllocation: true,
  seasons: [{ id: "S1", name: "", basePrice: "", start: null, end: null, basedOnRate: "", discountPercent: "", dowFilter: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] }],
  activeSeasonId: "S1",
  seasonMatrices: {},
  rooms: [""],
  meals: [],
  seasons: [{ id: "S1", name: "", basePrice: "", start: null, end: null, basedOnRate: "", discountPercent: "", dowFilter: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] }],
  activeSeasonId: "S1",
  seasonMatrices: {},
  matrix: {},
  // Rate
  rateCode: "",
  rateName: "",
  rateDescription: "",
  rateInclusions: "",
  marketSegment: "",
  ratePriority: "Standard",
  refundable: true,
  cancellationPolicies: [],
  blackoutDates: "",
  // Stay options
  minLOS: "1",
  maxLOS: "",
  dowAllowed: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
  advancePurchase: false,
  advanceDays: "",
  commissionable: true,
  commissionPercent: "",
  closedToArrival: false,
  cancellationPolicy: "",
  allocation: "",
  bookingStart: null,
  bookingEnd: null,
  travelStart: null,
  travelEnd: null,
  // Notes
  notes: "",
  attachments: [],
  acknowledged: false,
});

// ---- Sample-fill helper (for demo realism) ------------------------------
const sampleFill = () => ({
  requesterName: "Mara Vlček",
  requesterEmail: "mara.vlcek@axiomhospitality.com",
  requesterRegion: "EMEA",
  customer: MOCK_CUSTOMERS[3],
  customerEmail: "procurement@bluepeakconsulting.com",
  customerPhone: "+44 20 7123 4567",
  contractName: "Bluepeak Consulting — 2026 Negotiated",
  contractCode: "BLPK-26-NEG",
  contractStart: new Date(2026, 0, 1),
  contractEnd: new Date(2026, 11, 31),
  contractCurrency: "GBP",
  contractTax: "Inclusive",
  rateType: "Corporate Negotiated",
  lraType: "LRA",
  hotels: ["GBEDLONBER", "GBEDLONBSH", "GBEDLONHAM"],
  hotelConfigs: {
    "GBEDLONBER": { rooms: ["Standard Double", "Superior King"], meals: ["RO", "BB"] },
    "GBEDLONBSH": { rooms: ["Standard Double", "Deluxe Twin"], meals: ["RO"] },
    "GBEDLONHAM": { rooms: ["Standard Double", "Superior King", "Junior Suite"], meals: ["RO", "BB"] },
  },
  channels: ["DIRECT", "WEB", "GDS"],
  priceCodeType: "Dynamic Rate",
  priceCodeName: "Corp Base PC — Bluepeak — Single/Double Suppl. 25",
  perPerson: true,
  mealPlanAllocation: true,
  rooms: ["Standard Double", "Superior King"],
  meals: ["RO", "BB"],
  seasons: [
    { id: "S1", name: "Low season midweek", basePrice: "70.00", start: new Date(2026, 0, 1), end: new Date(2026, 3, 30), basedOnRate: "BAR", discountPercent: "-10", dowFilter: ["MO", "TU", "WE", "TH", "FR"] },
    { id: "S2", name: "Low season weekend", basePrice: "70.00", start: new Date(2026, 0, 1), end: new Date(2026, 3, 30), basedOnRate: "BAR", discountPercent: "-5", dowFilter: ["SA", "SU"] },
    { id: "S3", name: "High season", basePrice: "110.00", start: new Date(2026, 4, 1), end: new Date(2026, 8, 30), basedOnRate: "BAR", discountPercent: "-15", dowFilter: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] },
    { id: "S4", name: "Shoulder", basePrice: "85.00", start: new Date(2026, 9, 1), end: new Date(2026, 11, 31), basedOnRate: "Best Flexible Rate", discountPercent: "-12", dowFilter: ["MO", "TU", "WE", "TH", "FR", "SA", "SU"] },
  ],
  activeSeasonId: "S1",
  seasonMatrices: {
    S1: {
      "STD|RO|1": "0.00", "STD|RO|2": "20.00", "STD|RO|X": "30.00",
      "STD|BB|1": "15.00", "STD|BB|2": "50.00", "STD|BB|X": "45.00",
      "PRM|RO|1": "40.00", "PRM|RO|2": "60.00", "PRM|RO|X": "30.00",
      "PRM|BB|1": "55.00", "PRM|BB|2": "90.00", "PRM|BB|X": "45.00",
    },
    S2: {
      "STD|RO|1": "0.00", "STD|RO|2": "30.00", "STD|RO|X": "35.00",
      "STD|BB|1": "18.00", "STD|BB|2": "66.00", "STD|BB|X": "53.00",
      "PRM|RO|1": "55.00", "PRM|RO|2": "90.00", "PRM|RO|X": "35.00",
      "PRM|BB|1": "73.00", "PRM|BB|2": "126.00", "PRM|BB|X": "53.00",
    },
    S3: {},
  },
  matrix: {
    "STD|RO|1": "0.00", "STD|RO|2": "25.00", "STD|RO|X": "30.00",
    "STD|BB|1": "18.00", "STD|BB|2": "61.00", "STD|BB|X": "48.00",
    "PRM|RO|1": "45.00", "PRM|RO|2": "70.00", "PRM|RO|X": "30.00",
    "PRM|BB|1": "63.00", "PRM|BB|2": "106.00", "PRM|BB|X": "48.00",
  },
  rateCode: "BLPK-26-NEG-COR",
  rateName: "Bluepeak Corporate 2026",
  rateDescription: "Negotiated corporate rate for Bluepeak Consulting — includes standard and premium rooms",
  rateInclusions: "WiFi, breakfast, late checkout (subject to availability)",
  marketSegment: "Corporate",
  ratePriority: "Standard",
  refundable: true,
  cancellationPolicies: ["FLEX48", "NR"],
  cancellationPolicy: "Free cancellation up to 48 hours before arrival. After 48 hours: first night charge. No-show: full stay charged. Groups (10+ rooms): 30 days notice required.",
  commissionPercent: "10",
  blackoutDates: "24 Dec 2026 – 02 Jan 2027",
  minLOS: "1", maxLOS: "14",
  bookingStart: new Date(2025, 10, 1), bookingEnd: new Date(2026, 11, 15),
  travelStart: new Date(2026, 0, 1), travelEnd: new Date(2026, 11, 31),
  notes: "",
  attachments: [
    { name: "Bluepeak_2026_RateSheet.pdf", size: 248321, type: "application/pdf" },
    { name: "Signed_MSA_v2.pdf", size: 412918, type: "application/pdf" },
  ],
  acknowledged: false,
});

// ---- Field grid layout helper -------------------------------------------
const Field = ({ label, required, hint, children, span = 6, htmlFor }) => (
  <div style={{ gridColumn: `span ${span}` }}>
    <Label required={required} hint={hint} htmlFor={htmlFor}>{label}</Label>
    {children}
  </div>
);

const Grid = ({ children, gap = 14 }) => (
  <div style={{ display: "grid", gridTemplateColumns: "repeat(12, 1fr)", gap }}>{children}</div>
);

// ---- Helpers exported to window -----------------------------------------
Object.assign(window, {
  EMMA_TOKENS: tokens,
  EMMA_DATA: { MOCK_CUSTOMERS, MOCK_CONTRACTS, MOCK_HOTELS, ROOM_TYPES, MEAL_PLANS, CURRENCIES, RATE_TYPES, PRICE_CODE_TYPES, CANCELLATION_POLICIES, HOTEL_PROFILE, NOMENCLATURE, MARKET_SEGMENTS },
  suggestCodes,
  Label, TextField, Select, TextArea, Checkbox, Radio, Pill, Button,
  CustomerLookup, ContractLookup, DateRangeField, Attachments, PricingMatrix,
  ChipMultiSelect, SectionHeader, Field, Grid,
  emptyForm, sampleFill, fmt, iso,
});
