// CreateWO.jsx — Admin-only booking creation. Strict shift options (8am-12pm / 1pm-5pm / Custom).

// Reusable searchable select with an inline "+ Add new" footer.
// `options` is an array of strings; `onAddNew` (optional) opens an add UI.
function SearchableSelect({ value, onChange, options, placeholder, onAddNew, addLabel, error, emptyText, disabled }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const ref = React.useRef(null);
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    setTimeout(() => inputRef.current && inputRef.current.focus(), 30);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const filtered = (options || []).filter(o => o.toLowerCase().includes(q.toLowerCase()));

  return (
    <div ref={ref} style={{position: 'relative'}}>
      <button type="button" disabled={disabled}
        onClick={() => !disabled && setOpen(!open)}
        className="input"
        style={{
          width: '100%', height: 42, padding: '0 12px',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          textAlign: 'left', cursor: disabled ? 'not-allowed' : 'pointer',
          background: disabled ? 'var(--color-grey-100)' : (error ? '#fef2f2' : 'white'),
          borderColor: error ? '#dc2626' : 'var(--color-grey-300)',
          color: disabled ? 'var(--color-grey-400)' : 'inherit',
        }}>
        <span style={{flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          color: value ? 'inherit' : 'var(--color-grey-500)', fontSize: 14}}>
          {value || placeholder}
        </span>
        <ChevDown size={16} style={{color: 'var(--color-grey-500)', flexShrink: 0, transition: 'transform 0.15s', transform: open ? 'rotate(180deg)' : 'none'}}/>
      </button>
      {open ? (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, right: 0,
          background: 'white', border: '1px solid var(--color-grey-300)',
          borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
          zIndex: 60, maxHeight: 320, display: 'flex', flexDirection: 'column',
        }}>
          <div style={{padding: 8, borderBottom: '1px solid var(--color-grey-200)', position: 'relative'}}>
            <Search size={14} style={{position: 'absolute', left: 18, top: '50%', transform: 'translateY(-50%)', color: 'var(--color-grey-500)'}}/>
            <input ref={inputRef} value={q} onChange={e => setQ(e.target.value)}
              placeholder="Search..." className="input"
              style={{width: '100%', height: 34, paddingLeft: 32, fontSize: 13}}/>
          </div>
          <div style={{flex: 1, overflowY: 'auto', padding: '4px 0'}}>
            {filtered.length === 0 ? (
              <div style={{padding: '12px 16px', fontSize: 13, color: 'var(--color-grey-500)', fontStyle: 'italic'}}>
                {emptyText || 'No matches.'}
              </div>
            ) : filtered.map(o => (
              <button type="button" key={o}
                onClick={() => { onChange(o); setOpen(false); setQ(''); }}
                style={{
                  display: 'block', width: '100%', textAlign: 'left',
                  padding: '8px 14px', fontSize: 13, cursor: 'pointer',
                  background: value === o ? 'rgba(255,213,0,0.10)' : 'transparent',
                  color: value === o ? 'var(--color-navy)' : 'inherit',
                  fontWeight: value === o ? 600 : 400,
                }}>
                {o}
              </button>
            ))}
          </div>
          {onAddNew ? (
            <button type="button" onClick={() => { setOpen(false); setQ(''); onAddNew(); }}
              style={{
                padding: '10px 14px', borderTop: '1px solid var(--color-grey-200)',
                fontSize: 13, fontWeight: 600, color: 'var(--color-navy)',
                display: 'flex', alignItems: 'center', gap: 6,
                background: 'var(--color-grey-50)', borderRadius: '0 0 8px 8px',
              }}>
              <Plus size={14}/> {addLabel || 'Add new'}
            </button>
          ) : null}
        </div>
      ) : null}
    </div>
  );
}

// Machine picker — shows "PLATE (TONNAGE)" + a live status colour pill per
// option (and on the selected value). Searchable by plate or fleet id.
function MachineSelect({ value, onChange, machines, error, disabled }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const ref = React.useRef(null);
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    setTimeout(() => inputRef.current && inputRef.current.focus(), 30);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const statusMeta = (s) => s === 'available' ? { c: 'green', label: 'Available' }
    : s === 'occupied' ? { c: 'red', label: 'Occupied' }
    : { c: 'gray', label: 'Not available' };

  const selected = machines.find(m => m.id === value);
  const filtered = machines.filter(m =>
    (machineLabel(m) + ' ' + m.id).toLowerCase().includes(q.toLowerCase()));

  return (
    <div ref={ref} style={{position: 'relative'}}>
      <button type="button" disabled={disabled} onClick={() => !disabled && setOpen(!open)} className="input"
        style={{
          width: '100%', height: 42, padding: '0 12px', gap: 8,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          textAlign: 'left', cursor: disabled ? 'not-allowed' : 'pointer',
          background: disabled ? 'var(--color-grey-100)' : (error ? '#fef2f2' : 'white'),
          borderColor: error ? '#dc2626' : 'var(--color-grey-300)',
        }}>
        {selected ? (
          <span style={{display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1}}>
            <span style={{fontWeight: 600, fontSize: 14, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap'}}>{machineLabel(selected)}</span>
            <span className={"status status--" + statusMeta(selected.status).c} style={{flexShrink: 0}}>{statusMeta(selected.status).label}</span>
          </span>
        ) : (
          <span style={{flex: 1, color: 'var(--color-grey-500)', fontSize: 14}}>— Select machine —</span>
        )}
        <ChevDown size={16} style={{color: 'var(--color-grey-500)', flexShrink: 0, transition: 'transform 0.15s', transform: open ? 'rotate(180deg)' : 'none'}}/>
      </button>
      {open ? (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, right: 0,
          background: 'white', border: '1px solid var(--color-grey-300)',
          borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
          zIndex: 60, maxHeight: 340, display: 'flex', flexDirection: 'column',
        }}>
          <div style={{padding: 8, borderBottom: '1px solid var(--color-grey-200)', position: 'relative'}}>
            <Search size={14} style={{position: 'absolute', left: 18, top: '50%', transform: 'translateY(-50%)', color: 'var(--color-grey-500)'}}/>
            <input ref={inputRef} value={q} onChange={e => setQ(e.target.value)}
              placeholder="Search plate or fleet id..." className="input"
              style={{width: '100%', height: 34, paddingLeft: 32, fontSize: 13}}/>
          </div>
          <div style={{flex: 1, overflowY: 'auto', padding: '4px 0'}}>
            {filtered.length === 0 ? (
              <div style={{padding: '12px 16px', fontSize: 13, color: 'var(--color-grey-500)', fontStyle: 'italic'}}>No machines match.</div>
            ) : filtered.map(m => {
              const sm = statusMeta(m.status);
              return (
                <button type="button" key={m.id}
                  onClick={() => { onChange(m.id); setOpen(false); setQ(''); }}
                  style={{
                    display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
                    width: '100%', textAlign: 'left', padding: '9px 14px', fontSize: 13, cursor: 'pointer',
                    background: value === m.id ? 'rgba(255,213,0,0.10)' : 'transparent',
                  }}>
                  <span style={{fontWeight: value === m.id ? 600 : 500, color: 'var(--color-navy)'}}>{machineLabel(m)}</span>
                  <span className={"status status--" + sm.c} style={{flexShrink: 0}}>{sm.label}</span>
                </button>
              );
            })}
          </div>
        </div>
      ) : null}
    </div>
  );
}

