// Main app — vendor link-based access, mobile-first single-page form
const { useState: useS, useMemo: useM, useEffect: useE } = React;

const todayISO = () => new Date().toISOString().slice(0, 10);

// Aggressive Firestore payload scrub. The user kept hitting "array
// contains an invalid nested entity" — the safest pre-treatment is a
// JSON round-trip (drops undefined, functions, NaN, Infinity, DOM
// nodes, React fibers, anything non-serializable) followed by a
// recursive walk that wraps any direct array-of-array values in an
// object so Firestore accepts them.
function sanitizeForFirestore(value) {
  // Round-trip first to strip non-serializable junk. Replacer also
  // converts NaN/Infinity → null and drops `undefined` properties.
  let plain;
  try {
    plain = JSON.parse(JSON.stringify(value, (key, v) => {
      if (v === undefined) return null;
      if (typeof v === 'number' && !Number.isFinite(v)) return null;
      if (typeof v === 'function') return null;
      // Firestore Timestamps survive — toJSON returns ISO string,
      // which Firestore re-accepts as a Timestamp on write.
      return v;
    }));
  } catch {
    return null;
  }
  return scrubArrays(plain);
}

function scrubArrays(value) {
  if (value === null || typeof value !== 'object') return value;
  if (Array.isArray(value)) {
    return value.map((item) => {
      if (Array.isArray(item)) {
        // Firestore disallows direct array nesting — wrap inner array.
        return { items: scrubArrays(item) };
      }
      return scrubArrays(item);
    });
  }
  const out = {};
  for (const k of Object.keys(value)) {
    out[k] = scrubArrays(value[k]);
  }
  return out;
}

const blankData = () => ({
  vendorName: '', vendorContact: '', vendorPhone: '', vendorEmail: '', vendorPO: '',
  clientName: '', clientPhone: '', clientEmail: '',
  preferredContact: 'Phone',
  address: '', installNeeded: 'Yes', homeType: 'Single fam.',
  accessNotes: '',
  dateReceived: todayISO(), completionRequested: '',
  timeframe: '1mo',
  // Cabinets created on demand by the wizard (Step 6); no need to
  // seed an empty one — it caused phantom cabinets in the review.
  cabinets: [],
  toeKick: { height: '4', setback: '3', thickness: '1/4"', heightU: 'in', depthU: 'in', setbackU: 'in' },
  scribe: { needed: 'No', linearFeet: '', matchMaterial: 'Yes' },
  rush: false,
  specialInstructions: '',
  draftId: generateDraftId(),
});

// ============================================================
// Header — small "Precision Cabinets LLC" + large vendor name
// ============================================================
function VendorHeader({ vendor, showDashboard, onDashboard }) {
  return (
    <header className="vendor-header">
      <div className="vendor-header-brand">Precision Cabinets LLC</div>
      <div className="vendor-header-name">{vendor.name}</div>
      {showDashboard && (
        <button className="vendor-header-btn" onClick={onDashboard}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
            <rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
            <rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>
          </svg>
          My Orders
        </button>
      )}
    </header>
  );
}

// ============================================================
// Root — detects vendor slug and routes
// ============================================================
function AppRoot() {
  const [vendor, setVendor] = useS(null);
  const [vendorLoading, setVendorLoading] = useS(true);
  const route = useHashRouter();

  // Warranty landing page — DISABLED until the service-request inbox
  // is built in Wave 6. Code remains in place (warranty.jsx, the
  // route, the Firestore rules) so we can flip a single flag to turn
  // it back on. See admin /coming-soon for status.
  // if (route.page === 'warranty' && window.WarrantyPage) {
  //   return <WarrantyPage warrantyId={route.params.warrantyId} />;
  // }

  // Cache shop constants (rushThresholdDays + rushUpchargeAmount + scribe defaults).
  useE(() => {
    db.collection('shopConstants').doc('current').get()
      .then(snap => { if (snap.exists) window.PC_SHOP_CONSTANTS = snap.data(); })
      .catch(() => {});
    // Cache company info for outgoing PDFs (insurance estimate etc).
    db.collection('companyInfo').doc('current').get()
      .then(snap => { if (snap.exists) window.PC_COMPANY_INFO = snap.data(); })
      .catch(() => {});
  }, []);

  useE(() => {
    // Slug resolution order: URL first (so a fresh link always wins
    // and lets a tablet be handed between vendors), then localStorage
    // (lets a home-screen PWA rehydrate without the query string).
    const urlSlug = getVendorFromURL();
    const slug = urlSlug || getStoredVendorSlug();
    if (!slug) { setVendorLoading(false); return; }
    db.collection('vendors').where('slug', '==', slug).limit(1).get().then(snap => {
      if (!snap.empty) {
        const doc = snap.docs[0];
        const v = { id: doc.id, ...doc.data() };
        setVendor(v);
        setStoredVendorSlug(v.slug);
      }
      setVendorLoading(false);
    }).catch(() => {
      const local = loadVendors();
      const v = local.find(x => x.slug === slug);
      if (v) { setVendor(v); setStoredVendorSlug(v.slug); }
      setVendorLoading(false);
    });
  }, []);

  if (vendorLoading) {
    return (
      <div className="app-loading">
        <div className="brand-mark" style={{ width: 64, height: 64 }}></div>
        <p>Loading...</p>
      </div>
    );
  }

  if (!vendor) {
    return <NoVendorScreen onFound={(v) => { setStoredVendorSlug(v.slug); setVendor(v); }} />;
  }

  if (route.page === 'order-detail') {
    return (
      <>
        <VendorHeader vendor={vendor} showDashboard={true} onDashboard={() => navigate('/dashboard')} />
        <OrderDetailPage orderId={route.params.orderId} onBack={() => navigate('/dashboard')} isVendor={true} />
      </>
    );
  }

  if (route.page === 'new') {
    return (
      <>
        <VendorHeader vendor={vendor} showDashboard={true} onDashboard={() => navigate('/dashboard')} />
        <NewOrderPage vendor={vendor} onDone={() => navigate('/dashboard')} />
      </>
    );
  }

  if (route.page === 'draft') {
    return (
      <>
        <VendorHeader vendor={vendor} showDashboard={true} onDashboard={() => navigate('/dashboard')} />
        <NewOrderPage vendor={vendor} draftOrderId={route.params.draftId} onDone={() => navigate('/dashboard')} />
      </>
    );
  }

  // Default: dashboard
  return (
    <>
      <VendorHeader vendor={vendor} showDashboard={false} />
      <VendorDashboard
        vendorId={vendor.id}
        vendorName={vendor.name}
        onNewOrder={() => navigate('/new')}
        onViewOrder={(id) => navigate('/orders/' + id)}
      />
    </>
  );
}

