/* global React, TRIPS, AIRPORTS, FLEET, Icon, fmtMoney, StatusPill, StatusDot, TabBar, PhoneShell, QuoteEditSheet, LegendSheet, CompetitorSheet, AttachmentsSheet, MapGraphic, attachmentsFor, acYear */
const { useState: useStateC, useEffect, useRef } = React;

function BajopsMark() {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 7 }}>
      <span style={{ font: '800 21px/1 var(--font-ui)', letterSpacing: '-0.01em', color: '#fff' }}>
        BAJ<span style={{ color: 'var(--baj-blue-soft)' }}>ops</span>
      </span>
      <span style={{ font: '600 9px/1 var(--font-ui)', letterSpacing: '0.18em', color: 'rgba(255,255,255,0.5)', textTransform: 'uppercase' }}>by ONEflight</span>
    </span>
  );
}

/* ---------- quote row inside a trip group ---------- */
function QuoteRow({ trip, q, onCompetitor, onEdit, isLead }) {
  const total = trip.ladder ? trip.ladder.trip.length : null;
  return (
    <div onClick={() => onEdit && onEdit(q)} style={{ display: 'flex', alignItems: 'center', gap: 11, padding: '11px 2px', cursor: onEdit ? 'pointer' : 'default' }}>
      <div style={{ width: 36, height: 36, borderRadius: 9, background: 'var(--baj-surface-2)', color: 'var(--baj-navy)', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: '0 0 auto' }}>
        <Icon.takeoff width={16} height={16} />
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 7 }}>
          <span style={{ font: '700 13.5px/1.15 var(--font-ui)', color: 'var(--baj-text)' }}>{q.tail}</span>
          {isLead && total != null && trip.yourRank != null && (
            <button onClick={() => onCompetitor(trip)} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, border: 0, background: trip.yourRank === 1 ? 'rgba(46,139,87,0.12)' : 'var(--baj-navy)', color: trip.yourRank === 1 ? '#2E8B57' : '#fff', borderRadius: 6, padding: '3px 7px', cursor: 'pointer', font: '800 10px/1 var(--font-ui)' }}>
              {trip.yourRank === 1 ? <Icon.trophy width={10} height={10} /> : `#${trip.yourRank}`}<span style={{ fontWeight: 600, opacity: 0.8 }}>{trip.yourRank === 1 ? 'lead' : `of ${total}`}</span>
            </button>
          )}
        </div>
        <div style={{ font: '500 11px/1.3 var(--font-ui)', color: 'var(--baj-text-mute)', marginTop: 3, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{acYear(q.ac)} {q.ac} · {q.type === 'Leg' ? `Leg ${q.legNum}` : 'Trip'}</div>
      </div>
      <div style={{ textAlign: 'right', flex: '0 0 auto' }}>
        <div style={{ font: '800 14px/1 var(--font-ui)', color: 'var(--baj-text)' }}>{fmtMoney(q.amount)}</div>
        <div style={{ marginTop: 5 }}><StatusPill status={q.status} size="sm" /></div>
      </div>
    </div>
  );
}