// Small reusable cert-dot row. Expired certs are hidden entirely; certs
// inside the renewal window get an amber outline + "renew soon" tooltip.
// `compact` hides the text codes (dots only).
function CertDots({ certs, compact }) {
  const active = activeCerts(certs);
  if (active.length === 0) {
    return <span style={{fontSize: 11, color: 'var(--color-grey-400)', fontStyle: 'italic'}}>No valid certs</span>;
  }
  return (
    <span style={{display: 'inline-flex', flexWrap: 'wrap', gap: compact ? 4 : 6, alignItems: 'center'}}>
      {active.map(c => {
        const expiring = certState(c.exp) === 'expiring';
        return (
          <span key={c.code}
            title={(CERT_LABEL[c.code] || c.code) + ' · valid to ' + fmtCertDate(c.exp) + (expiring ? ' · renew soon' : '')}
            style={{
              display: 'inline-flex', alignItems: 'center', gap: 4,
              padding: compact ? 0 : '1px 6px 1px 5px', borderRadius: 999,
              background: compact ? 'transparent' : (expiring ? 'rgba(217,119,6,0.12)' : 'var(--color-grey-100)'),
              border: !compact && expiring ? '1px solid rgba(217,119,6,0.5)' : '1px solid transparent',
              fontSize: 10.5, fontWeight: 700, letterSpacing: '0.02em',
              color: expiring ? '#b45309' : 'var(--color-grey-700)', lineHeight: 1.6, whiteSpace: 'nowrap',
            }}>
            <span style={{width: 7, height: 7, borderRadius: '50%', background: CERT_COLOR[c.code] || '#999', flexShrink: 0}}/>
            {compact ? null : c.code}
            {!compact && expiring ? <Clock size={9} style={{marginLeft: 1}}/> : null}
          </span>
        );
      })}
    </span>
  );
}

// Operator picker — like MachineSelect, but each row shows the operator's
// certifications as coloured short-code chips so dispatch can see who is
// qualified while assigning. A legend sits at the top of the dropdown.
function OperatorSelect({ value, onChange, error }) {
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const ref = React.useRef(null);
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    setTimeout(() => inputRef.current && inputRef.current.focus(), 30);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const selected = OPERATORS.find(o => o.name === value);
  const filtered = OPERATORS.filter(o =>
    (o.name + ' ' + activeCerts(o.certs).map(c => c.code).join(' ')).toLowerCase().includes(q.toLowerCase()));

  return (
    <div ref={ref} style={{position: 'relative'}}>
      <button type="button" onClick={() => setOpen(!open)} className="input"
        style={{
          width: '100%', minHeight: 42, padding: '6px 12px', gap: 8,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          textAlign: 'left', cursor: 'pointer',
          background: error ? '#fef2f2' : 'white',
          borderColor: error ? '#dc2626' : 'var(--color-grey-300)',
        }}>
        {selected ? (
          <span style={{display: 'flex', alignItems: 'center', gap: 10, minWidth: 0, flex: 1, flexWrap: 'wrap'}}>
            <span style={{fontWeight: 600, fontSize: 14, color: 'var(--color-navy)'}}>{selected.name}{selected.status === 'On Job' ? ' (on job)' : ''}</span>
            <CertDots certs={selected.certs}/>
          </span>
        ) : (
          <span style={{flex: 1, color: 'var(--color-grey-500)', fontSize: 14}}>— Select operator —</span>
        )}
        <ChevDown size={16} style={{color: 'var(--color-grey-500)', flexShrink: 0, transition: 'transform 0.15s', transform: open ? 'rotate(180deg)' : 'none'}}/>
      </button>
      {open ? (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, right: 0,
          background: 'white', border: '1px solid var(--color-grey-300)',
          borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
          zIndex: 60, maxHeight: 420, display: 'flex', flexDirection: 'column',
        }}>
          {/* Legend */}
          <div style={{padding: '10px 12px', borderBottom: '1px solid var(--color-grey-200)', background: 'var(--color-grey-50)'}}>
            <div style={{fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--color-grey-500)', marginBottom: 6}}>Certification legend</div>
            <div style={{display: 'flex', flexWrap: 'wrap', gap: '6px 10px'}}>
              {CERT_LEGEND.map(c => (
                <span key={c.code} title={c.label} style={{display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 11, color: 'var(--color-grey-700)'}}>
                  <span style={{width: 8, height: 8, borderRadius: '50%', background: c.color}}/>
                  <strong style={{fontWeight: 700}}>{c.code}</strong>
                  <span style={{color: 'var(--color-grey-500)'}}>{c.label}</span>
                </span>
              ))}
            </div>
          </div>
          <div style={{padding: 8, borderBottom: '1px solid var(--color-grey-200)', position: 'relative'}}>
            <Search size={14} style={{position: 'absolute', left: 18, top: '50%', transform: 'translateY(-50%)', color: 'var(--color-grey-500)'}}/>
            <input ref={inputRef} value={q} onChange={e => setQ(e.target.value)}
              placeholder="Search name or cert (e.g. OGSP)..." className="input"
              style={{width: '100%', height: 34, paddingLeft: 32, fontSize: 13}}/>
          </div>
          <div style={{flex: 1, overflowY: 'auto', padding: '4px 0'}}>
            {filtered.length === 0 ? (
              <div style={{padding: '12px 16px', fontSize: 13, color: 'var(--color-grey-500)', fontStyle: 'italic'}}>No operators match.</div>
            ) : filtered.map(o => (
              <button type="button" key={o.id}
                onClick={() => { onChange(o.name); setOpen(false); setQ(''); }}
                style={{
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
                  width: '100%', textAlign: 'left', padding: '9px 14px', cursor: 'pointer',
                  background: value === o.name ? 'rgba(255,213,0,0.10)' : 'transparent',
                }}>
                <span style={{display: 'flex', flexDirection: 'column', gap: 4, minWidth: 0}}>
                  <span style={{fontWeight: value === o.name ? 700 : 500, fontSize: 13, color: 'var(--color-navy)'}}>
                    {o.name}{o.status === 'On Job' ? <span style={{color: '#d97706', fontWeight: 600}}> · on job</span> : null}
                  </span>
                  <CertDots certs={o.certs}/>
                </span>
              </button>
            ))}
          </div>
        </div>
      ) : null}
    </div>
  );
}

// Greyed-out style for management-locked inputs in Modify Booking.
const lockedInputStyle = {
  background: 'var(--color-grey-100)',
  color: 'var(--color-grey-500)',
  cursor: 'not-allowed',
};

// Inline "only Management can change this" note shown under a locked field.
function MgmtLockHint() {
  return (
    <div style={{fontSize: 11, color: 'var(--color-grey-500)', marginTop: 4, display: 'flex', alignItems: 'center', gap: 4}}>
      <Lock size={11} style={{flexShrink: 0}}/> Locked — only Management can change this.
    </div>
  );
}

// Mint the next sequential work-order / booking number, e.g. BK-2026-00424.
// Scans existing BOOKINGS for the highest numeric suffix, keeps the same
// prefix, and increments — so a duplicated job gets its own fresh WO number.
function genWorkOrderId() {
  let max = 0, prefix = 'BK-2026-';
  if (typeof BOOKINGS !== 'undefined') {
    BOOKINGS.forEach(b => {
      const m = /^(.*?)(\d+)$/.exec(b.id || '');
      if (m) { const n = parseInt(m[2], 10); if (n >= max) { max = n; prefix = m[1]; } }
    });
  }
  return prefix + String(max + 1).padStart(5, '0');
}