// ============================================================
// NoVendorScreen — recovery UI when the vendor slug is missing.
// On home-screen PWA launches the URL has no `?vendor=` param, so
// we lean on (a) localStorage from a previous slug-bearing visit,
// and (b) a manual lookup field where the vendor types their name
// or slug and we search the vendors collection. Both paths set the
// active vendor + persist the slug for next launch.
// ============================================================
function NoVendorScreen({ onFound }) {
  const [query, setQuery] = useS('');
  const [searching, setSearching] = useS(false);
  const [results, setResults] = useS([]);
  const [error, setError] = useS('');
  const stored = getStoredVendorSlug();

  const search = async () => {
    const q = query.trim();
    if (!q) return;
    setSearching(true);
    setError('');
    try {
      // Slug exact match first — fastest and most reliable.
      const slugSnap = await db.collection('vendors').where('slug', '==', q.toLowerCase()).limit(1).get();
      if (!slugSnap.empty) {
        const doc = slugSnap.docs[0];
        const v = { id: doc.id, ...doc.data() };
        if (v.isActive !== false) { onFound(v); return; }
      }
      // Fall back to a name prefix scan. Vendors collection is small;
      // we pull all active vendors and filter client-side rather than
      // requiring a Firestore index for case-insensitive search.
      const allSnap = await db.collection('vendors').get();
      const all = allSnap.docs
        .map(d => ({ id: d.id, ...d.data() }))
        .filter(v => v.isActive !== false);
      const lc = q.toLowerCase();
      const matches = all.filter(v =>
        (v.name || '').toLowerCase().includes(lc) ||
        (v.slug || '').toLowerCase().includes(lc) ||
        (v.contact || '').toLowerCase().includes(lc) ||
        (v.email || '').toLowerCase().includes(lc),
      );
      if (matches.length === 1) { onFound(matches[0]); return; }
      if (matches.length === 0) { setError('No vendor found. Double-check spelling or contact Precision Cabinets.'); setResults([]); return; }
      setResults(matches);
    } catch (err) {
      setError('Search failed: ' + (err.message || 'unknown error'));
    } finally {
      setSearching(false);
    }
  };

  return (
    <div className="app-loading" style={{ alignItems: 'stretch', padding: '32px 24px', maxWidth: 520, margin: '0 auto' }}>
      <div style={{ textAlign: 'center' }}>
        <div className="brand-mark" style={{ width: 64, height: 64, margin: '0 auto 12px' }}></div>
        <h2 style={{ fontFamily: 'var(--serif)', margin: '8px 0 4px' }}>Precision Cabinets LLC</h2>
        <p style={{ color: 'var(--ink-soft)', fontSize: 14, marginTop: 0 }}>Find your vendor account</p>
      </div>

      <div style={{ marginTop: 24 }}>
        <label style={{ fontSize: 12, color: 'var(--ink-soft)', display: 'block', marginBottom: 6 }}>
          Vendor name, slug, or contact email
        </label>
        <input
          className="input"
          autoFocus
          placeholder="e.g. Sun Valley Cabinets"
          value={query}
          onChange={e => setQuery(e.target.value)}
          onKeyDown={e => { if (e.key === 'Enter') search(); }}
        />
        <button
          className="btn primary lg"
          style={{ width: '100%', marginTop: 10 }}
          onClick={search}
          disabled={searching || !query.trim()}
        >
          {searching ? 'Searching…' : 'Open my portal'}
        </button>

        {error && (
          <div style={{ marginTop: 10, fontSize: 13, color: 'var(--danger, #b91c1c)', background: '#fef2f2', padding: 10, borderRadius: 6 }}>
            {error}
          </div>
        )}

        {results.length > 1 && (
          <div style={{ marginTop: 12 }}>
            <div style={{ fontSize: 12, color: 'var(--ink-soft)', marginBottom: 6 }}>Multiple matches — pick yours:</div>
            {results.map(v => (
              <button
                key={v.id}
                className="btn ghost"
                style={{ width: '100%', textAlign: 'left', justifyContent: 'flex-start', marginBottom: 6 }}
                onClick={() => onFound(v)}
              >
                <strong>{v.name}</strong>
                {v.contact && <span style={{ color: 'var(--ink-faint)', marginLeft: 8 }}>· {v.contact}</span>}
              </button>
            ))}
          </div>
        )}
      </div>

      <div style={{ marginTop: 24, fontSize: 12, color: 'var(--ink-faint)', textAlign: 'center' }}>
        Don't have an account?<br/>Contact Precision Cabinets at (480) 555-0100.
      </div>

      {stored && (
        <button
          className="btn ghost"
          style={{ marginTop: 16, fontSize: 12 }}
          onClick={() => { clearStoredVendorSlug(); window.location.reload(); }}
        >
          Clear saved vendor
        </button>
      )}
    </div>
  );
}