/* ---------- trip group card ---------- */
function TripGroup({ trip, openId, setOpenId, onCompetitor, onAddQuote, onAttachments, onEdit }) {
  const open = openId === trip.id;
  const pending = trip.bucket === 'pending';
  const total = trip.ladder ? trip.ladder.trip.length : null;
  const leadingYouId = trip.quotes
    .filter(q => q.you)
    .sort((a, b) => a.amount - b.amount)[0]?.id ?? null;

  return (
    <div style={{ background: '#fff', borderRadius: 16, border: trip.declined ? '1.5px solid rgba(192,57,43,0.45)' : '1px solid var(--baj-line)', boxShadow: trip.declined ? '0 1px 4px rgba(192,57,43,0.1)' : '0 1px 2px rgba(15,27,45,0.05)' }}>
      <button onClick={() => setOpenId(open ? null : trip.id)} style={{
        width: '100%', display: 'flex', alignItems: 'center', gap: 12, padding: '14px 15px', cursor: 'pointer',
        background: open ? 'var(--baj-navy)' : '#fff', border: 0, textAlign: 'left', transition: 'background 200ms',
        borderRadius: open ? '16px 16px 0 0' : 16,
      }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
            <span style={{ font: '800 17px/1.05 var(--font-ui)', color: open ? '#fff' : 'var(--baj-text)', whiteSpace: 'nowrap' }}>{trip.from} → {trip.to}</span>
            {trip.declined
              ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, font: '700 10px/1 var(--font-ui)', letterSpacing: '0.04em', textTransform: 'uppercase', color: '#C0392B' }}><span style={{ width: 7, height: 7, borderRadius: 99, background: '#C0392B' }} />Declined</span>
              : pending
              ? <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, font: '700 10px/1 var(--font-ui)', letterSpacing: '0.04em', textTransform: 'uppercase', color: '#E0930C' }}><span style={{ width: 7, height: 7, borderRadius: 99, background: '#F3A617' }} />Pending</span>
              : (() => {
                  const leadQuote = trip.quotes.find(q => q.id === leadingYouId);
                  const s = leadQuote ? STATUS[leadQuote.status] : null;
                  const dotColor = open ? 'rgba(255,255,255,0.85)' : s?.dot;
                  return <>
                    {s && (
                      <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, font: '700 10px/1 var(--font-ui)', letterSpacing: '0.04em', textTransform: 'uppercase', color: dotColor }}>
                        <span style={{ width: 7, height: 7, borderRadius: 99, background: dotColor }} />{s.label}
                      </span>
                    )}
                    {total != null && trip.yourRank != null && (
                      <span style={{ font: '800 10.5px/1 var(--font-ui)', background: trip.yourRank === 1 ? '#2E8B57' : (open ? 'rgba(255,255,255,0.16)' : 'var(--baj-surface-2)'), color: trip.yourRank === 1 ? '#fff' : (open ? '#fff' : 'var(--baj-navy)'), borderRadius: 6, padding: '4px 7px' }}>{trip.yourRank === 1 ? 'LEADING' : `RANK #${trip.yourRank}`}</span>
                    )}
                  </>;
                })()
            }
          </div>
          <div style={{ font: '500 11.5px/1.3 var(--font-ui)', color: open ? 'rgba(255,255,255,0.7)' : 'var(--baj-text-mute)', marginTop: 4 }}>
            {AIRPORTS[trip.from].city} → {AIRPORTS[trip.to].city} · {trip.date}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginTop: 8, font: '600 11px/1 var(--font-ui)', color: open ? 'rgba(255,255,255,0.85)' : 'var(--baj-text-mute)' }}>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap' }}><Icon.pax width={13} height={13} />{`${trip.pax} pax`}</span>
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap' }}><Icon.takeoff width={13} height={13} />{`${trip.legCount} leg${trip.legCount > 1 ? 's' : ''}`}</span>
            {!pending && trip.quotes.length > 0 && (
              <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap' }}><Icon.layers width={13} height={13} />{trip.quotes.length} quote{trip.quotes.length > 1 ? 's' : ''}</span>
            )}
          </div>
        </div>
        <span style={{ color: open ? '#fff' : 'var(--baj-text-mute)', transform: open ? 'rotate(180deg)' : 'none', transition: 'transform 240ms', flex: '0 0 auto' }}><Icon.chevD width={20} height={20} /></span>
      </button>

      <div style={{ maxHeight: open ? 2000 : 0, overflow: open ? 'visible' : 'hidden', transition: 'max-height 440ms var(--ease-out)' }}>
        <div style={{ padding: '4px 15px 16px', background: 'var(--baj-surface)', borderRadius: '0 0 16px 16px' }}>
          <div style={{ display: 'flex', gap: 8, paddingTop: 14 }}>
            <button onClick={() => onAddQuote(trip)} style={{
              flex: 1, padding: '11px 4px', borderRadius: 10, cursor: 'pointer', border: 0, whiteSpace: 'nowrap',
              background: 'var(--baj-blue)', color: '#fff', font: '700 11px/1 var(--font-ui)', letterSpacing: '0.01em',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5,
            }}><Icon.plus width={13} height={13} />Add Quotes</button>
            <button onClick={() => onAttachments(trip)} style={{
              flex: 1, padding: '11px 4px', borderRadius: 10, cursor: 'pointer', whiteSpace: 'nowrap',
              border: '1px solid var(--baj-line)', background: '#fff', color: 'var(--baj-text)', font: '700 11px/1 var(--font-ui)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5,
            }}><Icon.doc width={13} height={13} style={{ color: 'var(--baj-blue)' }} />Attachments</button>
            {total != null && (
              <button onClick={() => onCompetitor(trip)} style={{
                flex: 1, padding: '11px 4px', borderRadius: 10, cursor: 'pointer', border: 0, whiteSpace: 'nowrap',
                background: 'var(--baj-navy)', color: '#fff', font: '700 11px/1 var(--font-ui)',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5,
              }}><Icon.trophy width={13} height={13} style={{ color: '#F3A617' }} />Marketplace</button>
            )}
          </div>

          {pending ? (
            <div style={{ paddingTop: 13 }}>
              <div style={{ padding: '12px 14px', borderRadius: 11, background: '#fff', border: '1px solid var(--baj-line)', display: 'flex', alignItems: 'flex-start', gap: 11 }}>
                <i className="fa-duotone fa-light fa-plane-departure" style={{ '--fa-primary-color': 'rgb(13,146,236)', '--fa-secondary-color': 'rgb(13,146,236)', fontSize: 15, flex: '0 0 auto', marginTop: 2 }} />
                <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
                  <span style={{ font: '600 12.5px/1.35 var(--font-ui)', color: 'var(--baj-text)' }}>Want to service this flight?</span>
                  <span style={{ font: '500 12px/1.35 var(--font-ui)', color: 'var(--baj-text-mute)' }}>Submit a trip or leg quote to compete in the marketplace.</span>
                </div>
              </div>
            </div>
          ) : (
            <div style={{ marginTop: 12 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '0 3px 7px' }}>
                <span style={{ font: '600 10px/1 var(--font-ui)', letterSpacing: '0.12em', textTransform: 'uppercase', color: 'var(--baj-text-mute)' }}>Quotes</span>
                <span style={{ font: '700 10px/1 var(--font-ui)', color: 'var(--baj-text-mute)' }}>{trip.quotes.length} on file</span>
              </div>
              {(() => {
                const capped = trip.quotes.length > 5;
                return (
                  <div style={{ position: 'relative' }}>
                    <div className="quote-scroll" style={{
                      ...(capped ? { maxHeight: 294 } : {}),
                      overflowY: 'auto', overscrollBehavior: 'contain',
                      background: '#fff', borderRadius: 12, border: '1px solid var(--baj-line)', padding: '2px 13px',
                    }}>
                      {trip.quotes.map((q, i) => (
                        <div key={q.id} style={{ borderTop: i === 0 ? 'none' : '1px solid var(--baj-line)' }}>
                          <QuoteRow trip={trip} q={q} onCompetitor={onCompetitor} onEdit={onEdit} isLead={q.id === leadingYouId} />
                        </div>
                      ))}
                    </div>
                    {capped && (
                      <div style={{ position: 'absolute', left: 1, right: 1, bottom: 1, height: 24, borderRadius: '0 0 12px 12px', background: 'linear-gradient(rgba(255,255,255,0), #fff)', pointerEvents: 'none' }} />
                    )}
                  </div>
                );
              })()}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---------- batch quote builder sheet ---------- */
function BatchQuoteSheet({ trip, open, onClose, onDecline, onSubmitAll, isInitial, initialView }) {
  const [batch, setBatch] = useStateC([]);
  const [view, setView] = useStateC(null); // null = selector | 'trip' | 'leg-N' = form
  const [editingIndex, setEditingIndex] = useStateC(null);
  const [tail, setTail] = useStateC('');
  const [raw, setRaw] = useStateC('');
  const [price, setPrice] = useStateC(0);
  const [notes, setNotes] = useStateC('');
  const [pdfFile, setPdfFile] = useStateC(null);
  const fileRef = useRef(null);

  useEffect(() => {
    if (open) { setBatch([]); setView(initialView || null); setEditingIndex(null); resetForm(); }
  }, [open]);

  function resetForm() { setTail(''); setRaw(''); setPrice(0); setNotes(''); setPdfFile(null); }

  function openEdit(i) {
    const b = batch[i];
    setEditingIndex(i);
    setView(b.type);
    setTail(b.tail);
    setRaw(String(b.price));
    setPrice(b.price);
    setNotes(b.notes || '');
    setPdfFile(b.file || null);
  }

  function handleAddToBatch() {
    if (!price) return;
    const item = { type: view, tail, price, notes, file: pdfFile };
    if (editingIndex !== null) {
      setBatch(prev => prev.map((b, i) => i === editingIndex ? item : b));
      setEditingIndex(null);
    } else {
      setBatch(prev => [...prev, item]);
    }
    setView(null);
    resetForm();
  }

  const legs = trip ? Array.from({ length: trip.legCount }, (_, i) => i + 1) : [];
  const labelFor = key => key === 'trip' ? 'Trip Quote' : `Leg ${key.split('-')[1]} Quote`;

  const FL = { font: '600 11px/1 var(--font-ui)', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--baj-text-mute)', marginBottom: 7 };

  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 50,
      display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
      pointerEvents: open ? 'auto' : 'none',
    }}>
      <div onClick={isInitial ? undefined : onClose} style={{
        position: 'absolute', inset: 0,
        background: 'rgba(0,0,0,0.45)',
        opacity: open ? 1 : 0, transition: 'opacity 280ms',
        cursor: isInitial ? 'default' : 'pointer',
      }} />
      <div style={{
        position: 'relative', background: '#fff',
        borderRadius: '20px 20px 0 0',
        maxHeight: '90%', display: 'flex', flexDirection: 'column',
        transform: open ? 'translateY(0)' : 'translateY(100%)',
        transition: 'transform 340ms var(--ease-out)',
      }}>
        {/* header */}
        <div style={{ padding: '16px 16px 13px', borderBottom: '1px solid var(--baj-line)', flex: '0 0 auto' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
            <div>
              <div style={{ font: '600 10.5px/1 var(--font-ui)', letterSpacing: '0.14em', textTransform: 'uppercase', color: 'var(--baj-text-mute)', marginBottom: 4 }}>Add Quotes</div>
              <div style={{ font: '800 17px/1.1 var(--font-ui)', color: 'var(--baj-text)' }}>
                {trip ? `${trip.from} → ${trip.to}` : ''}
              </div>
              {trip && (
                <div style={{ font: '500 11.5px/1.3 var(--font-ui)', color: 'var(--baj-text-mute)', marginTop: 3 }}>
                  {trip.ref} · {trip.date}
                </div>
              )}
            </div>
          </div>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: '14px 16px 30px', display: 'flex', flexDirection: 'column', gap: 12 }}>
          {view === null ? (
            <>
              {/* batch list */}
              {batch.length > 0 && (
                <div style={{ background: '#D6E4F0', borderRadius: 13, padding: '12px 12px 10px' }}>
                  <div style={{ font: '600 10px/1 var(--font-ui)', letterSpacing: '0.12em', textTransform: 'uppercase', color: '#4A6FA5', marginBottom: 8 }}>
                    Batch · {batch.length} quote{batch.length > 1 ? 's' : ''}
                  </div>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
                    {batch.map((b, i) => (
                      <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 13px', borderRadius: 10, background: '#fff', border: '1px solid rgba(74,111,165,0.18)' }}>
                        <div style={{ width: 30, height: 30, borderRadius: 8, background: b.type === 'trip' ? 'var(--baj-blue)' : 'var(--baj-navy)', display: 'flex', alignItems: 'center', justifyContent: 'center', flex: '0 0 auto' }}>
                          {b.type === 'trip' ? <Icon.takeoff width={13} height={13} style={{ color: '#fff' }} /> : <Icon.layers width={13} height={13} style={{ color: '#fff' }} />}
                        </div>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ font: '700 12.5px/1 var(--font-ui)', color: 'var(--baj-text)' }}>{labelFor(b.type)}</div>
                          <div style={{ font: '500 11px/1.3 var(--font-ui)', color: 'var(--baj-text-mute)', marginTop: 3 }}>
                            {b.tail || '—'} · {fmtMoney(b.price)}{b.file ? ' · PDF' : ''}
                          </div>
                        </div>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
                          <button onClick={() => openEdit(i)} style={{ background: 'none', border: 0, cursor: 'pointer', color: 'var(--baj-blue)', display: 'flex', padding: 2 }}>
                            <Icon.pencil width={13} height={13} />
                          </button>
                          <button onClick={() => setBatch(prev => prev.filter((_, j) => j !== i))} style={{ background: 'none', border: 0, cursor: 'pointer', color: 'var(--baj-text-mute)', display: 'flex', padding: 2 }}>
                            <Icon.close width={13} height={13} />
                          </button>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              )}

              {batch.length > 0 && (
                <div style={{ height: 1, background: 'var(--baj-line)', margin: '2px 0' }} />
              )}

              {/* type selector */}
              <div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
                  <button onClick={() => setView('trip')} style={{
                    width: '100%', padding: '14px 16px', borderRadius: 11,
                    border: '1px solid var(--baj-line)', background: '#fff',
                    color: 'var(--baj-text)', font: '700 13px/1 var(--font-ui)', cursor: 'pointer',
                    display: 'flex', alignItems: 'center', gap: 9, textAlign: 'left',
                  }}>
                    <Icon.takeoff width={15} height={15} style={{ color: 'var(--baj-blue)', flex: '0 0 auto' }} />
                    <span>
                      Trip Quote
                      <span style={{ display: 'block', font: '500 11px/1.3 var(--font-ui)', color: 'var(--baj-text-mute)', marginTop: 2 }}>
                        Covers all {trip ? trip.legCount : ''} leg{trip && trip.legCount > 1 ? 's' : ''} in one price
                      </span>
                    </span>
                  </button>
                  {legs.map(n => (
                    <button key={n} onClick={() => setView(`leg-${n}`)} style={{
                      width: '100%', padding: '14px 16px', borderRadius: 11,
                      border: '1px solid var(--baj-line)', background: '#fff',
                      color: 'var(--baj-text)', font: '700 13px/1 var(--font-ui)',
                      cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 9, textAlign: 'left',
                    }}>
                      <Icon.layers width={15} height={15} style={{ color: 'var(--baj-blue)', flex: '0 0 auto' }} />
                      <span>
                        Leg {n} Quote
                        <span style={{ display: 'block', font: '500 11px/1.3 var(--font-ui)', color: 'var(--baj-text-mute)', marginTop: 2 }}>Price for leg {n} only</span>
                      </span>
                    </button>
                  ))}
                </div>
              </div>

              {/* submit / decline */}
              <div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginTop: 4 }}>
                <button
                  onClick={() => batch.length > 0 && onSubmitAll(batch)}
                  style={{
                    width: '100%', padding: '15px 0', border: 0, borderRadius: 12,
                    cursor: batch.length > 0 ? 'pointer' : 'default',
                    background: batch.length > 0 ? 'var(--baj-blue)' : 'var(--baj-surface-2)',
                    color: batch.length > 0 ? '#fff' : 'var(--baj-text-mute)',
                    font: '700 13px/1 var(--font-ui)', letterSpacing: '0.06em', textTransform: 'uppercase',
                  }}>
                  {batch.length > 0 ? `Submit ${batch.length} quote${batch.length > 1 ? 's' : ''}` : 'Submit quotes'}
                </button>
                <button onClick={isInitial ? onDecline : onClose} style={{
                  width: '100%', padding: '13px 0', borderRadius: 12, cursor: 'pointer',
                  border: isInitial ? '1px solid rgba(192,57,43,0.35)' : '1px solid var(--baj-line)',
                  background: isInitial ? 'rgba(192,57,43,0.06)' : '#fff',
                  color: isInitial ? '#C0392B' : 'var(--baj-text)',
                  font: '700 13px/1 var(--font-ui)',
                }}>{isInitial ? 'Decline' : 'Cancel'}</button>
              </div>
            </>
          ) : (
            /* ---- inline form ---- */
            <>
              <button onClick={() => { setView(null); setEditingIndex(null); resetForm(); }} style={{
                display: 'inline-flex', alignItems: 'center', gap: 5,
                background: 'none', border: 0, cursor: 'pointer',
                color: 'var(--baj-blue)', font: '600 12.5px/1 var(--font-ui)', padding: 0,
              }}>
                ← Back
              </button>

              <div style={{ font: '700 15px/1 var(--font-ui)', color: 'var(--baj-text)' }}>
                {editingIndex !== null ? `Edit ${labelFor(view)}` : labelFor(view)}
              </div>

              {/* aircraft */}
              <div>
                <div style={FL}>Aircraft</div>
                <div style={{ position: 'relative' }}>
                  <select value={tail} onChange={e => setTail(e.target.value)} style={{
                    width: '100%', padding: '11px 36px 11px 13px', borderRadius: 10,
                    border: '1px solid var(--baj-line)', background: '#fff',
                    font: '600 13px/1 var(--font-ui)', color: tail ? 'var(--baj-text)' : 'var(--baj-text-mute)',
                    appearance: 'none', WebkitAppearance: 'none', cursor: 'pointer', outline: 'none',
                  }}>
                    <option value="">Select aircraft…</option>
                    {FLEET.map(f => <option key={f.tail} value={f.tail}>{f.tail} · {acYear(f.ac)} {f.ac} · {f.cat}</option>)}
                  </select>
                  <span style={{ position: 'absolute', right: 12, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: 'var(--baj-text-mute)' }}>
                    <Icon.chevD width={14} height={14} />
                  </span>
                </div>
              </div>

              {/* amount */}
              <div style={{ background: 'var(--baj-surface)', borderRadius: 16, padding: '18px 18px 20px', border: '1px solid var(--baj-line)' }}>
                <div style={{ font: '600 11px/1 var(--font-ui)', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--baj-text-mute)', textAlign: 'center', marginBottom: 12 }}>Quote amount</div>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
                  <span style={{ font: '800 34px/1 var(--font-ui)', color: raw ? 'var(--baj-text)' : 'var(--baj-text-mute)', letterSpacing: '-0.01em' }}>$</span>
                  <input
                    inputMode="numeric" value={raw}
                    onChange={e => { const d = e.target.value.replace(/[^0-9]/g, ''); const n = d ? parseInt(d, 10) : 0; setRaw(d ? n.toLocaleString() : ''); setPrice(n); }}
                    placeholder="0"
                    style={{ font: '800 34px/1 var(--font-ui)', color: 'var(--baj-text)', background: 'transparent', border: 0, outline: 'none', width: 160, letterSpacing: '-0.01em', caretColor: 'var(--baj-blue)' }}
                  />
                </div>
              </div>

              {/* notes */}
              <div>
                <div style={FL}>Notes</div>
                <textarea value={notes} onChange={e => setNotes(e.target.value)} placeholder="Internal notes…" rows={3}
                  style={{ width: '100%', padding: '11px 13px', borderRadius: 10, border: '1px solid var(--baj-line)', background: '#fff', font: '500 13px/1.45 var(--font-ui)', color: 'var(--baj-text)', resize: 'vertical', outline: 'none', boxSizing: 'border-box' }} />
              </div>

              {/* pdf */}
              <div>
                <div style={FL}>Quote document</div>
                <input ref={fileRef} type="file" accept=".pdf,application/pdf" style={{ display: 'none' }}
                  onChange={e => setPdfFile(e.target.files[0] || null)} />
                {pdfFile ? (
                  <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 13px', borderRadius: 10, border: '1px solid var(--baj-line)', background: 'var(--baj-surface)' }}>
                    <Icon.doc width={16} height={16} style={{ color: 'var(--baj-blue)', flex: '0 0 auto' }} />
                    <span style={{ flex: 1, font: '500 12.5px/1.3 var(--font-ui)', color: 'var(--baj-text)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{pdfFile.name}</span>
                    <button onClick={() => setPdfFile(null)} style={{ background: 'none', border: 0, cursor: 'pointer', color: 'var(--baj-text-mute)', display: 'flex', padding: 2 }}>
                      <Icon.close width={14} height={14} />
                    </button>
                  </div>
                ) : (
                  <button onClick={() => fileRef.current && fileRef.current.click()} style={{
                    width: '100%', padding: '11px 13px', borderRadius: 10,
                    border: '1.5px dashed var(--baj-line)', background: '#fff',
                    font: '600 12.5px/1 var(--font-ui)', color: 'var(--baj-text-mute)',
                    cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
                  }}>
                    <Icon.doc width={14} height={14} style={{ color: 'var(--baj-blue)' }} />Attach PDF
                  </button>
                )}
              </div>

              <button onClick={handleAddToBatch} style={{
                width: '100%', padding: '15px 0', border: 0, borderRadius: 12,
                cursor: price ? 'pointer' : 'default',
                background: price ? 'var(--baj-navy)' : 'var(--baj-surface-2)',
                color: price ? '#fff' : 'var(--baj-text-mute)',
                font: '700 13px/1 var(--font-ui)', letterSpacing: '0.06em', textTransform: 'uppercase',
              }}>{editingIndex !== null ? 'Save changes' : 'Add to batch'}</button>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

/* ---------- holistic map sheet ---------- */
function MapSheet({ open, onClose, trips }) {
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 50,
      display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
      pointerEvents: open ? 'auto' : 'none',
    }}>
      <div onClick={onClose} style={{
        position: 'absolute', inset: 0,
        background: 'rgba(0,0,0,0.45)',
        opacity: open ? 1 : 0, transition: 'opacity 280ms',
      }} />
      <div style={{
        position: 'relative', background: '#fff', borderRadius: '20px 20px 0 0',
        maxHeight: '72%', display: 'flex', flexDirection: 'column',
        transform: open ? 'translateY(0)' : 'translateY(100%)',
        transition: 'transform 340ms var(--ease-out)',
      }}>
        <div style={{ padding: '14px 16px 11px', display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid var(--baj-line)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <i className="fas fa-map" style={{ fontSize: 14, color: 'var(--baj-blue)' }} />
            <span style={{ font: '700 14px/1 var(--font-ui)', color: 'var(--baj-text)' }}>Route Map</span>
            <span style={{ font: '600 11px/1 var(--font-ui)', color: 'var(--baj-text-mute)' }}>· {trips.length} itineraries</span>
          </div>
          <button onClick={onClose} style={{ background: 'none', border: 0, cursor: 'pointer', color: 'var(--baj-text-mute)', display: 'flex', padding: 4 }}>
            <Icon.close width={18} height={18} />
          </button>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column', gap: 12, padding: 14 }}>
          {trips.map(t => (
            <div key={t.id}>
              <div style={{ font: '700 12px/1 var(--font-ui)', color: 'var(--baj-text)', marginBottom: 6 }}>
                {t.from} → {t.to}
                <span style={{ fontWeight: 500, color: 'var(--baj-text-mute)', marginLeft: 7 }}>{t.date}</span>
              </div>
              <div style={{ borderRadius: 12, overflow: 'hidden', border: '1px solid var(--baj-line)' }}>
                <MapGraphic trip={t} />
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ---------- APP C ---------- */
function ItineraryApp() {
  const [trips, setTrips] = useStateC(() => TRIPS.map(t => {
    const stripYou = rows => rows ? rows.filter(r => !r.you) : rows;
    return {
      ...t,
      quotes: [],
      yourRank: null,
      ladder: t.ladder ? {
        ...t.ladder,
        trip: stripYou(t.ladder.trip),
        leg1: stripYou(t.ladder.leg1),
        leg2: stripYou(t.ladder.leg2),
      } : t.ladder,
    };
  }));
  const [tab, setTab] = useStateC('Quotes');
  const [openId, setOpenId] = useStateC(null);
  const [competitorId, setCompetitorId] = useStateC(null);
  const [quoteEdit, setQuoteEdit] = useStateC(null);
  const [attachmentsId, setAttachmentsId] = useStateC(null);
  const [legend, setLegend] = useStateC(false);
  const [addQuoteTripId, setAddQuoteTripId] = useStateC(() => {
    const first = TRIPS.find(t => t.bucket === 'pending');
    return first ? first.id : null;
  });
  const [addQuoteIsInitial, setAddQuoteIsInitial] = useStateC(true);
  const [batchInitialView, setBatchInitialView] = useStateC(null);
  const nextTripTimer = useRef(null);

  const displayedTrips = trips.filter(t => t.bucket === 'pending' || t.quotes.some(q => q.you) || t.declined);
  const competitorTrip = competitorId ? trips.find(t => t.id === competitorId) : null;
  const addQuoteTrip = addQuoteTripId ? trips.find(t => t.id === addQuoteTripId) : null;
  const attachmentsTrip = attachmentsId ? trips.find(t => t.id === attachmentsId) : null;
  const quoteEditTrip = quoteEdit ? trips.find(t => t.id === quoteEdit.tripId) : null;
  const quoteEditQuote = quoteEditTrip ? quoteEditTrip.quotes.find(q => q.id === quoteEdit.quoteId) : null;

  function updateTrip(tripId, updater) {
    setTrips(prev => prev.map(t => t.id === tripId ? updater(t) : t));
  }

  function applyLadderYou(ladder, updates) {
    if (!ladder) return ladder;
    const updateRows = rows => rows
      ? [...rows.map(r => r.you ? { ...r, ...updates } : r)].sort((a, b) => a.price - b.price)
      : rows;
    return {
      ...ladder,
      ...(ladder.trip ? { trip: updateRows(ladder.trip) } : {}),
      ...(ladder.leg1 ? { leg1: updateRows(ladder.leg1) } : {}),
      ...(ladder.leg2 ? { leg2: updateRows(ladder.leg2) } : {}),
    };
  }

  function calcRank(ladder) {
    return ladder && ladder.trip ? ladder.trip.findIndex(r => r.you) + 1 : null;
  }

  function handleRequote(tripId, quoteId, { tail, price }) {
    const fleet = FLEET.find(f => f.tail === tail) || {};
    const youUpdates = { price, ...(fleet.ac ? { ac: fleet.ac, cat: fleet.cat } : {}) };
    updateTrip(tripId, t => {
      const newLadder = applyLadderYou(t.ladder, youUpdates);
      return {
        ...t,
        yourRank: calcRank(newLadder) || t.yourRank,
        quotes: t.quotes.map(q => q.id === quoteId
          ? { ...q, amount: price, tail, status: 'submitted', ...(fleet.ac ? { ac: fleet.ac, cat: fleet.cat } : {}) }
          : q
        ),
        ladder: newLadder,
      };
    });
    setQuoteEdit(null);
    setCompetitorId(tripId);
  }

  function handleCompetitorAddQuote(viewKey) {
    const tripId = competitorId;
    const batchView = viewKey === 'trip' ? 'trip' : `leg-${viewKey.replace('leg', '')}`;
    setBatchInitialView(batchView);
    setAddQuoteIsInitial(false);
    setAddQuoteTripId(tripId);
    setCompetitorId(null);
  }

  function handleDecline(tripId) {
    clearTimeout(nextTripTimer.current);
    updateTrip(tripId, t => ({ ...t, declined: true }));
    setOpenId(tripId);
    setAddQuoteTripId(null);
    setAddQuoteIsInitial(false);
  }

  function handleBatchSubmit(tripId, batchQuotes) {
    updateTrip(tripId, t => {
      const newQuotes = batchQuotes.map((b, i) => {
        const f = (b.tail && FLEET.find(x => x.tail === b.tail)) || FLEET[0] || {};
        const isLeg = b.type !== 'trip';
        const legNum = isLeg ? parseInt(b.type.split('-')[1]) : null;
        return {
          id: `q_${tripId}_${Date.now()}_${i}`,
          tail: f.tail || '', ac: f.ac || '', cat: f.cat || '',
          type: isLeg ? 'Leg' : 'Trip',
          legs: isLeg ? 1 : t.legCount,
          ...(isLeg ? { legNum } : {}),
          ...(b.notes ? { notes: b.notes } : {}),
          ...(b.file ? { fileName: b.file.name } : {}),
          status: 'submitted', requested: null, amount: b.price, you: true,
        };
      });
      const getFleet = b => (b && b.tail && FLEET.find(x => x.tail === b.tail)) || FLEET[0] || {};
      const youMeta = b => { const f = getFleet(b); return { ac: f.ac || '', cat: f.cat || '', refurbInt: f.refurbInt, refurbExt: f.refurbExt }; };
      const insertAllYou = (rows, quotes) => {
        if (!rows) return rows;
        if (!quotes.length) return rows;
        return [...rows, ...quotes.map(b => ({ price: b.price, you: true, ...youMeta(b) }))].sort((a, b) => a.price - b.price);
      };
      const newLadder = t.ladder ? {
        ...t.ladder,
        trip: insertAllYou(t.ladder.trip, batchQuotes.filter(b => b.type === 'trip')),
        leg1: insertAllYou(t.ladder.leg1, batchQuotes.filter(b => b.type === 'leg-1')),
        leg2: insertAllYou(t.ladder.leg2, batchQuotes.filter(b => b.type === 'leg-2')),
      } : t.ladder;
      const newRank = newLadder?.trip ? newLadder.trip.findIndex(r => r.you) + 1 : null;
      return { ...t, bucket: 'live', yourRank: newRank || null, ladder: newLadder, quotes: [...t.quotes, ...newQuotes] };
    });
    setOpenId(tripId);
    setAddQuoteTripId(null);
    setAddQuoteIsInitial(false);
    setBatchInitialView(null);
  }

  return (
    <PhoneShell statusBg="#19253C" statusFg="#fff">
      <div style={{ flex: '0 0 auto', background: 'var(--baj-navy)', color: '#fff', padding: '6px 18px 15px' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 14 }}>
          <BajopsMark />
          <span style={{ font: '600 12px/1 var(--font-ui)', color: 'rgba(255,255,255,0.62)' }}>Demo Operator</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
          <div style={{ font: '800 23px/1.05 var(--font-ui)', letterSpacing: '-0.01em' }}>Active Quotes</div>
          <button onClick={() => setLegend(true)} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, background: 'rgba(255,255,255,0.1)', border: 0, borderRadius: 9, padding: '8px 11px', cursor: 'pointer', color: 'rgba(255,255,255,0.85)', font: '600 11px/1 var(--font-ui)' }}>
            <Icon.filter width={13} height={13} />Status key
          </button>
        </div>
      </div>

      <div style={{ flex: 1, overflowY: 'auto', background: '#EEF2F5', padding: '15px 14px 24px', display: 'flex', flexDirection: 'column', gap: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', padding: '0 4px' }}>
          <span style={{ font: '600 11px/1 var(--font-ui)', letterSpacing: '0.04em', color: 'var(--baj-text-mute)' }}>
            {displayedTrips.length > 0 ? `${displayedTrips.length} flights · tap to expand` : `${trips.length} itineraries pending`}
          </span>
        </div>
        {displayedTrips.length === 0 ? (
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '48px 24px', gap: 10, textAlign: 'center' }}>
            <Icon.layers width={28} height={28} style={{ color: 'var(--baj-text-mute)', opacity: 0.4 }} />
            <span style={{ font: '600 13px/1.4 var(--font-ui)', color: 'var(--baj-text-mute)' }}>Submitted quotes will appear here</span>
          </div>
        ) : displayedTrips.map(t => (
          <TripGroup key={t.id} trip={t} openId={openId} setOpenId={setOpenId}
            onCompetitor={(tr) => setCompetitorId(tr.id)}
            onAttachments={(tr) => setAttachmentsId(tr.id)}
            onEdit={(q) => setQuoteEdit({ tripId: t.id, quoteId: q.id })}
            onAddQuote={(tr) => { setAddQuoteIsInitial(false); setAddQuoteTripId(tr.id); }}
          />
        ))}
      </div>

      <TabBar active={tab} onTab={setTab} />

      <CompetitorSheet trip={competitorTrip} open={!!competitorId} onClose={() => setCompetitorId(null)} onAddQuote={handleCompetitorAddQuote} />
      <AttachmentsSheet trip={attachmentsTrip} open={!!attachmentsId} onClose={() => setAttachmentsId(null)} />
      <QuoteEditSheet
        trip={quoteEditTrip} quote={quoteEditQuote}
        open={!!quoteEdit} onClose={() => setQuoteEdit(null)}
        onRequote={({ tail, price }) => handleRequote(quoteEdit.tripId, quoteEdit.quoteId, { tail, price })}
      />
      <LegendSheet open={legend} onClose={() => setLegend(false)} />
      <BatchQuoteSheet
        trip={addQuoteTrip} open={!!addQuoteTripId}
        isInitial={addQuoteIsInitial}
        initialView={batchInitialView}
        onClose={() => { clearTimeout(nextTripTimer.current); setAddQuoteTripId(null); setBatchInitialView(null); }}
        onDecline={() => handleDecline(addQuoteTripId)}
        onSubmitAll={(batch) => handleBatchSubmit(addQuoteTripId, batch)}
      />
    </PhoneShell>
  );
}

Object.assign(window, { ItineraryApp });