function CreateWO({ onSubmit, onBack, role, drawer }) {
  // Pickup any pre-fill payload set by another page (e.g. clicking an Available
  // machine row in Current Bookings). Read once, then clear in useEffect so
  // we don't mutate state during render.
  const prefill = React.useRef(window.__createPrefill || null).current;
  React.useEffect(() => { if (typeof window !== 'undefined') window.__createPrefill = null; }, []);

  // Edit mode — when an existing booking is passed in, this whole form acts as
  // the "Modify Booking" page: every field pre-fills from the booking, the
  // heading/button text switch, and Submit writes the changes back in place.
  const editBooking = (prefill && prefill.editBooking) || null;
  const wasEdit = !!editBooking;
  // "Duplicate" — when an existing booking is open in Modify mode, the user can
  // copy every detail into a brand-new booking (same job, different date). We
  // keep all current field values in state and just flip the form out of edit
  // mode so Submit creates a fresh booking instead of overwriting this one.
  const [duplicated, setDuplicated] = React.useState(false);
  const isEdit = wasEdit && !duplicated;
  // The work-order number this duplicate will receive when submitted (preview).
  const pendingWO = duplicated ? genWorkOrderId() : null;
  // Company, Price, Diesel Surcharge and Overtime are commercial fields locked
  // once a booking exists — only Management may change them. Everyone else sees
  // them read-only in Modify Booking. (Creating a new booking is unaffected.)
  const mgmtLocked = isEdit && role !== 'Management';
  // Strip a leading "RM " (and keep digits/commas/dots) so price fields edit cleanly.
  const stripRM = (v) => (v == null ? '' : String(v).replace(/^\s*RM\s*/i, '').trim());
  // Resolve the booking's machine id from its "PLC-005 Tadano …" machine string.
  const editMachineId = isEdit ? ((editBooking.machine || '').split(' ')[0]) : '';

  // Default state: empty for everything except today's date.
  // Prefill (set from another page) can override per-field.
  const initialMachineId = editMachineId || (prefill && prefill.machineId) || '';
  const blankCustomer = isEdit ? false : !(prefill && prefill.customerId);

  // Infer a shift id from the booking's stored shift / times.
  const editShift = isEdit ? (editBooking.shift || 'custom') : null;
  const [shift, setShift] = React.useState(isEdit ? (editShift === 'morning' || editShift === 'afternoon' || editShift === 'custom' ? editShift : 'fullday') : 'fullday');
  const [start, setStart] = React.useState(isEdit ? (editBooking.start || '08:00') : '08:00');
  const [end, setEnd] = React.useState(isEdit ? (editBooking.end || '17:00') : '17:00');
  const prefillDate = isEdit ? (editBooking.date || TODAY) : ((prefill && prefill.date) || TODAY);
  const [dateFrom, setDateFrom] = React.useState(prefillDate);
  const [dateTo, setDateTo] = React.useState(prefillDate);

  // Day preset — quick-pick row at top of the Schedule section.
  const addDays = (iso, n) => {
    const d = new Date(iso + 'T00:00:00');
    d.setDate(d.getDate() + n);
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, '0');
    const dd = String(d.getDate()).padStart(2, '0');
    return `${y}-${m}-${dd}`;
  };
  const tomorrow = addDays(TODAY, 1);
  const dayAfter = addDays(TODAY, 2);

  // Infer initial Day preset from the current dateFrom/dateTo. Defaults to Today.
  const inferDayPreset = (from, to) => {
    if (from !== to) return 'custom';
    if (from === TODAY) return 'today';
    if (from === tomorrow) return 'tomorrow';
    if (from === dayAfter) return 'dayafter';
    return 'custom';
  };
  const [dayPreset, setDayPreset] = React.useState(inferDayPreset(dateFrom, dateTo));

  const applyDayPreset = (id) => {
    setDayPreset(id);
    if (id === 'today')     { setDateFrom(TODAY);    setDateTo(TODAY); }
    if (id === 'tomorrow')  { setDateFrom(tomorrow); setDateTo(tomorrow); }
    if (id === 'dayafter')  { setDateFrom(dayAfter); setDateTo(dayAfter); }
    // 'custom' — leave dates as-is so user can edit
  };
  const [photo, setPhoto] = React.useState(null);
  const [toolsOpen, setToolsOpen] = React.useState(false);
  const [selectedTools, setSelectedTools] = React.useState([]);
  const toolsRef = React.useRef(null);

  // Close tools dropdown when clicking outside
  React.useEffect(() => {
    if (!toolsOpen) return;
    const onClick = (e) => { if (toolsRef.current && !toolsRef.current.contains(e.target)) setToolsOpen(false); };
    document.addEventListener('mousedown', onClick);
    return () => document.removeEventListener('mousedown', onClick);
  }, [toolsOpen]);

  const toggleTool = (id) => {
    setSelectedTools(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
  };

  const toolsByCat = {};
  TOOLS.forEach(t => { (toolsByCat[t.category] = toolsByCat[t.category] || []).push(t); });
  const [machineId, setMachineId] = React.useState(initialMachineId);
  const [category, setCategory] = React.useState(() => {
    const m = MACHINES.find(x => x.id === initialMachineId);
    return m ? m.category : '';
  });
  const [machineStatus, setMachineStatus] = React.useState(() => {
    const m = MACHINES.find(x => x.id === initialMachineId);
    return m ? m.status : 'occupied';
  });
  // In edit mode pre-fill company / contact / phone / location straight from
  // the booking (matching the company record by name where possible).
  const editCustomerId = isEdit ? ((CUSTOMERS.find(c => c.name === editBooking.customer) || {}).id || '') : '';
  const [customerId, setCustomerId] = React.useState(isEdit ? editCustomerId : (blankCustomer ? '' : 'C-001'));
  const customerObj = CUSTOMERS.find(c => c.id === customerId) || null;
  const [contactName, setContactName] = React.useState(isEdit ? (editBooking.contact || '') : (blankCustomer ? '' : (customerObj ? customerObj.contacts[0].name : '')));
  const [phone, setPhone] = React.useState(isEdit ? (editBooking.phone || '') : (blankCustomer ? '' : (customerObj ? customerObj.contacts[0].phone : '')));
  const [location, setLocation] = React.useState(isEdit ? (editBooking.location || '') : (blankCustomer ? '' : (customerObj && customerObj.locations[0] ? customerObj.locations[0] : '')));
  const [operator, setOperator] = React.useState(() => {
    if (isEdit) return editBooking.operator || '';
    const m = MACHINES.find(x => x.id === initialMachineId);
    return (m && m.operator) || '';
  });

  // When user picks a different machine, auto-fill its default operator
  // (only if that machine has one assigned). Skip the very first run in edit
  // mode so we don't clobber the booking's saved operator on mount.
  const skipOperatorAutofill = React.useRef(isEdit);
  React.useEffect(() => {
    if (skipOperatorAutofill.current) { skipOperatorAutofill.current = false; return; }
    const m = MACHINES.find(x => x.id === machineId);
    if (m && m.operator) setOperator(m.operator);
  }, [machineId]);
  const [showErrors, setShowErrors] = React.useState(false);

  // Entry_Type — replaces the standalone Machine Status section.
  // When 'occupied' the full booking form shows; 'available' / 'unavailable'
  // means we're just flipping the machine status without capturing a booking.
  const [entryType, setEntryType] = React.useState(machineStatus === 'available' ? 'occupied' : machineStatus);
  const [entryOpen, setEntryOpen] = React.useState(false);
  const entryRef = React.useRef(null);
  React.useEffect(() => {
    if (!entryOpen) return;
    const onDoc = (e) => { if (entryRef.current && !entryRef.current.contains(e.target)) setEntryOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [entryOpen]);

  // Additional contact phone numbers — a contact can have several (office, mobile, whatsapp).
  // Seeded from the selected contact's main phone.
  const [extraContacts, setExtraContacts] = React.useState([]);
  React.useEffect(() => { setExtraContacts([]); }, [customerId, contactName]);
  const addExtraContact = () => setExtraContacts([...extraContacts, '']);
  const updateExtraContact = (i, val) => setExtraContacts(extraContacts.map((p, idx) => idx === i ? val : p));
  const removeExtraContact = (i) => setExtraContacts(extraContacts.filter((_, idx) => idx !== i));

  // Address URL (google maps link) + free-text address
  const [addressUrl, setAddressUrl] = React.useState(isEdit ? (editBooking.addressUrl || '') : '');
  const [address, setAddress] = React.useState(isEdit ? (editBooking.address || '') : '');

  // Price (RM) — keep diesel surcharge, add new overtime field
  const [price, setPrice] = React.useState(isEdit ? stripRM(editBooking.price) : '');
  const [diesel, setDiesel] = React.useState(isEdit ? stripRM(editBooking.diesel) : '');
  const [overtime, setOvertime] = React.useState(isEdit ? stripRM(editBooking.overtime) : '');
  const [remarks, setRemarks] = React.useState(isEdit ? (editBooking.remarks || '') : '');

  // Once Entry_Type isn't 'occupied' (Booked), most booking-detail fields hide.
  const isBookingMode = entryType === 'occupied';

  // Inline-add modal state
  const [addModal, setAddModal] = React.useState(null); // 'customer' | 'contact' | 'location' | null
  const [, forceRender] = React.useReducer(x => x + 1, 0);

  // When customer changes, reset contact / phone / location to first of that customer
  const pickCustomer = (name) => {
    const c = CUSTOMERS.find(x => x.name === name);
    setCustomerId(c ? c.id : '');
    if (c && c.contacts[0]) { setContactName(c.contacts[0].name); setPhone(c.contacts[0].phone); }
    else { setContactName(''); setPhone(''); }
    if (c && c.locations[0]) setLocation(c.locations[0]); else setLocation('');
  };

  // When contact changes (within the same customer), auto-fill phone
  const pickContact = (name) => {
    setContactName(name);
    const c = CUSTOMERS.find(x => x.id === customerId);
    const ct = c && c.contacts.find(x => x.name === name);
    if (ct) setPhone(ct.phone);
  };

  // Add handlers — these mutate the master data arrays and re-render
  const addCustomer = (data) => {
    const id = 'C-' + String(CUSTOMERS.length + 1).padStart(3, '0');
    const next = {
      id, name: data.name, jobs: 0,
      contacts: [{ name: data.contactName, phone: data.phone }],
      locations: data.location ? [data.location] : [],
    };
    CUSTOMERS.push(next);
    if (data.location && !LOCATIONS.includes(data.location)) LOCATIONS.push(data.location);
    setCustomerId(id);
    setContactName(data.contactName);
    setPhone(data.phone);
    setLocation(data.location || '');
    forceRender();
  };

  const addContact = (data) => {
    const c = CUSTOMERS.find(x => x.id === customerId);
    if (!c) return;
    c.contacts.push({ name: data.name, phone: data.phone });
    setContactName(data.name);
    setPhone(data.phone);
    forceRender();
  };

  const addLocation = (addr) => {
    if (!LOCATIONS.includes(addr)) LOCATIONS.push(addr);
    const c = CUSTOMERS.find(x => x.id === customerId);
    if (c && !c.locations.includes(addr)) c.locations.push(addr);
    setLocation(addr);
    forceRender();
  };

  // Bind the currently-typed phone to the selected contact in master data
  const savePhoneToContact = (newPhone) => {
    const c = CUSTOMERS.find(x => x.id === customerId);
    if (!c) return;
    const ct = c.contacts.find(x => x.name === contactName);
    if (ct) ct.phone = newPhone;
    setPhone(newPhone);
    forceRender();
  };

  const setShiftOption = (id) => {
    setShift(id);
    const opt = SHIFT_OPTIONS.find(s => s.id === id);
    if (opt && opt.start) { setStart(opt.start); setEnd(opt.end); }
  };

  // Multi-day bookings can only use Full-day (8am–5pm) or Custom shift.
  // If user expands the range while a half-day shift is selected, auto-switch to fullday.
  React.useEffect(() => {
    if (rangeDays > 1 && (shift === 'morning' || shift === 'afternoon')) {
      setShiftOption('fullday');
    }
  }, [rangeDays]);

  const machine = MACHINES.find(m => m.id === machineId) || MACHINES[0];

  // Rigger: only required for Mobile Crane >= 30t. Everything else defaults to empty.
  const tonNum = parseInt(machine.tonnage, 10);
  const riggerRequired = machine.category === 'Mobile Crane' && !isNaN(tonNum) && tonNum >= 30;
  const [rigger, setRigger] = React.useState(isEdit ? (editBooking.rigger || '—') : '—');
  const skipRiggerReset = React.useRef(isEdit);
  React.useEffect(() => {
    if (skipRiggerReset.current) { skipRiggerReset.current = false; return; }
    setRigger('—');
  }, [machineId, riggerRequired]);

  // Operator is OPTIONAL for these categories (machines often self-operated /
  // driven by the customer, or no dedicated operator on file).
  const OPERATOR_OPTIONAL_CATEGORIES = ['Scissor Lift', 'Lorry Crane', 'Lorry', 'Boom Lift', 'Forklift', 'Cross Rent', 'Utility', 'Car'];
  const operatorRequired = !OPERATOR_OPTIONAL_CATEGORIES.includes(category);

  // Required-field validation
  const errors = {
    customer: !customerObj,
    contact: !contactName.trim(),
    phone: !phone.trim(),
    location: !location.trim(),
    operator: operatorRequired && (!operator.trim() || operator === '—'),
    machine: !machineId,
    date: isBookingMode && !dateFrom,
  };
  const hasErrors = Object.values(errors).some(Boolean);

  // Build array of dates in the inclusive range [dateFrom, dateTo]
  const buildDateRange = (from, to) => {
    if (!from || !to || from > to) return from ? [from] : [];
    const out = [];
    const cur = new Date(from + 'T00:00:00');
    const end = new Date(to + 'T00:00:00');
    while (cur <= end) {
      const y = cur.getFullYear();
      const m = String(cur.getMonth() + 1).padStart(2, '0');
      const d = String(cur.getDate()).padStart(2, '0');
      out.push(`${y}-${m}-${d}`);
      cur.setDate(cur.getDate() + 1);
    }
    return out;
  };
  const rangeDates = buildDateRange(dateFrom, dateTo);
  const rangeDays = rangeDates.length;

  // Check overlap across every day in the range; report the first conflict found.
  // We check 3 kinds of overlap: machine (plate), operator, rigger.
  let conflict = null;
  let conflictDate = null;
  let conflictKind = null; // 'machine' | 'operator' | 'rigger'
  for (const d of rangeDates) {
    const excludeId = isEdit ? editBooking.id : undefined;
    // Machine
    let c = findBookingOverlap({ plate: machine.plate, date: d, start, end, excludeId });
    if (c) { conflict = c; conflictDate = d; conflictKind = 'machine'; break; }
    // Operator
    c = findOperatorOverlap({ operator, date: d, start, end, excludeId });
    if (c) { conflict = c; conflictDate = d; conflictKind = 'operator'; break; }
    // Rigger (only if assigned)
    if (riggerRequired && rigger && rigger !== '—') {
      c = findRiggerOverlap({ rigger, date: d, start, end, excludeId });
      if (c) { conflict = c; conflictDate = d; conflictKind = 'rigger'; break; }
    }
  }

  const handleSubmit = () => {
    if (hasErrors) { setShowErrors(true); return; }
    if (conflict) return;
    const fmtRM = (v) => { const s = String(v == null ? '' : v).trim(); return s === '' ? '' : (/^rm/i.test(s) ? s : 'RM ' + s); };
    // Edit mode — write the edited fields back onto the booking record in place
    // (BOOKINGS holds the same object reference) before handing control back.
    if (isEdit) {
      Object.assign(editBooking, {
        customer: customerObj ? customerObj.name : editBooking.customer,
        contact: contactName,
        phone,
        location,
        machine: machine.id + ' ' + machineLabel(machine),
        plate: machine.plate,
        category: machine.category,
        tonnage: machine.tonnage,
        date: dateFrom,
        shift, start, end,
        operator,
        rigger,
        price: fmtRM(price),
        diesel: fmtRM(diesel),
        overtime: fmtRM(overtime),
        addressUrl, address,
        remarks,
      });
      if (onSubmit) onSubmit(true);
      return;
    }
    // New booking (including a duplicated job moved to another date) — mint a
    // fresh work-order number and add it to the live BOOKINGS list so it shows
    // up as its own separate record.
    let newId = null;
    if (isBookingMode) {
      newId = genWorkOrderId();
      const newBooking = {
        id: newId,
        customer: customerObj ? customerObj.name : '',
        contact: contactName,
        phone,
        location,
        machine: machine.id + ' ' + machineLabel(machine),
        plate: machine.plate,
        category: machine.category,
        tonnage: machine.tonnage,
        date: dateFrom,
        shift, start, end,
        operator,
        rigger,
        desc: (duplicated && editBooking ? editBooking.desc : '') || '',
        price: fmtRM(price),
        diesel: fmtRM(diesel),
        overtime: fmtRM(overtime),
        addressUrl, address,
        remarks,
        status: 'Assigned',
        urgency: 'normal',
      };
      if (typeof BOOKINGS !== 'undefined') BOOKINGS.unshift(newBooking);
    }
    if (onSubmit) onSubmit(false, newId);
  };

  // Duplicate this booking into a new one. Keep the job details, but blank out
  // the schedule (user must pick a fresh date) and reset the operator to whoever
  // is assigned to this machine plate — then flip out of edit mode so Submit
  // creates a brand-new record with its own work-order number.
  const handleDuplicate = () => {
    setDuplicated(true);
    setDateFrom('');
    setDateTo('');
    setDayPreset('custom');
    const m = MACHINES.find(x => x.id === machineId);
    setOperator((m && m.operator) || '');
    setShowErrors(false);
  };

  const errStyle = (key) => (showErrors && errors[key]) ? { borderColor: '#dc2626', background: '#fef2f2' } : {};
  const errMsg = (key, msg) => (showErrors && errors[key]) ? (
    <div style={{fontSize: 11, color: '#dc2626', marginTop: 4, display: 'flex', alignItems: 'center', gap: 4}}>
      <AlertTriangle size={11}/> {msg}
    </div>
  ) : null;

  const drawerHead = drawer ? (
    <div className="wo-drawer__head">
      <button type="button" className="wo-drawer__close" onClick={onBack} aria-label="Close"><X size={22}/></button>
      <div className="wo-drawer__title">{isEdit ? 'Modify Booking' : 'New Booking'}</div>
      <div className="wo-drawer__head-actions">
        <button type="button" className="btn btn--ghost" onClick={onBack}>Cancel</button>
        <button type="button" className="btn btn--primary"
          disabled={!!conflict}
          style={conflict ? {opacity: 0.5, cursor: 'not-allowed'} : {}}
          onClick={handleSubmit}>{isEdit ? 'Save Changes' : 'Save'}</button>
      </div>
    </div>
  ) : null;

  return (
    <div className={drawer ? "wo-drawer__inner" : "content"}>
      {drawerHead}
      {!drawer && onBack ? (
        <button type="button" className="btn btn--ghost"
          onClick={onBack}
          style={{display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px', marginBottom: 16, alignSelf: 'flex-start'}}>
          <ChevLeft size={16}/> Back
        </button>
      ) : null}
      <div className={drawer ? "wo-drawer__body" : "card"}>
        <div className="card__header">
          <div>
            {!drawer ? <div className="card__title">{isEdit ? 'Modify Booking' : 'New Booking'}</div> : null}
            <div style={{fontSize: 13, color: 'var(--color-grey-500)', marginTop: 4}}>
              <Lock size={11} style={{verticalAlign: 'middle'}}/> {isEdit
                ? <>Editing <code style={{fontSize: 11, background: 'var(--color-grey-100)', padding: '1px 5px', borderRadius: 3}}>{editBooking.id}</code> · changes notify the operator via push</>
                : duplicated
                  ? <>Duplicated from <code style={{fontSize: 11, background: 'var(--color-grey-100)', padding: '1px 5px', borderRadius: 3}}>{editBooking.id}</code> · new work order <code style={{fontSize: 11, background: 'rgba(255,213,0,0.18)', padding: '1px 5px', borderRadius: 3}}>{pendingWO}</code> assigned on submit · pick a new date</>
                  : <>Booking creation is restricted to Admin · Customer code maps to AutoCount Debtor</>}
            </div>
          </div>
          <div style={{display: 'flex', alignItems: 'center', gap: 10}}>
            {isEdit ? (
              <button type="button" className="btn btn--ghost"
                onClick={handleDuplicate}
                style={{display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 12px'}}>
                <Copy size={14}/> Duplicate
              </button>
            ) : null}
            <div className="badge badge--yellow badge--no-dot" style={{padding: '6px 12px'}}>{isEdit ? 'Editing booking' : duplicated ? 'Duplicate · new date' : 'Draft · auto-saved'}</div>
          </div>
        </div>

        <div style={{display: 'flex', flexDirection: 'column', gap: 32}}>

          {/* Entry_Type dropdown — picks the machine status. When set to anything
              other than "Booked" the rest of the booking form is hidden. */}
          <section>
            <div className="field">
              <label>Entry_Type</label>
              <div ref={entryRef} style={{position: 'relative'}}>
                <button type="button"
                  onClick={() => setEntryOpen(!entryOpen)}
                  className="input"
                  style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer', minHeight: 42, padding: '6px 12px', width: '100%', textAlign: 'left'}}>
                  <span className={"status status--" + (entryType === 'available' ? 'green' : entryType === 'occupied' ? 'red' : 'gray')}>
                    {entryType === 'available' ? 'Available' : entryType === 'occupied' ? 'Booking' : 'Not available'}
                  </span>
                  <ChevDown size={16} style={{color: 'var(--color-grey-500)', transition: 'transform 0.15s', transform: entryOpen ? 'rotate(180deg)' : 'none'}}/>
                </button>
                {entryOpen ? (
                  <div style={{
                    position: 'absolute', top: 'calc(100% + 4px)', left: 0, right: 0,
                    background: 'white', border: '1px solid var(--color-grey-300)',
                    borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
                    zIndex: 50, padding: 6,
                  }}>
                    {[
                      { id: 'occupied',    label: 'Booking',       pill: 'red' },
                      { id: 'available',   label: 'Available',     pill: 'green' },
                      { id: 'unavailable', label: 'Not available', pill: 'gray' },
                    ].map(opt => (
                      <button key={opt.id} type="button"
                        onClick={() => { setEntryType(opt.id); setEntryOpen(false); }}
                        style={{display: 'flex', alignItems: 'center', gap: 8, width: '100%', padding: '8px 10px', borderRadius: 6, fontSize: 13, cursor: 'pointer', background: entryType === opt.id ? 'rgba(255,213,0,0.10)' : 'transparent', textAlign: 'left'}}>
                        <span className={"status status--" + opt.pill}>{opt.label}</span>
                      </button>
                    ))}
                  </div>
                ) : null}
              </div>
            </div>
          </section>

          {/* Machinery — always visible. Status applies to the picked machine. */}
          <section>
            <div className="eyebrow" style={{marginBottom: 12}}>Machinery</div>
            <div className="form-grid">
              <div className="field full">
                <label>Category *</label>
                <select value={category} onChange={e => {
                  // Set the category and CLEAR the machine — the user picks the
                  // specific machine themselves from the filtered list below.
                  setCategory(e.target.value);
                  setMachineId('');
                }}>
                  <option value="">— Select category —</option>
                  {CATEGORIES.map(c => <option key={c.id} value={c.name}>{c.name}</option>)}
                </select>
              </div>
              <div className="field full">
                <label>Machine / Plate *</label>
                <MachineSelect
                  value={machineId}
                  onChange={setMachineId}
                  machines={category ? MACHINES.filter(m => m.category === category) : MACHINES}
                  disabled={!category}
                  error={showErrors && errors.machine}/>
                <div className="field__hint">
                  {category
                    ? <>Status pill shows live availability — <span style={{color: '#16a34a', fontWeight: 600}}>Available</span> · <span style={{color: '#dc2626', fontWeight: 600}}>Occupied</span> · <span style={{color: '#6b7280', fontWeight: 600}}>Not available</span> (workshop/leave).</>
                    : 'Pick a category first, then choose a machine from that list.'}
                </div>
              </div>
              <div className="field">
                <label>Operator Assigned {operatorRequired
                  ? <>* <small style={{color: '#dc2626'}}>(required)</small></>
                  : <small style={{fontSize: 11, color: 'var(--color-grey-500)'}}>(optional for {category})</small>}</label>
                <OperatorSelect value={operator} onChange={setOperator} error={showErrors && errors.operator}/>
                {errMsg('operator', 'Operator must be assigned')}
              </div>
              <div className="field">
                <label>Rigger Assigned {riggerRequired ? '*' : <span style={{fontSize: 11, color: 'var(--color-grey-500)'}}>(not required)</span>}</label>
                <select value={rigger} onChange={e => setRigger(e.target.value)} disabled={!riggerRequired}>
                  <option>—</option>
                  {RIGGERS.map(r => <option key={r.id}>{r.name}{r.status === 'On Job' ? ' (on job)' : ''}</option>)}
                </select>
                <div className="field__hint">
                  {riggerRequired
                    ? `Rigger required for Mobile Crane ${machine.tonnage}.`
                    : `Rigger not assigned — ${machine.category} ${machine.tonnage} is operated solo.`}
                </div>
              </div>
            </div>
          </section>

          {isBookingMode ? (
          <>
          <section>
            <div className="eyebrow" style={{marginBottom: 12}}>Schedule</div>

            {/* Day quick-pick row */}
            <div className="field" style={{marginBottom: 16}}>
              <label>Day <span style={{color: '#dc2626'}}>*</span></label>
              <div style={{display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8}}>
                {[
                  { id: 'today',    label: 'Today' },
                  { id: 'tomorrow', label: 'Tomorrow' },
                  { id: 'dayafter', label: 'Day After' },
                  { id: 'custom',   label: 'Custom' },
                ].map(opt => (
                  <button key={opt.id} type="button"
                    onClick={() => applyDayPreset(opt.id)}
                    className={"day-chip " + (dayPreset === opt.id ? "day-chip--active" : "")}
                    style={{
                      padding: '12px 16px',
                      borderRadius: 8,
                      fontWeight: 600,
                      fontSize: 14,
                      border: '1px solid var(--color-grey-300)',
                      background: dayPreset === opt.id ? 'var(--color-yellow)' : 'var(--color-grey-50)',
                      color: dayPreset === opt.id ? 'var(--color-navy)' : 'var(--color-grey-700)',
                      cursor: 'pointer',
                    }}>
                    {opt.label}
                  </button>
                ))}
              </div>
            </div>

            {/* Shift quick-pick row */}
            <div className="field" style={{marginBottom: 16}}>
              <label>Shift <span style={{color: '#dc2626'}}>*</span></label>
              <div style={{display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8}}>
                {[
                  { id: 'fullday',   label: 'All Day' },
                  { id: 'morning',   label: '8 AM - 12 PM' },
                  { id: 'afternoon', label: '1 PM - 5 PM' },
                  { id: 'custom',    label: 'Custom' },
                ].map(opt => {
                  const isHalf = opt.id === 'morning' || opt.id === 'afternoon';
                  const locked = rangeDays > 1 && isHalf;
                  return (
                    <button key={opt.id} type="button"
                      disabled={locked}
                      onClick={() => !locked && setShiftOption(opt.id)}
                      className={"day-chip " + (shift === opt.id ? "day-chip--active" : "")}
                      style={{
                        padding: '12px 16px',
                        borderRadius: 8,
                        fontWeight: 600,
                        fontSize: 14,
                        border: '1px solid var(--color-grey-300)',
                        background: shift === opt.id ? 'var(--color-yellow)' : 'var(--color-grey-50)',
                        color: shift === opt.id ? 'var(--color-navy)' : 'var(--color-grey-700)',
                        cursor: locked ? 'not-allowed' : 'pointer',
                        opacity: locked ? 0.4 : 1,
                      }}
                      title={locked ? 'Half-day shifts only for single-day bookings' : undefined}>
                      {opt.label}
                    </button>
                  );
                })}
              </div>
              {rangeDays > 1 ? (
                <div className="field__hint" style={{marginTop: 6}}>Multi-day booking — only All Day or Custom allowed.</div>
              ) : null}
            </div>

            {/* Shift_Start datetime */}
            <div className="field" style={{marginBottom: 16}}>
              <label>Shift_Start <span style={{color: '#dc2626'}}>*</span></label>
              <input type="datetime-local"
                value={dateFrom + 'T' + start}
                disabled={dayPreset !== 'custom' && shift !== 'custom'}
                onChange={e => {
                  const [d, t] = e.target.value.split('T');
                  if (d) { setDateFrom(d); if (dateTo < d) setDateTo(d); setDayPreset('custom'); }
                  if (t) { setStart(t); setShift('custom'); }
                }}
                style={{width: '100%'}}/>
            </div>

            {/* Shift_End datetime */}
            <div className="field">
              <label>Shift_End <span style={{color: '#dc2626'}}>*</span></label>
              <input type="datetime-local"
                value={dateTo + 'T' + end}
                disabled={dayPreset !== 'custom' && shift !== 'custom'}
                min={dateFrom + 'T' + start}
                onChange={e => {
                  const [d, t] = e.target.value.split('T');
                  if (d) { setDateTo(d); setDayPreset('custom'); }
                  if (t) { setEnd(t); setShift('custom'); }
                }}
                style={{width: '100%'}}/>
              <div className="field__hint" style={{marginTop: 6}}>
                {!dateFrom
                  ? 'Pick a date for this booking.'
                  : rangeDays > 1
                    ? `${rangeDays}-day range · ${dateFrom} ${start} → ${dateTo} ${end}`
                    : `Single day · ${start} – ${end}`}
              </div>
              {errMsg('date', 'A booking date is required')}
            </div>
          </section>

          <section>
            <div className="eyebrow" style={{marginBottom: 12}}>Company &amp; Location</div>
            <div className="form-grid">
              <div className="field full">
                <label>Company * <small style={{color: '#dc2626'}}>(required)</small></label>
                <SearchableSelect
                  value={customerObj ? customerObj.name : ''}
                  onChange={pickCustomer}
                  options={CUSTOMERS.map(c => c.name)}
                  placeholder="— Select company —"
                  emptyText="No company matches. Add new?"
                  onAddNew={mgmtLocked ? undefined : () => setAddModal('customer')}
                  addLabel="Add new company"
                  error={showErrors && errors.customer}
                  disabled={mgmtLocked}
                />
                {mgmtLocked ? <MgmtLockHint/> : null}
                {errMsg('customer', 'Company name is required')}
              </div>
              <div className="field full">
                <label>Location * <small style={{color: '#dc2626'}}>(required)</small></label>
                <SearchableSelect
                  value={location}
                  onChange={setLocation}
                  options={
                    customerObj
                      ? [...customerObj.locations, ...LOCATIONS.filter(l => !customerObj.locations.includes(l))]
                      : LOCATIONS
                  }
                  placeholder="— Select location —"
                  emptyText="No location matches. Add new?"
                  onAddNew={() => setAddModal('location')}
                  addLabel="Add new location"
                  error={showErrors && errors.location}
                />
                {errMsg('location', 'Job location is required')}
              </div>
              <div className="field full">
                <label>Contact_Person * <small style={{color: '#dc2626'}}>(required)</small></label>
                <SearchableSelect
                  value={contactName}
                  onChange={pickContact}
                  options={customerObj ? customerObj.contacts.map(c => c.name) : []}
                  placeholder={customerObj ? '— Select contact —' : 'Pick company first'}
                  emptyText="No contacts. Add new?"
                  onAddNew={() => setAddModal('contact')}
                  addLabel="Add new contact for this company"
                  error={showErrors && errors.contact}
                  disabled={!customerObj}
                />
                {errMsg('contact', 'Contact person is required')}
              </div>
              <div className="field full">
                <label>Contact <small style={{color: '#dc2626'}}>(required, auto-filled, +add more)</small></label>
                <div style={{display: 'flex', flexDirection: 'column', gap: 6}}>
                  {/* Primary phone (bound to contact) */}
                  <div style={{display: 'flex', gap: 6}}>
                    <input value={phone} onChange={e => setPhone(e.target.value)} style={{...errStyle('phone'), flex: 1}} placeholder="+60 ..."/>
                    <button type="button" className="btn btn--ghost"
                      onClick={() => setAddModal('phone')}
                      disabled={!customerObj || !contactName}
                      title="Save this phone to the contact"
                      style={{whiteSpace: 'nowrap', padding: '0 12px'}}>
                      <Plus size={14}/> Save
                    </button>
                  </div>
                  {/* Additional phone numbers — added inline */}
                  {extraContacts.map((p, i) => (
                    <div key={i} style={{display: 'flex', gap: 6}}>
                      <input value={p} onChange={e => updateExtraContact(i, e.target.value)} placeholder="+60 ..." style={{flex: 1}}/>
                      <button type="button" className="btn btn--ghost" style={{padding: '0 12px', color: '#dc2626'}} onClick={() => removeExtraContact(i)} title="Remove">
                        <X size={14}/>
                      </button>
                    </div>
                  ))}
                  <button type="button" onClick={addExtraContact}
                    style={{display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6, padding: '8px 12px', border: '1px dashed var(--color-grey-300)', borderRadius: 8, background: 'var(--color-grey-50)', color: 'var(--color-navy)', fontWeight: 600, fontSize: 13, cursor: 'pointer'}}>
                    <Plus size={14}/> Add another contact number
                  </button>
                </div>
                {errMsg('phone', 'At least one contact number is required')}
              </div>
              <div className="field full">
                <label>Address_URL</label>
                <input value={addressUrl} onChange={e => setAddressUrl(e.target.value)} placeholder="http://" type="url"/>
                <div className="field__hint">Google Maps share link — pasted here so operator can tap → open directions.</div>
              </div>
              <div className="field full">
                <label>Address</label>
                <textarea value={address} onChange={e => setAddress(e.target.value)} placeholder="Written street address / site notes" style={{minHeight: 60}}/>
              </div>
            </div>
          </section>

          <section>
            <div className="eyebrow" style={{marginBottom: 12}}>Pricing &amp; Job Detail</div>
            {mgmtLocked ? (
              <div style={{display: 'flex', alignItems: 'center', gap: 8, padding: '10px 12px', marginBottom: 16, background: 'var(--color-grey-50)', border: '1px solid var(--color-grey-200)', borderRadius: 8, fontSize: 12.5, color: 'var(--color-grey-700)'}}>
                <Lock size={13} style={{flexShrink: 0, color: 'var(--color-grey-500)'}}/>
                <span>Company &amp; pricing are locked. Only <strong>Management</strong> can change the company, price, diesel surcharge or overtime once a booking exists.</span>
              </div>
            ) : null}
            <div className="form-grid">
              <div className="field">
                <label>Price (RM) *</label>
                <input value={price} onChange={e => setPrice(e.target.value)} placeholder="0.00" disabled={mgmtLocked} style={mgmtLocked ? lockedInputStyle : undefined}/>
                <div className="field__hint">Maps to AutoCount → Amount</div>
              </div>
              <div className="field">
                <label>Diesel Surcharge (RM)</label>
                <input value={diesel} onChange={e => setDiesel(e.target.value)} placeholder="0.00" disabled={mgmtLocked} style={mgmtLocked ? lockedInputStyle : undefined}/>
                <div className="field__hint">Maps to AutoCount → Additional Charge</div>
              </div>
              <div className="field full">
                <label>Overtime (RM)</label>
                <input value={overtime} onChange={e => setOvertime(e.target.value)} placeholder="0.00" disabled={mgmtLocked} style={mgmtLocked ? lockedInputStyle : undefined}/>
                <div className="field__hint">Extra hours / late completion charge (added on top of Price).</div>
              </div>
              <div className="field full">
                <label>Tools</label>
                <div ref={toolsRef} style={{position: 'relative'}}>
                  <button type="button" className="input" onClick={() => setToolsOpen(!toolsOpen)}
                    style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', cursor: 'pointer', minHeight: 42, height: 'auto', padding: '6px 12px', width: '100%', textAlign: 'left'}}>
                    <span style={{display: 'flex', flexWrap: 'wrap', gap: 4, flex: 1}}>
                      {selectedTools.length === 0 ? (
                        <span style={{color: 'var(--color-grey-500)', fontSize: 13}}>— Select tools / equipment —</span>
                      ) : (
                        selectedTools.map(id => {
                          const t = TOOLS.find(x => x.id === id);
                          if (!t) return null;
                          return (
                            <span key={id} style={{background: 'var(--color-navy)', color: 'var(--color-yellow)', fontSize: 11, padding: '2px 8px', borderRadius: 999, fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 4}}>
                              {t.name}
                              <button type="button" onClick={(e) => { e.stopPropagation(); toggleTool(id); }} style={{padding: 0, color: 'inherit', display: 'flex', alignItems: 'center'}}>
                                <X size={11}/>
                              </button>
                            </span>
                          );
                        })
                      )}
                    </span>
                    <ChevDown size={16} style={{color: 'var(--color-grey-500)', transition: 'transform 0.15s', transform: toolsOpen ? 'rotate(180deg)' : 'none'}}/>
                  </button>
                  {toolsOpen ? (
                    <div style={{
                      position: 'absolute', top: 'calc(100% + 4px)', left: 0, right: 0,
                      background: 'white', border: '1px solid var(--color-grey-300)',
                      borderRadius: 8, boxShadow: '0 8px 24px rgba(0,0,0,0.12)',
                      maxHeight: 360, overflowY: 'auto', zIndex: 50, padding: 8,
                    }}>
                      {Object.entries(toolsByCat).map(([cat, items]) => (
                        <div key={cat} style={{marginBottom: 6}}>
                          <div style={{fontSize: 10, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--color-grey-500)', padding: '6px 8px 4px'}}>{cat}</div>
                          {items.map(t => (
                            <label key={t.id} style={{
                              display: 'flex', alignItems: 'center', gap: 8, padding: '6px 8px',
                              cursor: 'pointer', borderRadius: 4, fontSize: 13,
                              background: selectedTools.includes(t.id) ? 'rgba(255,213,0,0.10)' : 'transparent',
                            }} onMouseDown={(e) => e.preventDefault()}>
                              <input type="checkbox" checked={selectedTools.includes(t.id)} onChange={() => toggleTool(t.id)}/>
                              <span style={{flex: 1}}>{t.name}</span>
                              {t.default ? <span className="status status--green" style={{padding: '1px 6px', fontSize: 9}}>DEFAULT</span> : null}
                            </label>
                          ))}
                        </div>
                      ))}
                      <div style={{borderTop: '1px solid var(--color-grey-200)', padding: '8px 8px 4px', marginTop: 4, display: 'flex', justifyContent: 'space-between', fontSize: 11, color: 'var(--color-grey-500)'}}>
                        <span>{selectedTools.length} of {TOOLS.length} selected</span>
                        <button type="button" onClick={() => setSelectedTools([])} style={{color: 'var(--color-navy)', fontWeight: 600, fontSize: 11}}>Clear all</button>
                      </div>
                    </div>
                  ) : null}
                </div>
                <div className="field__hint">
                  Managed by Admin under <strong>Administration → Tools &amp; Equipment</strong>. Default-tagged tools are pre-checked on every new booking.
                </div>
              </div>
              <div className="field full">
                <label>Remarks</label>
                <textarea value={remarks} onChange={e => setRemarks(e.target.value)} placeholder="Special instructions (e.g. soft ground — bring outrigger pads)" style={{minHeight: 60}}/>
              </div>
            </div>
          </section>
          </>
          ) : null}

          {showErrors && hasErrors ? (
            <div style={{padding: 14, background: '#fef2f2', border: '1px solid #fecaca', borderLeft: '4px solid #dc2626', borderRadius: 8, color: '#991b1b', fontSize: 13, lineHeight: 1.55}}>
              <div style={{display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4}}>
                <AlertTriangle size={16}/>
                <strong>Missing required fields</strong>
              </div>
              Fill in the highlighted fields before submitting:
              <ul style={{margin: '6px 0 0 18px', padding: 0}}>
                {errors.customer ? <li>Company name</li> : null}
                {errors.contact ? <li>Contact person</li> : null}
                {errors.phone ? <li>Contact phone number</li> : null}
                {errors.location ? <li>Job location</li> : null}
                {errors.operator ? <li>Operator assigned</li> : null}
                {errors.machine ? <li>Machine</li> : null}
              </ul>
            </div>
          ) : null}

          {conflict ? (
            <div style={{padding: 14, background: '#fef2f2', border: '1px solid #fecaca', borderLeft: '4px solid #dc2626', borderRadius: 8, color: '#991b1b', fontSize: 13, lineHeight: 1.55}}>
              <div style={{display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4}}>
                <AlertTriangle size={16}/>
                <strong>
                  {conflictKind === 'machine'  ? 'Time conflict — this machine is already booked.'
                  : conflictKind === 'operator' ? 'Time conflict — this operator is already assigned.'
                  : conflictKind === 'rigger'   ? 'Time conflict — this rigger is already assigned.'
                  : 'Time conflict.'}
                </strong>
              </div>
              <div>
                {conflictKind === 'machine' ? (
                  <>Plate <strong>{machine.plate}</strong> is booked on <strong>{conflictDate}</strong> from <strong>{conflict.start}–{conflict.end}</strong> ({conflict.id} · {conflict.customer}).</>
                ) : conflictKind === 'operator' ? (
                  <>Operator <strong>{operator}</strong> is already on <strong>{conflict.id}</strong> on <strong>{conflictDate}</strong> from <strong>{conflict.start}–{conflict.end}</strong> ({conflict.customer} · plate {conflict.plate}).</>
                ) : (
                  <>Rigger <strong>{rigger}</strong> is already on <strong>{conflict.id}</strong> on <strong>{conflictDate}</strong> from <strong>{conflict.start}–{conflict.end}</strong> ({conflict.customer} · plate {conflict.plate}).</>
                )}
                {' '}Your requested time <strong>{start}–{end}</strong> overlaps{rangeDays > 1 ? ' on at least one day in the range' : ''}.
              </div>
              <div style={{marginTop: 6, fontSize: 12, color: '#7f1d1d'}}>
                Resolve by: changing the time, {conflictKind === 'machine' ? 'picking another machine' : conflictKind === 'operator' ? 'picking another operator' : 'picking another rigger'}, or using the <strong>Switch</strong> button on the existing booking to swap resources.
              </div>
            </div>
          ) : null}

          <div style={{display: 'flex', gap: 12, justifyContent: 'flex-end', borderTop: '1px solid var(--border-default)', paddingTop: 24, alignItems: 'center'}}>
            <span style={{fontSize: 11, color: 'var(--color-grey-500)', marginRight: 'auto'}}>
              <Lock size={11} style={{verticalAlign: 'middle'}}/> {isEdit ? 'Only Management can delete this booking.' : 'Once submitted, only Management can delete this booking.'}
            </span>
            {isEdit ? null : <button className="btn btn--ghost">Save as Draft</button>}
            <button className="btn btn--ghost" onClick={onBack}>Cancel</button>
            <button className="btn btn--primary btn--lg"
              disabled={!!conflict}
              style={conflict ? {opacity: 0.5, cursor: 'not-allowed'} : {}}
              onClick={handleSubmit}>
              {isEdit ? <><Check size={16}/> Save Changes</> : <><Send size={16}/> Submit Booking</>}
            </button>
          </div>
        </div>
      </div>
      {/* Inline add modals — customer / contact / location */}
      {addModal === 'customer' ? (
        <AddCustomerModal onClose={() => setAddModal(null)} onAdd={(d) => { addCustomer(d); setAddModal(null); }}/>
      ) : null}
      {addModal === 'contact' && customerObj ? (
        <AddContactModal customerName={customerObj.name} onClose={() => setAddModal(null)} onAdd={(d) => { addContact(d); setAddModal(null); }}/>
      ) : null}
      {addModal === 'location' ? (
        <AddLocationModal customerName={customerObj ? customerObj.name : null} onClose={() => setAddModal(null)} onAdd={(addr) => { addLocation(addr); setAddModal(null); }}/>
      ) : null}
      {addModal === 'phone' && customerObj && contactName ? (
        <AddPhoneModal contactName={contactName} currentPhone={phone} onClose={() => setAddModal(null)} onAdd={(newPhone) => { savePhoneToContact(newPhone); setAddModal(null); }}/>
      ) : null}
    </div>
  );
}

// === Inline-add modals =============================================
function AddCustomerModal({ onClose, onAdd }) {
  const [name, setName] = React.useState('');
  const [contactName, setContactName] = React.useState('');
  const [phone, setPhone] = React.useState('');
  const [loc, setLoc] = React.useState('');
  const valid = name.trim() && contactName.trim() && phone.trim();
  return (
    <div className="install-modal" onClick={onClose}>
      <div className="install-modal__card" onClick={(e) => e.stopPropagation()} style={{maxWidth: 460}}>
        <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14}}>
          <div style={{fontWeight: 700, fontSize: 17, color: 'var(--color-navy)'}}>Add Customer</div>
          <button type="button" className="install-banner__close" onClick={onClose}><X size={18}/></button>
        </div>
        <div style={{display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 14}}>
          <div className="field"><label>Company name *</label><input className="input" value={name} onChange={e => setName(e.target.value)} placeholder="e.g. Pioneer Steel Sdn Bhd" autoFocus/></div>
          <div className="field"><label>Primary contact name *</label><input className="input" value={contactName} onChange={e => setContactName(e.target.value)} placeholder="Person you'll call"/></div>
          <div className="field"><label>Phone *</label><input className="input" value={phone} onChange={e => setPhone(e.target.value)} placeholder="+60 ..."/></div>
          <div className="field"><label>Primary location (optional)</label><input className="input" value={loc} onChange={e => setLoc(e.target.value)} placeholder="Site address or plot"/></div>
        </div>
        <div style={{display: 'flex', gap: 8, justifyContent: 'flex-end'}}>
          <button className="btn btn--ghost" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary" disabled={!valid} onClick={() => onAdd({name: name.trim(), contactName: contactName.trim(), phone: phone.trim(), location: loc.trim()})}>
            <Plus size={14}/> Create &amp; Select
          </button>
        </div>
      </div>
    </div>
  );
}

function AddContactModal({ customerName, onClose, onAdd }) {
  const [name, setName] = React.useState('');
  const [phone, setPhone] = React.useState('');
  const valid = name.trim() && phone.trim();
  return (
    <div className="install-modal" onClick={onClose}>
      <div className="install-modal__card" onClick={(e) => e.stopPropagation()} style={{maxWidth: 420}}>
        <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14}}>
          <div>
            <div style={{fontWeight: 700, fontSize: 17, color: 'var(--color-navy)'}}>Add Contact</div>
            <div style={{fontSize: 12, color: 'var(--color-grey-500)', marginTop: 2}}>For <strong>{customerName}</strong></div>
          </div>
          <button type="button" className="install-banner__close" onClick={onClose}><X size={18}/></button>
        </div>
        <div style={{display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 14}}>
          <div className="field"><label>Contact name *</label><input className="input" value={name} onChange={e => setName(e.target.value)} autoFocus/></div>
          <div className="field"><label>Phone *</label><input className="input" value={phone} onChange={e => setPhone(e.target.value)} placeholder="+60 ..."/></div>
        </div>
        <div style={{display: 'flex', gap: 8, justifyContent: 'flex-end'}}>
          <button className="btn btn--ghost" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary" disabled={!valid} onClick={() => onAdd({name: name.trim(), phone: phone.trim()})}>
            <Plus size={14}/> Add &amp; Select
          </button>
        </div>
      </div>
    </div>
  );
}

function AddPhoneModal({ contactName, currentPhone, onClose, onAdd }) {
  const [phone, setPhone] = React.useState(currentPhone || '');
  return (
    <div className="install-modal" onClick={onClose}>
      <div className="install-modal__card" onClick={(e) => e.stopPropagation()} style={{maxWidth: 420}}>
        <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14}}>
          <div>
            <div style={{fontWeight: 700, fontSize: 17, color: 'var(--color-navy)'}}>Save Phone Number</div>
            <div style={{fontSize: 12, color: 'var(--color-grey-500)', marginTop: 2}}>Bind to <strong>{contactName}</strong> — auto-filled on future bookings.</div>
          </div>
          <button type="button" className="install-banner__close" onClick={onClose}><X size={18}/></button>
        </div>
        <div className="field" style={{marginBottom: 14}}>
          <label>Phone *</label>
          <input className="input" value={phone} onChange={e => setPhone(e.target.value)} placeholder="+60 ..." autoFocus/>
        </div>
        <div style={{display: 'flex', gap: 8, justifyContent: 'flex-end'}}>
          <button className="btn btn--ghost" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary" disabled={!phone.trim()} onClick={() => onAdd(phone.trim())}>
            <Plus size={14}/> Save Phone
          </button>
        </div>
      </div>
    </div>
  );
}

function AddLocationModal({ customerName, onClose, onAdd }) {
  const [addr, setAddr] = React.useState('');
  return (
    <div className="install-modal" onClick={onClose}>
      <div className="install-modal__card" onClick={(e) => e.stopPropagation()} style={{maxWidth: 460}}>
        <div style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14}}>
          <div>
            <div style={{fontWeight: 700, fontSize: 17, color: 'var(--color-navy)'}}>Add Job Location</div>
            <div style={{fontSize: 12, color: 'var(--color-grey-500)', marginTop: 2}}>
              {customerName ? <>Will be linked to <strong>{customerName}</strong> for future bookings.</> : 'Added to global location pool.'}
            </div>
          </div>
          <button type="button" className="install-banner__close" onClick={onClose}><X size={18}/></button>
        </div>
        <div className="field" style={{marginBottom: 14}}>
          <label>Site address / plot *</label>
          <input className="input" value={addr} onChange={e => setAddr(e.target.value)} placeholder="e.g. Kulai PLO 12, Lot 88" autoFocus/>
        </div>
        <div style={{display: 'flex', gap: 8, justifyContent: 'flex-end'}}>
          <button className="btn btn--ghost" onClick={onClose}>Cancel</button>
          <button className="btn btn--primary" disabled={!addr.trim()} onClick={() => onAdd(addr.trim())}>
            <Plus size={14}/> Add &amp; Select
          </button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { CreateWO });