// ============================================================
// NewOrderPage — single scrollable form, no sidebar/steps
// ============================================================
function NewOrderPage({ vendor, onDone, draftOrderId }) {
  const [data, setData] = useS(() => applyVendorToData(blankData(), vendor));
  const [pricing, setPricing] = useS(loadPricing);
  const [submitting, setSubmitting] = useS(false);
  const [savingDraft, setSavingDraft] = useS(false);
  const [draftId, setDraftId] = useS(draftOrderId || null);
  const [draftLoading, setDraftLoading] = useS(!!draftOrderId);
  const [autoSavedAt, setAutoSavedAt] = useS(null);
  const [submitted, setSubmitted] = useS(null); // { orderId, orderNumber }
  const [showChecklist, setShowChecklist] = useS(false);
  const [showMarkupModal, setShowMarkupModal] = useS(false);

  useE(() => { loadPricingFromFirestore().then(p => { setPricing(p); window.PC_PRICING_CACHE = p; }); }, []);

  // Load existing draft when entering /drafts/:id route.
  useE(() => {
    if (!draftOrderId) return;
    let cancelled = false;
    db.collection('orders').doc(draftOrderId).get().then(doc => {
      if (cancelled || !doc.exists) { setDraftLoading(false); return; }
      const d = doc.data();
      setData(prev => ({
        ...prev,
        ...d,
        cabinets: d.cabinets || prev.cabinets,
        toeKick: d.toeKick || prev.toeKick,
        // keep our local draftId but use the Firestore doc id as the
        // canonical identifier so subsequent saves UPSERT in place.
        draftId: prev.draftId,
      }));
      setDraftLoading(false);
    }).catch(() => setDraftLoading(false));
    return () => { cancelled = true; };
  }, [draftOrderId]);

  // Auto-fill vendor's last-used contact info from vendors/{id}.lastContact
  // (overridable on every order — this is a default, not a lock).
  useE(() => {
    if (draftOrderId || !vendor?.id) return;
    db.collection('vendors').doc(vendor.id).get().then(snap => {
      if (!snap.exists) return;
      const lc = snap.data()?.lastContact;
      if (!lc) return;
      setData(prev => ({
        ...prev,
        vendorContact: prev.vendorContact || lc.contact || '',
        vendorPhone:   prev.vendorPhone   || lc.phone   || '',
        vendorEmail:   prev.vendorEmail   || lc.email   || '',
      }));
    }).catch(() => {});
  }, [vendor?.id, draftOrderId]);

  const update = (k, v) => setData(prev => ({ ...prev, [k]: v }));
  const estimate = useM(() => computeEstimate(data, pricing), [data, pricing]);
  const cabinets = data.cabinets || [];
  const tk = data.toeKick || {};
  const canSubmit = data.clientName && data.address && cabinets.length > 0;

  const updateCabinet = (idx, patch) => {
    const next = cabinets.map((c, i) => i === idx ? { ...c, ...patch } : c);
    setData(prev => ({ ...prev, cabinets: next }));
  };

  const addCabinet = () => {
    setData(prev => ({ ...prev, cabinets: [...(prev.cabinets || []), makeCabinet(cabinets.length + 1)] }));
  };

  const removeCabinet = (idx) => {
    if (cabinets.length <= 1) return;
    setData(prev => ({ ...prev, cabinets: prev.cabinets.filter((_, i) => i !== idx) }));
  };

  const updateToeKick = (patch) => {
    setData(prev => ({ ...prev, toeKick: { ...(prev.toeKick || {}), ...patch } }));
  };

  // Persist the vendor's most recent contact info so future orders
  // pre-fill from `vendors/{id}.lastContact`.
  const writeBackVendorContact = async () => {
    if (!vendor?.id) return;
    try {
      await db.collection('vendors').doc(vendor.id).set({
        lastContact: {
          contact: data.vendorContact || '',
          phone:   data.vendorPhone   || '',
          email:   data.vendorEmail   || '',
        },
      }, { merge: true });
    } catch {}
  };

  // Build the order payload — same shape used for both submits and
  // draft saves. Drafts keep photos in-place (base64) so the vendor
  // can come back to a partially-filled form. Submits strip them.
  const buildOrderPayload = ({ status, includePhotos }) => {
    const finalEstimate = computeEstimate(data, pricing);
    const pricingSnapshot = {};
    for (const [k, v] of Object.entries(pricing)) pricingSnapshot[k] = { price: v.price };
    const cleanCabinets = includePhotos ? cabinets : cabinets.map(({ photo, ...rest }) => rest);
    const raw = {
      status,
      vendorId: vendor.id, vendorName: data.vendorName || vendor.name, vendorSlug: vendor.slug,
      vendorContact: data.vendorContact, vendorPhone: data.vendorPhone, vendorEmail: data.vendorEmail, vendorPO: data.vendorPO,
      clientName: data.clientName, clientPhone: data.clientPhone, clientEmail: data.clientEmail,
      preferredContact: data.preferredContact, address: data.address,
      installNeeded: data.installNeeded, homeType: data.homeType, accessNotes: data.accessNotes,
      dateReceived: data.dateReceived || todayISO(), completionRequested: data.completionRequested, timeframe: data.timeframe,
      specialInstructions: data.specialInstructions,
      cabinets: cleanCabinets, toeKick: data.toeKick || {},
      estimate: finalEstimate, pricingSnapshot,
      // Carry over markup state if it's already on the doc.
      insuranceMarkupPercent: data.insuranceMarkupPercent || 0,
      markupHistory: data.markupHistory || [],
      reminderSent: data.reminderSent || false,
    };
    // Firestore rejects payloads containing `undefined`, raw functions,
    // arrays-of-arrays, or NaN. Run every value through the sanitizer
    // so we never blow up a draft save because some intake field
    // happened to be unset (annotations.data is the usual culprit).
    return sanitizeForFirestore(raw);
  };

  // Save (or upsert) as a draft. Drafts have no order number and do
  // not appear in the tech queue. Auto-save and the explicit "Save
  // as Draft" button both call this.
  const saveDraft = async ({ silent } = {}) => {
    if (!silent) setSavingDraft(true);
    try {
      const payload = buildOrderPayload({ status: 'draft', includePhotos: true });
      const writePayload = {
        ...payload,
        updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
      };
      if (draftId) {
        await db.collection('orders').doc(draftId).set(writePayload, { merge: true });
      } else {
        const ref = await db.collection('orders').add({
          ...writePayload,
          orderNumber: 'DRAFT-' + Date.now(),
          statusHistory: [{ status: 'draft', timestamp: new Date(), changedBy: 'vendor', changedByName: vendor.name, method: 'auto-save' }],
          createdAt: firebase.firestore.FieldValue.serverTimestamp(),
        });
        setDraftId(ref.id);
      }
      setAutoSavedAt(new Date());
      writeBackVendorContact();
    } catch (err) {
      console.error('Save draft failed:', err);
      if (!silent) {
        const detail = err?.message ? err.message : 'Unknown error';
        alert(`Failed to save draft.\n\n${detail}\n\nIf this keeps happening, screenshot this and send to Precision.`);
      }
    } finally {
      if (!silent) setSavingDraft(false);
    }
  };

  // Auto-save: 5s debounce after the last edit. Only fires once a
  // customer name + at least one cabinet are present (avoids spamming
  // empty drafts on first load).
  useE(() => {
    if (draftLoading) return;
    if (!data.clientName || !cabinets.length) return;
    const id = setTimeout(() => { saveDraft({ silent: true }); }, 5000);
    return () => clearTimeout(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [data, draftLoading]);

  const handleSubmit = async () => {
    setSubmitting(true);
    try {
      const counterRef = db.doc('counters/orderNumber');
      let orderNumber;
      try {
        orderNumber = await db.runTransaction(async (tx) => {
          const counterDoc = await tx.get(counterRef);
          const current = counterDoc.exists ? (counterDoc.data()?.current || 0) : 0;
          const next = current + 1;
          tx.set(counterRef, { current: next }, { merge: true });
          return 'WO-' + new Date().getFullYear() + '-' + String(next).padStart(4, '0');
        });
      } catch { orderNumber = 'WO-' + Date.now(); }

      const payload = buildOrderPayload({ status: 'new', includePhotos: false });
      const writePayload = {
        ...payload,
        orderNumber,
        statusHistory: [
          ...(Array.isArray(data.statusHistory) ? data.statusHistory : []),
          { status: 'new', timestamp: new Date(), changedBy: 'vendor', changedByName: vendor.name, method: 'manual' },
        ],
        updatedAt: firebase.firestore.FieldValue.serverTimestamp(),
      };

      let orderId;
      if (draftId) {
        // Convert the existing draft in place — no orphan draft left behind.
        await db.collection('orders').doc(draftId).set(writePayload, { merge: true });
        orderId = draftId;
      } else {
        const ref = await db.collection('orders').add({
          ...writePayload,
          createdAt: firebase.firestore.FieldValue.serverTimestamp(),
        });
        orderId = ref.id;
      }
      writeBackVendorContact();
      setSubmitted({ orderId, orderNumber, estimate: payload.estimate });
    } catch (err) {
      alert('Failed to submit: ' + (err.message || 'Unknown error'));
    } finally {
      setSubmitting(false);
    }
  };

  // Called by the InsuranceWizard on Generate. Records the markup
  // history on the draft and persists in the background.
  const commitMarkup = (percent) => {
    const total = computeEstimate(data, pricing).total || 0;
    const markedUpTotal = total * (1 + (percent || 0) / 100);
    const newEntry = {
      percent: percent || 0,
      markedUpTotal,
      preparedAt: new Date().toISOString(),
      preparedBy: data.vendorContact || vendor.name,
    };
    setData({
      ...data,
      insuranceMarkupPercent: percent || 0,
      markupHistory: [...(data.markupHistory || []), newEntry],
    });
    saveDraft({ silent: true }).catch(() => {});
  };

  // Submitted — show confirmation
  if (submitted) {
    return (
      <div className="vendor-form-wrap">
        <SubmittedStep data={data} estimate={submitted.estimate} submitResult={submitted}
          onReset={() => { setData(applyVendorToData(blankData(), vendor)); setSubmitted(null); }} />
      </div>
    );
  }

  // Wizard for new orders; the long-form scroll page is replaced.
  // Drafts re-enter the wizard at step 1 with all fields prefilled —
  // a summary-page UI for drafts can come later.
  if (draftLoading) {
    return <div className="app-loading"><p>Loading draft…</p></div>;
  }

  return (
    <>
      <IntakeWizard
        vendor={vendor}
        data={data}
        setData={setData}
        pricing={pricing}
        sc={window.PC_SHOP_CONSTANTS}
        submitting={submitting}
        savingDraft={savingDraft}
        onSubmit={() => setShowChecklist(true)}
        onSaveDraft={() => saveDraft()}
        onInsurancePreview={() => { window.scrollTo(0, 0); setShowMarkupModal(true); }}
      />

      {showChecklist && (
        <ChecklistModal onClose={() => setShowChecklist(false)} onProceed={() => { setShowChecklist(false); handleSubmit(); }}/>
      )}

      {showMarkupModal && (
        <InsuranceWizard
          initialPercent={data.insuranceMarkupPercent || 0}
          historyCount={(data.markupHistory || []).length}
          vendor={vendor}
          data={data}
          pricing={pricing}
          onClose={() => setShowMarkupModal(false)}
          onCommitMarkup={commitMarkup}
        />
      )}
    </>
  );
}

// ============================================================
// Legacy long-form NewOrderPage body — preserved as a separate
// component for now in case we need to fall back. Not rendered.
// ============================================================
function _LegacyNewOrderForm({ data, update, cabinets, addCabinet, removeCabinet, updateCabinet, updateToeKick, tk, estimate, canSubmit, submitting, setShowChecklist, savingDraft, saveDraft, setShowMarkupModal, autoSavedAt, draftId }) {
  return (
    <div className="vendor-form-wrap">
      {/* Your Info */}
      <div className="v-section">
        <h2 className="v-section-title">Your Info</h2>
        <div className="v-fields">
          <div className="v-field"><label>Contact person</label><input className="input" value={data.vendorContact || ''} onChange={e => update('vendorContact', e.target.value)} placeholder="Point of contact"/></div>
          <div className="v-field"><label>Phone</label><input className="input mono" value={data.vendorPhone || ''} onChange={e => update('vendorPhone', e.target.value)} placeholder="(480) 555-0100"/></div>
          <div className="v-field"><label>Email</label><input className="input" value={data.vendorEmail || ''} onChange={e => update('vendorEmail', e.target.value)} placeholder="pm@company.com"/></div>
          <div className="v-field"><label>PO / job ref</label><input className="input mono" value={data.vendorPO || ''} onChange={e => update('vendorPO', e.target.value)} placeholder="Your PO or job number"/></div>
        </div>
      </div>

      {/* Customer */}
      <div className="v-section">
        <h2 className="v-section-title">Customer</h2>
        <div className="v-fields">
          <div className="v-field"><label>Customer name <span className="req">*</span></label><input className="input" value={data.clientName || ''} onChange={e => update('clientName', e.target.value)} placeholder="Homeowner name"/></div>
          <div className="v-field"><label>Phone</label><input className="input mono" value={data.clientPhone || ''} onChange={e => update('clientPhone', e.target.value)}/></div>
          <div className="v-field"><label>Email</label><input className="input" value={data.clientEmail || ''} onChange={e => update('clientEmail', e.target.value)}/></div>
          <div className="v-field">
            <label>Preferred contact</label>
            <div className="segmented">{['Phone', 'Text', 'Email'].map(opt => (
              <button key={opt} className={data.preferredContact === opt ? 'on' : ''} onClick={() => update('preferredContact', opt)}>{opt}</button>
            ))}</div>
          </div>
        </div>
      </div>

      {/* Project Site */}
      <div className="v-section">
        <h2 className="v-section-title">Project Site</h2>
        <div className="v-fields">
          <div className="v-field full"><label>Address <span className="req">*</span></label><input className="input" value={data.address || ''} onChange={e => update('address', e.target.value)} placeholder="Street, city, state, ZIP"/></div>
          <div className="v-field">
            <label>Install needed?</label>
            <div className="segmented">{['Yes', 'No', 'Unsure'].map(opt => (
              <button key={opt} className={data.installNeeded === opt ? 'on' : ''} onClick={() => update('installNeeded', opt)}>{opt}</button>
            ))}</div>
          </div>
          <div className="v-field">
            <label>Home type</label>
            <div className="segmented">{['Single fam.', 'Condo', 'Rental', 'Commercial'].map(opt => (
              <button key={opt} className={data.homeType === opt ? 'on' : ''} onClick={() => update('homeType', opt)}>{opt}</button>
            ))}</div>
          </div>
          <div className="v-field full"><label>Access notes</label><textarea className="textarea" value={data.accessNotes || ''} onChange={e => update('accessNotes', e.target.value)} placeholder="Gate code, pets, parking…"/></div>
          <div className="v-field"><label>Completion requested</label><input className="input mono" type="date" value={data.completionRequested || ''} onChange={e => update('completionRequested', e.target.value)}/></div>
          <div className="v-field">
            <label>Priority</label>
            <div className="chips">{[
              { v: 'asap', l: 'ASAP' }, { v: '2wk', l: '2 weeks' }, { v: '1mo', l: '1 month' }, { v: '3mo', l: '3 months' }, { v: 'flex', l: 'Flexible' },
            ].map(o => (
              <button key={o.v} className={'chip' + (data.timeframe === o.v ? ' on' : '')} onClick={() => update('timeframe', o.v)}><span className="dot"></span>{o.l}</button>
            ))}</div>
          </div>
        </div>
      </div>

      {/* Cabinets */}
      <div className="v-section">
        <div className="v-section-header">
          <h2 className="v-section-title">Cabinets</h2>
          <button className="btn primary sm" onClick={addCabinet}><Icon.Plus /> Add</button>
        </div>
        {cabinets.map((cab, idx) => (
          <CabinetCard key={cab.id} cab={cab} idx={idx} total={cabinets.length}
            onChange={(patch) => updateCabinet(idx, patch)}
            onRemove={() => removeCabinet(idx)} />
        ))}
      </div>

      {/* Toe Kick */}
      <div className="v-section">
        <h2 className="v-section-title">Toe Kick</h2>
        <div className="v-fields">
          <DimField label="Toe Kick Height" value={tk.height} unit={tk.heightU} onValue={v => updateToeKick({ height: v })} onUnit={u => updateToeKick({ heightU: u })}/>
          <DimField label="Setback" value={tk.setback} unit={tk.setbackU} onValue={v => updateToeKick({ setback: v })} onUnit={u => updateToeKick({ setbackU: u })}/>
          <div className="v-field">
            <label>Thickness</label>
            <div className="segmented">{['1/4"', '1/2"', '5/8"', '3/4"'].map(opt => (
              <button key={opt} className={tk.thickness === opt ? 'on' : ''} onClick={() => updateToeKick({ thickness: opt })}>{opt}</button>
            ))}</div>
          </div>
          <DimField label="Total length" value={tk.linearFt} onValue={v => updateToeKick({ linearFt: v })}/>
        </div>
      </div>

      {/* Special Instructions */}
      <div className="v-section">
        <h2 className="v-section-title">Special Instructions</h2>
        <textarea className="textarea" value={data.specialInstructions || ''} onChange={e => update('specialInstructions', e.target.value)}
          placeholder="Color match notes, hardware preferences, anything else…" style={{ minHeight: 80 }}/>
      </div>

      {/* Estimate + Submit */}
      <div className="v-section v-estimate">
        <div className="v-estimate-row">
          <span>Estimate</span>
          <span className="mono" style={{ fontSize: 24, fontWeight: 700 }}>{fmtMoney(estimate.total)}</span>
        </div>
        {estimate.rushApplied && <div className="v-estimate-note">Rush priority applied</div>}
        <button className="btn primary lg" style={{ width: '100%', marginTop: 16 }}
          onClick={() => setShowChecklist(true)} disabled={!canSubmit || submitting}>
          {submitting ? 'Submitting...' : 'Submit Order'}
        </button>
        <div style={{ display: 'flex', gap: 8, marginTop: 8, flexWrap: 'wrap' }}>
          <button
            className="btn"
            style={{
              flex: '1 1 160px',
              background: '#fff',
              color: '#0a2240',
              border: '2px solid #0a2240',
              fontWeight: 600,
            }}
            onClick={() => saveDraft()}
            disabled={savingDraft || submitting}
          >
            {savingDraft ? 'Saving…' : draftId ? '💾 Save Draft' : '💾 Save as Draft'}
          </button>
          <button
            className="btn"
            style={{
              flex: '1 1 160px',
              background: '#b08720',
              color: '#fff',
              border: 0,
              fontWeight: 600,
            }}
            onClick={() => setShowMarkupModal(true)}
            disabled={!cabinets.length || submitting}
          >
            📄 Insurance Estimate
          </button>
        </div>
        {autoSavedAt && (
          <div className="hint" style={{ textAlign: 'center', marginTop: 6, fontSize: 11 }}>
            Auto-saved {autoSavedAt.toLocaleTimeString()}
          </div>
        )}
        {!canSubmit && <div className="hint" style={{ textAlign: 'center', marginTop: 8 }}>Customer name, address, and at least one cabinet required</div>}
      </div>

      {showChecklist && (
        <ChecklistModal onClose={() => setShowChecklist(false)} onProceed={() => { setShowChecklist(false); handleSubmit(); }}/>
      )}

      {showMarkupModal && (
        <MarkupModal
          initialPercent={data.insuranceMarkupPercent || 0}
          historyCount={(data.markupHistory || []).length}
          onClose={() => setShowMarkupModal(false)}
          onConfirm={(p) => { setShowMarkupModal(false); handleInsurancePdf(p); }}
        />
      )}

      {pdfPreview && (
        <InsurancePdfPreview
          html={pdfPreview.html}
          onClose={() => setPdfPreview(null)}
        />
      )}
    </div>
  );
}

// In-page PDF preview. Renders the estimate HTML inside a same-origin
// iframe (srcdoc) so the user can scroll, print, or save without ever
// triggering a popup. Print and Open-in-new-tab buttons live in the
// modal toolbar, not inside the iframe content, because the iframe's
// own print toolbar is hidden when displayed in a modal.
function InsurancePdfPreview({ html, onClose }) {
  const iframeRef = React.useRef(null);

  const printIt = () => {
    const f = iframeRef.current;
    if (!f) return;
    try {
      f.focus();
      f.contentWindow.print();
    } catch (err) {
      alert('Print failed: ' + (err.message || 'unknown'));
    }
  };

  const openInNewTab = () => {
    try {
      const blob = new Blob([html], { type: 'text/html' });
      const url = URL.createObjectURL(blob);
      const w = window.open(url, '_blank');
      if (!w) {
        // Popup blocked — fall back to a download. Browsers don't
        // block direct downloads triggered by a click.
        const a = document.createElement('a');
        a.href = url;
        a.download = 'precision-cabinets-estimate.html';
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
      }
    } catch (err) {
      alert('Open failed: ' + (err.message || 'unknown'));
    }
  };

  // Native share sheet — uses the OS share menu so the vendor can
  // send the estimate via Mail, Messages, AirDrop, etc. directly
  // from their phone. Falls back to download if not supported.
  const shareIt = async () => {
    try {
      const blob = new Blob([html], { type: 'text/html' });
      const file = new File([blob], 'precision-cabinets-estimate.html', { type: 'text/html' });
      if (navigator.canShare && navigator.canShare({ files: [file] })) {
        await navigator.share({
          title: 'Precision Cabinets Estimate',
          text: 'Marked-up insurance estimate',
          files: [file],
        });
        return;
      }
      // No file-share support — share a URL instead.
      if (navigator.share) {
        const url = URL.createObjectURL(blob);
        await navigator.share({ title: 'Precision Cabinets Estimate', url });
        return;
      }
      openInNewTab();
    } catch (err) {
      if (err && err.name !== 'AbortError') {
        alert('Share failed: ' + (err.message || 'unknown'));
      }
    }
  };

  return (
    <div
      style={{
        position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', zIndex: 1000,
        display: 'flex', flexDirection: 'column',
      }}
    >
      <div
        style={{
          background: '#0a2240', color: '#fff', padding: '10px 14px',
          display: 'flex', alignItems: 'center', gap: 10, justifyContent: 'space-between',
          flexWrap: 'wrap',
        }}
      >
        <div style={{ fontWeight: 600 }}>Insurance Estimate Preview</div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <button
            className="btn"
            style={{ background: '#b08720', color: '#fff', border: 0, fontWeight: 600 }}
            onClick={printIt}
          >
            🖨️ Print / Save as PDF
          </button>
          <button
            className="btn"
            style={{ background: '#fff', color: '#0a2240', border: 0, fontWeight: 600 }}
            onClick={shareIt}
          >
            📤 Share / Save
          </button>
          <button
            className="btn"
            style={{ background: 'transparent', color: '#fff', border: '1px solid rgba(255,255,255,0.4)' }}
            onClick={openInNewTab}
          >
            ↗ New tab
          </button>
          <button
            className="btn"
            style={{ background: 'transparent', color: '#fff', border: '1px solid rgba(255,255,255,0.4)' }}
            onClick={onClose}
          >
            ✕ Close
          </button>
        </div>
      </div>
      <iframe
        ref={iframeRef}
        title="Insurance Estimate"
        srcDoc={html}
        style={{ flex: 1, width: '100%', border: 0, background: '#fff' }}
      />
    </div>
  );
}

// Insurance Estimate Wizard — three-step modal that walks the vendor
// through (1) what this PDF is for, (2) picking a markup %, (3)
// previewing/sharing the generated PDF. Replaces the previous bare
// chip-selector. Always rendered as a fullscreen takeover so the
// vendor can't lose track of where the form is on the page.
function InsuranceWizard({ initialPercent, historyCount, vendor, data, pricing, onClose, onCommitMarkup }) {
  const [step, setStep] = useS('intro');             // 'intro' | 'pick' | 'preview'
  const [percent, setPercent] = useS(initialPercent || 0);
  const [html, setHtml] = useS(null);
  const options = [0, 5, 10, 15, 20, 25, 30, 35, 40];
  const iframeRef = React.useRef(null);

  const generate = () => {
    if (!window.buildInsuranceEstimateHtml) {
      alert('Insurance PDF generator not loaded — refresh the page.');
      return;
    }
    // Tell the parent to record the markup history + persist the draft.
    onCommitMarkup(percent);
    const generated = window.buildInsuranceEstimateHtml({
      vendor, data, pricing, markupPercent: percent || 0,
    });
    setHtml(generated);
    setStep('preview');
  };

  const printIt = () => {
    const f = iframeRef.current;
    if (!f) return;
    try { f.focus(); f.contentWindow.print(); } catch (err) { alert('Print failed: ' + err.message); }
  };
  const shareIt = async () => {
    if (!html) return;
    try {
      const blob = new Blob([html], { type: 'text/html' });
      const file = new File([blob], 'precision-cabinets-estimate.html', { type: 'text/html' });
      if (navigator.canShare && navigator.canShare({ files: [file] })) {
        await navigator.share({
          title: 'Precision Cabinets Estimate',
          text: 'Marked-up insurance estimate',
          files: [file],
        });
        return;
      }
      if (navigator.share) {
        const url = URL.createObjectURL(blob);
        await navigator.share({ title: 'Precision Cabinets Estimate', url });
        return;
      }
      // Fall back to download.
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'precision-cabinets-estimate.html';
      document.body.appendChild(a); a.click(); document.body.removeChild(a);
    } catch (err) {
      if (err && err.name !== 'AbortError') alert('Share failed: ' + err.message);
    }
  };

  return (
    <div className="iw-fullscreen" role="dialog" aria-modal="true">
      <div className="iw-header">
        <div style={{ fontWeight: 600 }}>📄 Insurance Estimate Tool</div>
        <button className="btn"
                style={{ background: 'transparent', color: '#fff', border: '1px solid rgba(255,255,255,0.4)' }}
                onClick={onClose}>✕ Close</button>
      </div>

      {step === 'intro' && (
        <div className="iw-body">
          <div className="iw-intro-card">
            <h2 style={{ marginTop: 0, color: '#0a2240' }}>What is this?</h2>
            <p>
              The Insurance Estimate is a Precision-branded PDF you can hand to a homeowner's
              insurance adjuster. It shows the cabinetry repair as a single clean total, broken into
              <strong> Materials</strong> and <strong>Labor</strong> at a per-linear-foot rate.
            </p>
            <h3 style={{ color: '#b08720' }}>What it does for you</h3>
            <ul>
              <li>Lets you add a markup percentage (0–40%) to your Precision contract price so you can build in your own margin.</li>
              <li>Generates a polished PDF on Precision Cabinets letterhead so it reads as a real cabinet-shop estimate to the adjuster.</li>
              <li>Hides Precision's contract-discount price — the adjuster only sees the marked-up total.</li>
              <li>Includes thumbnails of the cabinet photos you uploaded (when available) so the scope of work is obvious.</li>
            </ul>
            <h3 style={{ color: '#b08720' }}>What Precision does with it</h3>
            <ul>
              <li>Every estimate you generate is logged on this draft so we have a copy for verification.</li>
              <li>If the adjuster calls Precision, we can confirm the exact estimate amount and markup % you handed out.</li>
              <li>Once the adjuster approves, you submit the same draft as a real order at our true contract price — no double entry.</li>
            </ul>
            <p style={{ fontStyle: 'italic', color: 'var(--ink-soft)', fontSize: 13 }}>
              Estimate is non-binding until the customer accepts and Precision verifies the scope after build.
            </p>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 20 }}>
              <button className="btn ghost" onClick={onClose}>Not now</button>
              <button className="btn primary lg" onClick={() => setStep('pick')}>Continue →</button>
            </div>
          </div>
        </div>
      )}

      {step === 'pick' && (
        <div className="iw-body">
          <div className="iw-intro-card">
            <h2 style={{ marginTop: 0, color: '#0a2240' }}>Pick your markup</h2>
            <p>
              How much margin do you want to add on top of Precision's contract price? Pick any 5%
              increment from 0–40%.
            </p>
            {historyCount > 0 && (
              <p style={{ fontSize: 13, color: 'var(--ink-soft)' }}>
                You've generated {historyCount} estimate{historyCount === 1 ? '' : 's'} on this draft already. Each new one is recorded on top of the others.
              </p>
            )}
            <div className="chips" style={{ marginTop: 12, fontSize: 16 }}>
              {options.map(p => (
                <button
                  key={p}
                  className={'chip' + (percent === p ? ' on' : '')}
                  onClick={() => setPercent(p)}
                  style={{ minWidth: 72 }}
                >
                  <span className="dot"></span>{p}%
                </button>
              ))}
            </div>
            <div className="hint" style={{ fontSize: 12, marginTop: 12 }}>
              Capped at 40%. Generating the PDF saves your draft and logs the markup % to your draft history (visible to Precision admins).
            </div>
            <div style={{ display: 'flex', gap: 8, justifyContent: 'space-between', marginTop: 20 }}>
              <button className="btn ghost" onClick={() => setStep('intro')}>← Back</button>
              <button className="btn primary lg" onClick={generate}>Generate PDF →</button>
            </div>
          </div>
        </div>
      )}

      {step === 'preview' && (
        <>
          <div className="iw-toolbar">
            <button className="btn"
                    style={{ background: '#b08720', color: '#fff', border: 0, fontWeight: 600 }}
                    onClick={printIt}>🖨️ Print / Save PDF</button>
            <button className="btn"
                    style={{ background: '#fff', color: '#0a2240', border: 0, fontWeight: 600 }}
                    onClick={shareIt}>📤 Share / Send</button>
            <button className="btn ghost"
                    style={{ background: 'rgba(255,255,255,0.1)', color: '#fff', border: 0 }}
                    onClick={() => setStep('pick')}>← Adjust markup</button>
          </div>
          <iframe
            ref={iframeRef}
            title="Insurance Estimate"
            srcDoc={html || ''}
            style={{ flex: 1, width: '100%', border: 0, background: '#fff' }}
          />
        </>
      )}
    </div>
  );
}

// ============================================================
// Cabinet size presets — exhaustive list, grouped by type
// Format: B36 = Base 36×34.5×24, W3030 = Wall 30×30×12, etc.
// ============================================================
const CABINET_PRESETS = (() => {
  const list = [];
  const baseWidths = [9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 42, 48, 60];
  const wallWidths = [9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 42, 48];
  const wallHeights = [12, 15, 18, 24, 30, 36, 42];
  const wallDepths = [12, 15];
  const tallWidths = [18, 24, 30, 36];
  const tallHeights = [84, 90, 96];

  // Bases: standard 34.5H × 24D
  baseWidths.forEach(w => {
    list.push({ code: `B${w}`, type: 'Base', w: String(w), h: '34.5', d: '24', desc: `${w}×34.5×24` });
  });

  // Walls: various heights × 12D and 15D
  wallWidths.forEach(w => {
    wallHeights.forEach(wh => {
      wallDepths.forEach(wd => {
        const code = `W${w}${wh}${wd !== 12 ? 'x' + wd : ''}`;
        list.push({ code, type: 'Wall', w: String(w), h: String(wh), d: String(wd), desc: `${w}×${wh}×${wd}` });
      });
    });
  });

  // Tall/Pantry
  tallWidths.forEach(w => {
    tallHeights.forEach(th => {
      list.push({ code: `T${w}${th}`, type: 'Tall', w: String(w), h: String(th), d: '24', desc: `${w}×${th}×24` });
    });
  });

  // Drawer bases: standard 34.5H × 24D (common 3-drawer configs)
  [12, 15, 18, 24, 30, 36].forEach(w => {
    list.push({ code: `DB${w}`, type: 'Drawer Base', w: String(w), h: '34.5', d: '24', desc: `${w}×34.5×24` });
  });

  // Sink bases
  [30, 33, 36, 42].forEach(w => {
    list.push({ code: `SB${w}`, type: 'Sink Base', w: String(w), h: '34.5', d: '24', desc: `${w}×34.5×24` });
  });

  // Lazy susan / corner bases
  [33, 36].forEach(w => {
    list.push({ code: `LS${w}`, type: 'Lazy Susan', w: String(w), h: '34.5', d: String(w), desc: `${w}×34.5×${w}` });
  });

  return list;
})();

const PRESET_TYPES = ['Base', 'Wall', 'Drawer Base', 'Sink Base', 'Tall', 'Lazy Susan'];

// Expose for the intake wizard so it can render the same preset
// data in its own (taller) grid layout instead of the small dropdown.
Object.assign(window, { CABINET_PRESETS, PRESET_TYPES });

function CabinetSizePreset({ cab, onSelect }) {
  const [open, setOpen] = React.useState(false);
  const [activeType, setActiveType] = React.useState('Base');

  const currentCode = CABINET_PRESETS.find(p => p.w === cab.w && p.h === cab.h && p.d === cab.d)?.code || null;

  const filtered = CABINET_PRESETS.filter(p => p.type === activeType);

  return (
    <div className="size-preset-wrap">
      <button className="size-preset-trigger" onClick={() => setOpen(!open)}>
        <span className="size-preset-value">
          {currentCode ? (
            <><span className="size-preset-code">{currentCode}</span> <span className="size-preset-dims">{cab.w}×{cab.h}×{cab.d}</span></>
          ) : (
            <span className="size-preset-placeholder">Select a standard size…</span>
          )}
        </span>
        <span className="size-preset-arrow">{open ? '▴' : '▾'}</span>
      </button>

      {/* Backdrop — click anywhere outside to close */}
      {open && <div className="size-preset-backdrop" onClick={() => setOpen(false)} />}

      {open && (
        <div className="size-preset-dropdown">
          <div className="size-preset-types">
            {PRESET_TYPES.map(t => (
              <button key={t} className={'size-preset-type-btn' + (activeType === t ? ' on' : '')}
                onClick={() => setActiveType(t)}>{t}</button>
            ))}
          </div>
          <div className="size-preset-grid">
            {filtered.map(p => (
              <button key={p.code} className={'size-preset-item' + (currentCode === p.code ? ' on' : '')}
                onClick={() => { onSelect(p.w, p.h, p.d); setOpen(false); }}>
                <span className="size-preset-item-code">{p.code}</span>
                <span className="size-preset-item-dims">{p.desc}</span>
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ============================================================
// CabinetCard — collapsible cabinet in the single-page flow
// ============================================================
function CabinetCard({ cab, idx, total, onChange, onRemove }) {
  const [open, setOpen] = useS(idx === 0);
  const uf = (k, v) => onChange({ [k]: v });

  return (
    <div className="v-cab-card">
      <button className="v-cab-header" onClick={() => setOpen(!open)}>
        <span className="v-cab-num">Cab {String(idx + 1).padStart(2, '0')}</span>
        <span className="v-cab-label">{cab.name || 'Untitled'}</span>
        <span className="v-cab-dims mono">{cab.w && cab.h ? `${cab.w}×${cab.h}` : '—'}</span>
        <span className="v-cab-chevron">{open ? '▾' : '▸'}</span>
      </button>
      {open && (
        <div className="v-cab-body">
          <div className="v-field full">
            <label>Cabinet name</label>
            <input className="input" value={cab.name || ''} onChange={e => uf('name', e.target.value)} placeholder="e.g. Sink base, Upper L"/>
          </div>

          {/* Photo */}
          <div className="v-field full">
            <label>Photo</label>
            {!cab.photo ? (
              <div style={{ display: 'flex', gap: 8 }}>
                <PhotoCaptureButton kind="camera" onSet={(src) => uf('photo', src)}/>
                <PhotoCaptureButton kind="upload" onSet={(src) => uf('photo', src)}/>
              </div>
            ) : (
              <div>
                <MarkupCanvas photo={cab.photo} annotations={cab.annotations || []} setAnnotations={(ann) => uf('annotations', ann)} />
                <button className="btn ghost" style={{ marginTop: 6 }} onClick={() => { uf('photo', null); uf('annotations', []); }}><Icon.X/> Replace</button>
              </div>
            )}
          </div>

          {/* Quick size preset dropdown */}
          <div className="v-field full">
            <label>Quick size</label>
            <CabinetSizePreset cab={cab} onSelect={(w, h, d) => onChange({ w, wu: 'in', h, hu: 'in', d, du: 'in' })} />
          </div>

          {/* Dimensions — individual with quickpicks */}
          <div className="v-fields">
            <DimField label="Width" value={cab.w} unit={cab.wu} onValue={v => uf('w', v)} onUnit={u => uf('wu', u)}/>
            <DimField label="Height" value={cab.h} unit={cab.hu} onValue={v => uf('h', v)} onUnit={u => uf('hu', u)}/>
            <DimField label="Depth" value={cab.d} unit={cab.du} onValue={v => uf('d', v)} onUnit={u => uf('du', u)}/>
          </div>

          {/* Components */}
          <div className="v-fields">
            <div className="v-field"><label>Doors</label><NumberStepper value={cab.doors} onChange={v => uf('doors', v)}/></div>
            <div className="v-field"><label>Drawers</label><NumberStepper value={cab.drawers} onChange={v => uf('drawers', v)}/></div>
            <div className="v-field"><label>Shelves</label><NumberStepper value={cab.shelves} onChange={v => uf('shelves', v)}/></div>
          </div>

          {/* Drawer layout — only shown when 2+ drawers. Lets the shop
              know if all drawers are equal, the bottom is taller, or
              custom heights apply. The cut sheet uses this to compute
              face-frame openings + drawer-box dimensions. */}
          {parseInt(String(cab.drawers || 0), 10) >= 2 && (
            <div className="v-field full">
              <label>Drawer layout</label>
              <div className="chips">
                {[
                  { v: '', l: 'Equal' },
                  { v: 'deep-bottom', l: 'Deep bottom' },
                  ...(parseInt(String(cab.drawers || 0), 10) === 4 ? [{ v: '2up-2down', l: '2 up + 2 down' }] : []),
                  { v: 'custom', l: 'Custom…' },
                ].map(o => (
                  <button
                    key={o.v}
                    className={'chip' + ((cab.drawerLayout || '') === o.v ? ' on' : '')}
                    onClick={() => uf('drawerLayout', o.v)}
                  >
                    <span className="dot"></span>{o.l}
                  </button>
                ))}
              </div>
              {cab.drawerLayout === 'custom' && (
                <div style={{ marginTop: 8 }}>
                  <input
                    className="input mono"
                    placeholder={`Heights in inches, comma-separated (e.g. 6, 6, 8, 14)`}
                    value={cab.drawerHeights || ''}
                    onChange={e => uf('drawerHeights', e.target.value)}
                  />
                  <div className="hint" style={{ fontSize: 11, marginTop: 4 }}>
                    Sum should match the drawer-bank opening height. Order top → bottom.
                  </div>
                </div>
              )}
            </div>
          )}

          {/* Finished side */}
          <div className="v-fields">
            <div className="v-field">
              <label>Finished side?</label>
              <div className="segmented">{['Yes', 'No'].map(opt => (
                <button key={opt} className={cab.finishedSide === opt ? 'on' : ''} onClick={() => uf('finishedSide', opt)}>{opt}</button>
              ))}</div>
            </div>
            {cab.finishedSide === 'Yes' && (
              <div className="v-field">
                <label>Which side?</label>
                <div className="segmented">{['Left', 'Right', 'Both'].map(opt => (
                  <button key={opt} className={cab.finishedWhich === opt ? 'on' : ''} onClick={() => uf('finishedWhich', opt)}>{opt}</button>
                ))}</div>
              </div>
            )}
          </div>

          {/* Material */}
          <div className="v-fields">
            <div className="v-field">
              <label>Box material</label>
              <select className="select" value={cab.boxMaterial || ''} onChange={e => uf('boxMaterial', e.target.value)}>
                <option value="">Select…</option>
                {BOX_MATERIALS.map(m => <option key={m}>{m}</option>)}
              </select>
            </div>
            <div className="v-field">
              <label>Thickness</label>
              <div className="segmented">{['1/2"', '5/8"', '3/4"'].map(opt => (
                <button key={opt} className={cab.boxThickness === opt ? 'on' : ''} onClick={() => uf('boxThickness', opt)}>{opt}</button>
              ))}</div>
            </div>
          </div>

          {/* Type */}
          <div className="v-field full">
            <label>Cabinet type</label>
            <div className="segmented">{['Standard wall', 'Island', 'Peninsula'].map(opt => (
              <button key={opt} className={cab.cabType === opt ? 'on' : ''} onClick={() => uf('cabType', opt)}>{opt}</button>
            ))}</div>
          </div>

          {/* Under-counter install — base cabinets going under an
              existing countertop need a shorter box plus adjustable
              leveling feet. The shop uses this to deduct counter
              thickness from the box height and add feet to hardware. */}
          <div className="v-field full">
            <label>Installs under existing countertop?</label>
            <div className="segmented">{['No', 'Yes'].map(opt => (
              <button
                key={opt}
                className={(cab.installUnderExisting || 'No') === opt ? 'on' : ''}
                onClick={() => uf('installUnderExisting', opt)}
              >{opt}</button>
            ))}</div>
            {cab.installUnderExisting === 'Yes' && (
              <div className="hint" style={{ fontSize: 11, marginTop: 4 }}>
                Box height is reduced and adjustable feet are added so the cabinet slides under the existing counter.
              </div>
            )}
          </div>

          {/* Notes */}
          <div className="v-field full">
            <label>Notes</label>
            <textarea className="textarea" value={cab.notes || ''} onChange={e => uf('notes', e.target.value)} placeholder="Damage, finish details, hardware…"/>
          </div>

          {total > 1 && (
            <button className="btn ghost danger-hover" style={{ marginTop: 8 }} onClick={onRemove}><Icon.Trash/> Remove cabinet</button>
          )}
        </div>
      )}
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<AppRoot/>);
