/* SER-OS — App: top-level state machine + screen routing */

/* ─────────────────────────────────────────────────────────────────────
   Odoo send processing view — shown inside the Send modal while the
   prototype simulates the three-step workflow:
     Step 1 "generating" → Generating Service Summary Form…
     Step 2 "snapshot"   → Preparing final arrangement snapshot…
     Step 3 "sending"    → Sending to Odoo for signature…
   All steps are always visible; they reveal their state progressively
   (pending → active → done) so the FD can see the full workflow.
   ───────────────────────────────────────────────────────────────────── */
function SendOdooProcessingView({ step }) {
  // step: "generating" | "snapshot" | "sending"
  const step1State = step === "generating" ? "active" : "done";
  const step2State = step === "generating" ? "pending" : step === "snapshot" ? "active" : "done";
  const step3State = step === "sending"    ? "active" : "pending";

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 24, alignItems: "center", padding: "8px 0 4px" }}>
      {/* Central spinner */}
      <div style={{ position: "relative", width: 56, height: 56, display: "flex", alignItems: "center", justifyContent: "center" }}>
        {/* Outer ring — always spinning gently */}
        <div style={{
          position: "absolute", inset: 0, borderRadius: "50%",
          border: "3px solid #E5E7EB", borderTopColor: "#1F3A60",
          animation: "seros-spin 0.85s linear infinite",
        }} />
        {/* Inner icon */}
        <span style={{ color: "#1F3A60", display: "flex", zIndex: 1 }}>
          <SerIcons.Document size={22} />
        </span>
      </div>

      {/* FD App → Odoo → Customer flow — Odoo highlights on step 3 */}
      <div style={{
        display: "flex", alignItems: "center", gap: 6,
        padding: "8px 16px", borderRadius: 24,
        background: "#F0F4F8", border: "1px solid #E5E7EB",
        fontSize: 12, fontWeight: 500, color: "#6B7A8F", letterSpacing: "-0.1px",
      }}>
        <span style={{ color: "#1F3A60" }}>FD App</span>
        <SerIcons.Arrow size={14} color="#9CA8B8" />
        <span style={{ color: step === "sending" ? "#1F3A60" : "#9CA8B8", transition: "color 200ms ease" }}>Odoo</span>
        <SerIcons.Arrow size={14} color="#9CA8B8" />
        <span style={{ color: "#9CA8B8" }}>Customer</span>
      </div>

      {/* Step indicators */}
      <div style={{ width: "100%", display: "flex", flexDirection: "column", gap: 8 }}>
        <SendOdooStep number="1" label="Generating Service Summary Form…"       state={step1State} />
        <SendOdooStep number="2" label="Preparing final arrangement snapshot…"  state={step2State} />
        <SendOdooStep number="3" label="Sending to Odoo for signature…"         state={step3State} />
      </div>

      <p style={{ margin: 0, fontSize: 13, color: "#9CA8B8", letterSpacing: "-0.15px", textAlign: "center" }}>
        Please wait — this takes just a moment
      </p>
    </div>
  );
}

function SendOdooStep({ number, label, state }) {
  // state: "pending" | "active" | "done"
  const cfg = {
    pending: { bg: "#FAFAFA",   border: "#E5E7EB", fg: "#9CA8B8", weight: 400 },
    active:  { bg: "#F0F4F8",   border: "#CBD5E1", fg: "#1F3A60", weight: 500 },
    done:    { bg: "#E8F0E4",   border: "#C4D9B8", fg: "#5B7D4F", weight: 500 },
  }[state];
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 12,
      padding: "11px 14px", borderRadius: 8,
      background: cfg.bg, border: `1px solid ${cfg.border}`,
      transition: "background 280ms ease, border-color 280ms ease",
    }}>
      {/* State indicator */}
      {state === "done" ? (
        <span style={{ width: 20, height: 20, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center" }}>
          <SerIcons.Check size={14} color="#5B7D4F" />
        </span>
      ) : state === "active" ? (
        <span style={{
          width: 18, height: 18, borderRadius: "50%", flexShrink: 0,
          border: "2.5px solid #CBD5E1", borderTopColor: "#1F3A60",
          animation: "seros-spin 0.75s linear infinite",
          display: "inline-block",
        }} />
      ) : (
        <span style={{
          width: 20, height: 20, borderRadius: "50%", flexShrink: 0,
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          background: "#E5E7EB",
          fontSize: 11, fontWeight: 600, color: "#9CA8B8",
        }}>{number}</span>
      )}
      <span style={{
        fontSize: 14, fontWeight: cfg.weight, color: cfg.fg,
        letterSpacing: "-0.15px", transition: "color 280ms ease",
      }}>
        {label}
      </span>
    </div>
  );
}

/* ─────────────────────────────────────────────────────────────────────
   SEED_CASE_DATA — pre-populated service data + add-ons for demo cases.
   Keyed by caseNumber. Merged into the open-case handler so confirmed
   seed cases display realistic, fully-filled-in arrangements.
   ───────────────────────────────────────────────────────────────────── */
const SEED_CASE_DATA = {
  "QT-240517-013": {
    serviceData: {
      salutations:             "Mdm",
      deceasedName:            "Mary Lim Siew Hwa",
      gender:                  "F",
      maritalStatus:           "W",
      religion:                "christian",
      dateOfDeath:             "2026-05-16",
      timeOfDeath:             "04:22",
      dateOfBirth:             "1949-03-18",
      age:                     "77",
      transferLocation:        "Tan Tock Seng Hospital (TTSH)",
      embalmingOptions:        ["embalming_required"],
      embalmingRemarks:        "Family will visit within 24 hours. Prepare viewing with modest light-coloured attire.",
      primaryRelationship:     "Son",
      primaryContactId:        "c002",
      secondaryRelationship:   "Son-in-Law",
      secondaryContactId:      "c018",
      wakeVenue:               "Blk 88 Bishan Street 13 — HDB Void Deck",
      wakeStartDate:           "2026-05-16",
      wakeEndDate:             "2026-05-18",
      wakeDuration:            "3",
      visitingHours:           "10:00 AM – 10:00 PM Daily",
      cremationOrBurial:       "Cremation",
      dispositionDate:         "2026-05-23",
      cremationLocation:       "Mandai Crematorium",
      restingArrangement:      "Columbarium",
      finalRestingLocation:    "Mandai Columbarium",
      finalRestingDate:        "2026-05-23",
      church:                  "Church of St Mary",
      pastor:                  "Reverend James Tan",
      memorialServiceDateTime: "2026-05-18T19:00",
    },
    notes: "Family requests quiet music and minimal ornamentation during the wake. Pastor Tan to deliver Sunday eulogy.",
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"),  quantity: 1,  rentalDays: 3 },  // Chiller × 1 · 3 days
        { ...byId("9"),  quantity: 4,  rentalDays: 3 },  // Standing Fan × 4 · 3 days
        { ...byId("13"), quantity: 8,  rentalDays: 3 },  // Round Table × 8 · 3 days
        { ...byId("22"), quantity: 1 },                  // Photo Montage
        { ...byId("23"), quantity: 1 },                  // Live Streaming
        { ...byId("25"), quantity: 40 },                 // Buffet Catering × 40 pax
        { id: "note-catering-1", name: "Veg + Halal", itemType: "note", quantity: 1 },
        { ...byId("28"), quantity: 1 },                  // Additional Bus
      ];
    })(),
    confirmedAt: "2026-05-25T20:33:00+08:00",
  },

  "QT-260518-019": {
    serviceData: {
      salutations:         "Mr",
      deceasedName:        "Tan Swee Kiat",
      gender:              "M",
      maritalStatus:       "M",
      religion:            "christian",
      dateOfDeath:         "2026-05-15",
      timeOfDeath:         "22:10",
      transferLocation:    "Singapore General Hospital (SGH)",
      embalmingOptions:    ["embalming_required"],
      primaryRelationship: "Daughter",
      primaryContactId:    "c014",
      wakeVenue:           "Blk 42 Bishan Street 22 — HDB Void Deck",
      wakeStartDate:       "2026-05-16",
      wakeEndDate:         "2026-05-18",
      wakeDuration:        "3",
      visitingHours:       "9:00 AM – 9:00 PM Daily",
      cremationOrBurial:   "Cremation",
      dispositionDate:     "2026-05-19",
      cremationLocation:   "Mandai Crematorium",
      restingArrangement:  "Columbarium",
      finalRestingLocation: "Mandai Columbarium",
      church:              "Calvary Church",
      pastor:              "Pastor Andrew Ng",
      memorialServiceDateTime: "2026-05-18T15:00",
    },
    notes: "Family prefers a quiet service. Memorial to be held on the afternoon of 18 May.",
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"), quantity: 1, rentalDays: 3 },   // Chiller × 1 · 3 days
        { ...byId("9"), quantity: 4, rentalDays: 3 },   // Standing Fan × 4 · 3 days
      ];
    })(),
  },

  "QT-260517-020": {
    serviceData: {
      salutations:         "Mdm",
      deceasedName:        "Chua Ah Mooi",
      gender:              "F",
      maritalStatus:       "W",
      religion:            "catholic",
      dateOfDeath:         "2026-05-14",
      timeOfDeath:         "11:45",
      transferLocation:    "Ng Teng Fong General Hospital",
      embalmingOptions:    ["embalming_required"],
      primaryRelationship: "Son",
      primaryContactId:    "c015",
      wakeVenue:           "8 Toh Yi Drive — Private Residence",
      wakeStartDate:       "2026-05-15",
      wakeEndDate:         "2026-05-17",
      wakeDuration:        "3",
      visitingHours:       "10:00 AM – 10:00 PM Daily",
      cremationOrBurial:   "Burial",
      dispositionDate:     "2026-05-18",
      church:              "Church of the Holy Family",
      pastor:              "Father Emmanuel Lau",
      memorialServiceDateTime: "2026-05-17T10:00",
    },
    notes: "Father Emmanuel to lead the rosary each evening. Family requests Catholic rites throughout.",
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"),  quantity: 1, rentalDays: 3 },  // Chiller × 1 · 3 days
        { ...byId("4"),  quantity: 2, rentalDays: 3 },  // Mobile Toilet × 2 · 3 days
        { ...byId("13"), quantity: 6, rentalDays: 3 },  // Round Table × 6 · 3 days
      ];
    })(),
  },

  "QT-260515-021": {
    serviceData: {
      salutations:         "Mdm",
      deceasedName:        "Lee Siew Eng",
      gender:              "F",
      maritalStatus:       "W",
      religion:            "free thinker",
      dateOfDeath:         "2026-05-12",
      timeOfDeath:         "08:30",
      transferLocation:    "Khoo Teck Puat Hospital",
      primaryRelationship: "Son",
      primaryContactId:    "c016",
      wakeVenue:           "Blk 23 Lorong Chuan — HDB Void Deck",
      wakeStartDate:       "2026-05-13",
      wakeEndDate:         "2026-05-15",
      wakeDuration:        "3",
      visitingHours:       "9:00 AM – 9:00 PM Daily",
      cremationOrBurial:   "Cremation",
      dispositionDate:     "2026-05-16",
      cremationLocation:   "Mandai Crematorium",
      restingArrangement:  "Columbarium",
      finalRestingLocation: "Mandai Columbarium",
    },
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"), quantity: 1, rentalDays: 3 },  // Chiller × 1 · 3 days
      ];
    })(),
    confirmedAt: "2026-05-15T17:20:00+08:00",
  },

  "QT-260512-022": {
    serviceData: {
      salutations:         "Mr",
      deceasedName:        "Yap Boon Teik",
      gender:              "M",
      maritalStatus:       "M",
      religion:            "christian",
      dateOfDeath:         "2026-05-09",
      timeOfDeath:         "16:55",
      transferLocation:    "Changi General Hospital",
      embalmingOptions:    ["embalming_required"],
      primaryRelationship: "Wife",
      primaryContactId:    "c017",
      wakeVenue:           "67 Sunset Way — Private Residence",
      wakeStartDate:       "2026-05-10",
      wakeEndDate:         "2026-05-12",
      wakeDuration:        "3",
      visitingHours:       "10:00 AM – 10:00 PM Daily",
      cremationOrBurial:   "Cremation",
      dispositionDate:     "2026-05-13",
      cremationLocation:   "Mount Vernon Crematorium",
      restingArrangement:  "Columbarium",
      finalRestingLocation: "Mount Vernon Columbarium",
      church:              "Bethel Assembly of God",
      pastor:              "Pastor David Koh",
      memorialServiceDateTime: "2026-05-12T14:00",
    },
    notes: "Family requests a worship segment during the memorial service.",
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"),  quantity: 1,  rentalDays: 3 },  // Chiller × 1 · 3 days
        { ...byId("4"),  quantity: 2,  rentalDays: 3 },  // Mobile Toilet × 2 · 3 days
        { ...byId("9"),  quantity: 6,  rentalDays: 3 },  // Standing Fan × 6 · 3 days
        { ...byId("13"), quantity: 10, rentalDays: 3 },  // Round Table × 10 · 3 days
        { ...byId("22"), quantity: 1 },                  // Photo Montage
      ];
    })(),
    confirmedAt: "2026-05-12T19:30:00+08:00",
  },

  "QT-230812-002": {
    serviceData: {
      salutations:         "Mdm",
      deceasedName:        "Tan Bee Lian",
      gender:              "F",
      maritalStatus:       "W",
      religion:            "buddhist",
      dateOfDeath:         "2023-08-09",
      timeOfDeath:         "06:15",
      transferLocation:    "Tan Tock Seng Hospital (TTSH)",
      primaryRelationship: "Daughter",
      primaryContactId:    "c001",
      wakeVenue:           "Blk 88 Tampines Street 22 — HDB Void Deck",
      wakeStartDate:       "2023-08-10",
      wakeEndDate:         "2023-08-12",
      wakeDuration:        "3",
      visitingHours:       "9:00 AM – 9:00 PM Daily",
      cremationOrBurial:   "Cremation",
      dispositionDate:     "2023-08-14",
      cremationLocation:   "Mandai Crematorium",
      restingArrangement:  "Columbarium",
      finalRestingLocation: "Mandai Columbarium",
    },
    addOns: (() => {
      const byId = (id) => ADDON_ITEMS.find((it) => it.id === id);
      return [
        { ...byId("6"),  quantity: 1, rentalDays: 3 },  // Chiller × 1 · 3 days
        { ...byId("13"), quantity: 8, rentalDays: 3 },  // Round Table × 8 · 3 days
      ];
    })(),
    confirmedAt: "2023-08-12T16:00:00+08:00",
  },
};

/* Auth wrapper — owns login state, renders LoginScreen or AppContent */
function App() {
  const [isLoggedIn, setIsLoggedIn] = React.useState(() => {
    try { return localStorage.getItem("ser-os-login") === "1"; }
    catch { return false; }
  });
  const handleLogin = ({ email, rememberMe }) => {
    if (rememberMe) { try { localStorage.setItem("ser-os-login", "1"); } catch {} }
    setIsLoggedIn(true);
  };
  const handleLogout = () => {
    try { localStorage.removeItem("ser-os-login"); } catch {}
    setIsLoggedIn(false);
  };
  if (!isLoggedIn) return <LoginScreen onLogin={handleLogin} />;
  return <AppContent onLogout={handleLogout} />;
}

function AppContent({ onLogout }) {
  /* ---------- Workspace (brand context) ---------- */
  const [workspace, setWorkspaceState] = React.useState(() => {
    try { return localStorage.getItem("ser-os-workspace") || "serenity"; }
    catch { return "serenity"; }
  });
  // Bump this whenever workspace changes so screens can react (e.g. clear religion)
  const [workspaceTick, setWorkspaceTick] = React.useState(0);

  /* ---------- Toast (subtle, calm) ---------- */
  const [toast, setToast] = React.useState(null);
  const toastTimerRef = React.useRef(null);
  const showToast = (msg) => {
    setToast(msg);
    clearTimeout(toastTimerRef.current);
    toastTimerRef.current = setTimeout(() => setToast(null), 3400);
  };

  /* ---------- Screen + case state ---------- */
  const [screen, setScreen]   = React.useState("cases");
  // cases | archived | start | package | casket | addons

  const [caseDetails, setCaseDetails] = React.useState(null);
  // { quotationId, contact, phone, deceased, religion, brand, caseNumber? }

  /* ---------- Cancel (archive) state ----------
     Source of truth for cancelled-and-filed cases. Seeded from the
     `archived` flag on seed cases. When a director cancels a case via
     the overflow menu, the case is added here, its status is overridden
     to "Cancelled", and it appears in the Case Archive. Restoring
     removes it from the set and returns the case to its original status. */
  const [archivedSet, setArchivedSet] = React.useState(() =>
    new Set(SEED_CASES.filter((c) => c.archived).map((c) => c.caseNumber))
  );
  // Cases whose status has been forced to "Cancelled" via the cancel
  // action. Seeded from any seed cases already marked Cancelled.
  const [cancelledSet, setCancelledSet] = React.useState(() =>
    new Set(SEED_CASES.filter((c) => c.status === "Cancelled").map((c) => c.caseNumber))
  );
  const [archiveTarget,  setArchiveTarget]  = React.useState(null); // case pending cancel confirm

  /* ---------- Contacts state ----------
     Mocks an Odoo res.partner store. CRUD operations go through
     `odooCreate/Update/DeleteContact` so the wiring is ready to be
     swapped for a real Odoo REST/JSON-RPC call. */
  const [contacts, setContacts] = React.useState(() => SEED_CONTACTS);
  const [contactPreselect, setContactPreselect] = React.useState(null);

  /* ---------- Contact modal state (lifted to top-level so the modals
     can open from any screen — My Cases, Archived, Contacts, or a
     case-detail screen). */
  const [viewingContactId,   setViewingContactId]   = React.useState(null); // shows details modal
  const [editingContactState, setEditingContactState] = React.useState(null); // null | "new" | contact object
  const [deletingContact,    setDeletingContact]    = React.useState(null);

  const [selectedPackage, setSelectedPackage] = React.useState(null);
  const [selectedCasket,  setSelectedCasket]  = React.useState(null);
  const [selectedAddOns,  setSelectedAddOns]  = React.useState([]);
  const [notes, setNotes] = React.useState("");
  const [appliedCoupon,   setAppliedCoupon]   = React.useState([]);

  /* ---------- Service Summary state (Odoo-aligned).
     One blob per session — mirrors caseDetails.deceased / religion
     so the existing sidebar Case Details stays correct as the FD
     fills in the Service Summary form. */
  const [serviceData, setServiceDataState] = React.useState(() => makeBlankServiceData());
  const setServiceData = (next) => {
    setServiceDataState(next);
    // Sync the lightweight caseDetails fields so the sidebar Case Details
    // section reflects the latest deceased name / religion.
    if (caseDetails) {
      const patch = {};
      if (typeof next.deceasedName === "string" && next.deceasedName !== caseDetails.deceased) {
        patch.deceased = next.deceasedName;
      }
      if (typeof next.religion === "string" && next.religion && next.religion.toLowerCase() !== caseDetails.religion) {
        patch.religion = next.religion.toLowerCase();
      }
      if (Object.keys(patch).length) setCaseDetails({ ...caseDetails, ...patch });
    }
  };

  // Package screen filter state (persisted across nav)
  const [viewBrand,   setViewBrand]   = React.useState("serenity"); // mirrors workspace
  const [venueFilter, setVenueFilter] = React.useState("all");
  const [comparePkgs, setComparePkgs] = React.useState([]);
  const [pkgDetails,  setPkgDetails]  = React.useState(null);
  const [comparing,   setComparing]   = React.useState(false);

  const [showDraftModal,  setShowDraftModal]  = React.useState(false);
  const [pendingDraft,    setPendingDraft]    = React.useState(null);
  const [showEditCase,    setShowEditCase]    = React.useState(false);
  const [showStartOver,   setShowStartOver]   = React.useState(false);

  /* ── Confirmation workflow stage ─────────────────────────────────
     Tracks where this arrangement sits in the Odoo confirmation flow.
       "draft"           → local only; FD is still building the arrangement
       "awaiting"        → synced to Odoo; customer reviewing + signing
       "confirmed"       → customer signed in Odoo; status synced back here
       "amendment_draft" → FD is editing a previously-confirmed arrangement
       "amended_awaiting"→ amendment sent; awaiting customer re-confirmation
       "confirmed_updated" → customer re-confirmed the amended arrangement
     No manual override — transitions are driven by specific FD actions
     ("Send for Confirmation") or simulated Odoo callbacks (prototype). */
  const [confirmationStatus, setConfirmationStatus] = React.useState("draft");
  const [showSendModal,      setShowSendModal]      = React.useState(false);
  const [showPdfModal,       setShowPdfModal]       = React.useState(false);
  const [showEditWarning,    setShowEditWarning]    = React.useState(false);

  /* ── Per-case workflow state persistence (session-only) ───────────
     Maps caseNumber → confirmationStatus string so navigating between
     My Cases and a consultation screen doesn't reset the stage. */
  const [caseWorkflowStates, setCaseWorkflowStates] = React.useState({
    "QT-260518-019": "awaiting",   // Tan Swee Kiat — Awaiting Signature
    "QT-260517-020": "awaiting",   // Chua Ah Mooi  — Awaiting Signature
    "QT-260515-021": "confirmed",  // Lee Siew Eng  — Confirmed
    "QT-260512-022": "confirmed",  // Yap Boon Teik — Confirmed
    "QT-240517-013": "confirmed",  // Mary Lim Siew Hwa — Confirmed (signed 25 May 2026)
    "QT-230812-002": "confirmed",  // Tan Bee Lian — Historical confirmed case (Sarah Tan's mother)
  });

  /* ── Version histories ──────────────────────────────────────────
     Maps caseNumber → [ { label, timestamp, isSigned, confirmedTotal,
       + full arrangement snapshot: package, casket, addOns, addOnTotal,
         serviceData, caseDetails, appliedCoupon, notes }]
     Versions are created only on signing — unsigned drafts are not versioned. */
  const [caseVersionHistories, setCaseVersionHistories] = React.useState({});

  /* Currently-viewed historical version (for "history-view" screen) */
  const [viewingHistoricalVersion, setViewingHistoricalVersion] = React.useState(null);

  /* ── Amendment snapshot ───────────────────────────────────────────
     Captured when FD clicks "Enter Edit Mode" on a confirmed case.
     Stores { package, casket, addOns, previousStatus, confirmedTotal,
     enteredAt } so "Discard Changes" can roll back to the exact state
     the customer confirmed. */
  const [amendmentSnapshot, setAmendmentSnapshot] = React.useState(null);

  /* Amendment review modal (shown before finalising) */
  const [showAmendmentReview, setShowAmendmentReview] = React.useState(false);

  /* ── Edit Mode entry warning + Discard warning ───────────────────
     Both modals require explicit FD confirmation before performing
     destructive / high-stakes actions in the amendment workflow.
     editModeSource: "readonly" → navigates to addons after confirming;
                     "direct"   → captures snapshot only (no nav). */
  const [showEnterEditModeWarning, setShowEnterEditModeWarning] = React.useState(false);
  const [editModeSource,           setEditModeSource]           = React.useState(null);
  const [showDiscardWarning,       setShowDiscardWarning]       = React.useState(false);

  /* ── Navigation safety in Amendment Mode ─────────────────────────
     When the FD is mid-amendment and tries to navigate away (top Nav
     link, back button), we intercept and ask first. Auto-save is OFF
     in amendment mode, so silent navigation = data loss.
     pendingExitNav: null | { destination: "cases" | "contacts" | "archived" }
     ────────────────────────────────────────────────────────────── */
  const [pendingExitNav, setPendingExitNav] = React.useState(null);

  /* ── Odoo send processing animation ──────────────────────────────
     Drives the 3-step processing view shown inside the Send modal:
       null          → modal showing form (normal state)
       "generating"  → Step 1: Generating Service Summary Form…
       "snapshot"    → Step 2: Preparing final arrangement snapshot…
       "sending"     → Step 3: Sending to Odoo for signature… */
  const [sendingStep, setSendingStep] = React.useState(null);

  /* ── Signed-in-Odoo animation state ──────────────────────────────
     Brief flash (3.5s) shown on the ReadOnly screen immediately after
     the FD simulates the customer signing. While true, the screen
     shows "Signed in Odoo" before settling to "Confirmed".
     Resets automatically via setTimeout in the simulate handler. */
  const [signedAnimation, setSignedAnimation] = React.useState(false);

  /* ── Confirmation timestamp ───────────────────────────────────────
     Records when the simulate-sign was triggered so the ReadOnly
     screen can display the signature timestamp (prototype-only). */
  const [confirmedAt, setConfirmedAt] = React.useState(null);

  /* ── PDF generation timestamp ─────────────────────────────────────
     Set when the FD triggers "Review and Sign" — used as the "Prepared on"
     date inside the generated Service Summary Form. */
  const [pdfGeneratedAt, setPdfGeneratedAt] = React.useState(null);

  /* ── PDF review origin ────────────────────────────────────────────
     Tracks where the FD came from when opening pdf-review so the
     Back button returns them to the correct screen:
       "cases"   → opened confirmed case directly from My Cases
       "readonly" → navigated from ReadOnly screen (awaiting) or
                    arrived here after SendConfirmation */
  const [pdfReviewOrigin, setPdfReviewOrigin] = React.useState("readonly");

  /* ── Send modal validation ────────────────────────────────────────
     Computed when FD opens the Send for Confirmation modal.
     { errors: string[] } — empty array means all required fields OK. */
  const [sendValidation, setSendValidation] = React.useState({ errors: [] });

  /* ── Read-only screen control ─────────────────────────────────
     When a confirmed/awaiting case is opened from My Cases, the app
     routes to "readonly". If the FD tapped the pen/edit icon on the
     card, initialShowEditWarning is set so the edit-mode warning
     overlay opens immediately on mount. */
  const [readOnlyInitialEditWarning, setReadOnlyInitialEditWarning] = React.useState(false);

  // Reset sendingStep if the modal is closed externally
  React.useEffect(() => {
    if (!showSendModal) setSendingStep(null);
  }, [showSendModal]);

  // Keep viewBrand mirrored to workspace
  React.useEffect(() => { setViewBrand(workspace); }, [workspace]);

  // Persist confirmationStatus into the per-case map whenever it changes
  React.useEffect(() => {
    if (!caseDetails?.caseNumber) return;
    setCaseWorkflowStates((prev) => ({
      ...prev,
      [caseDetails.caseNumber]: confirmationStatus,
    }));
  }, [confirmationStatus, caseDetails?.caseNumber]);

  /* ============ Auto-save signal ============
     Whenever consultation state changes, fire `markDirty()` so the
     SavedIndicator in the nav transitions through Saving… → Saved
     just now → Saved. Skips the first render so we don't flash
     "Saving…" on initial app mount. */
  const initialMountRef = React.useRef(true);
  React.useEffect(() => {
    if (initialMountRef.current) { initialMountRef.current = false; return; }
    // Auto-save is disabled in Edit Mode — FD must explicitly Finalise & Save
    if (confirmationStatus === "amendment_draft") return;
    markDirty();
  }, [caseDetails, selectedPackage, selectedCasket, selectedAddOns, notes, archivedSet, cancelledSet, serviceData]);

  // Cases filtered by current workspace, with archived flag pulled
  // from local archive state (not just the seed value).
  //
  // Each case is decorated with its live contact's name + phone resolved
  // through `contactsById` — so when a contact is renamed in My Contacts,
  // every linked case reflects the new name automatically (no duplicated
  // data inside the case, matching Odoo's many2one relation behavior).
  const contactsById = React.useMemo(
    () => Object.fromEntries(contacts.map((c) => [c.id, c])),
    [contacts]
  );

  const visibleCases = React.useMemo(
    () => SEED_CASES
      .filter((c) => workspaceFromCase(c) === workspace)
      .map((c) => {
        const contact = c.contactId ? contactsById[c.contactId] : null;
        const isCancelled = cancelledSet.has(c.caseNumber);
        const workflowStatus = caseWorkflowStates[c.caseNumber] || null;

        // Determine display pill label and tab category based on workflow state
        let pillStatus, tabCategory;
        if (isCancelled) {
          pillStatus   = "Cancelled";
          tabCategory  = "Cancelled";
        } else if (workflowStatus === "confirmed_updated") {
          pillStatus   = "Confirmed";
          tabCategory  = "Confirmed";
        } else if (workflowStatus === "confirmed") {
          pillStatus   = "Confirmed";
          tabCategory  = "Confirmed";
        } else if (workflowStatus === "amendment_draft") {
          pillStatus   = "Amendment Draft";   // FD is mid-edit of a confirmed case
          tabCategory  = "Confirmed";
        } else if (workflowStatus === "amended_awaiting") {
          pillStatus   = "Awaiting Signature";
          tabCategory  = "Confirmed";          // stays in Confirmed group
        } else if (workflowStatus === "awaiting") {
          pillStatus   = "Awaiting Signature";
          tabCategory  = "Awaiting";           // own filter bucket
        } else {
          pillStatus   = null;                 // no pill for draft (working) cases
          tabCategory  = "Draft";
        }

        return {
          ...c,
          archived: archivedSet.has(c.caseNumber),
          status: isCancelled ? "Cancelled" : c.status,
          workflowStatus,
          pillStatus,
          tabCategory,
          // Live join: prefer the contact record over the case's denormalized cache
          contact: contact?.name || c.contact || "—",
          phone:   contact?.mobile || contact?.phone || c.phone || "",
        };
      }),
    [workspace, archivedSet, cancelledSet, contactsById, caseWorkflowStates]
  );

  const activeWorkspaceCount = React.useMemo(
    () => visibleCases.filter((c) => !c.archived && c.tabCategory !== "Cancelled").length,
    [visibleCases]
  );
  const archivedWorkspaceCount = React.useMemo(
    () => visibleCases.filter((c) => c.archived).length,
    [visibleCases]
  );

  // Compute add-on totals (including per-item discounts, excluding section/note rows)
  const addOnTotal = selectedAddOns.reduce((sum, it) => {
    if (it.chargeType === "complimentary") return sum;
    if (it.itemType === "section" || it.itemType === "note") return sum;
    const lineTotal = it.unitPrice * it.quantity * (it.rentalDays || 1);
    if (it.discountType === "percent" && it.discountValue > 0) {
      return sum + lineTotal * (1 - it.discountValue / 100);
    }
    if (it.discountType === "fixed" && it.discountValue > 0) {
      return sum + Math.max(0, lineTotal - it.discountValue);
    }
    return sum + lineTotal;
  }, 0);

  /* ============ Workspace switch handler ============ */
  const handleSetWorkspace = (next) => {
    if (next === workspace) return;
    setWorkspaceState(next);
    setWorkspaceTick((t) => t + 1);
    try { localStorage.setItem("ser-os-workspace", next); } catch {}

    if (screen === "start") {
      // Stay on the create-case screen, but religion may no longer be valid.
      showToast("Workspace updated. Please reselect religion.");
    } else if ((screen === "package" || screen === "casket" || screen === "addons") && caseDetails) {
      const caseWs = workspaceFromCase(caseDetails);
      if (caseWs !== next) {
        // Cross-brand editing not allowed — exit case, return to My Cases.
        showToast("Switched workspace. Returning to your cases.");
        setCaseDetails(null);
        setSelectedPackage(null);
        setSelectedCasket(null);
        setSelectedAddOns([]);
        setComparePkgs([]);
        setComparing(false);
        setScreen("cases");
      }
    }
  };

  // ============ Handlers ============

  /* ── Coupon handlers ──────────────────────────────────────────────
     appliedCoupon is now an ARRAY — multiple coupons stack per Odoo logic.
     handleApplyCoupon: validates + appends to array; replaces if same code.
       For freeitem coupons, inserts a synthetic complimentary add-on.
     handleRemoveCoupon(code): removes a specific coupon by code and its add-on.
       Call with no args to remove all. */
  const handleApplyCoupon = (code, coupon) => {
    // Remove existing entry for this code (allows re-applying to update)
    setSelectedAddOns((prev) => prev.filter((it) => it.couponCode !== code));
    setAppliedCoupon((prev) => {
      const filtered = (prev || []).filter((c) => c.code !== code);
      return [...filtered, { code, ...coupon }];
    });
    // For free-item coupons, add a synthetic complimentary add-on tagged with code
    if (coupon.type === "freeitem") {
      const freeItem = {
        id:            `coupon-${code}`,
        name:          coupon.itemName,
        category:      "Complimentary",
        unitPrice:     0,
        quantity:      1,
        chargeType:    "complimentary",
        discountType:  null,
        discountValue: 0,
        isCouponItem:  true,
        couponCode:    code,
      };
      setSelectedAddOns((prev) => [...prev, freeItem]);
    }
    markDirty();
  };

  const handleRemoveCoupon = (code) => {
    if (!code) {
      // Remove all coupons
      setSelectedAddOns((prev) => prev.filter((it) => !it.isCouponItem));
      setAppliedCoupon([]);
    } else {
      // Remove only the specified coupon
      setSelectedAddOns((prev) => prev.filter((it) => it.couponCode !== code));
      setAppliedCoupon((prev) => (prev || []).filter((c) => c.code !== code));
    }
    markDirty();
  };

  /* handleOpenCase — routes confirmed, confirmed_updated, awaiting, and
     amended_awaiting cases to the read-only details screen; routes
     draft/active cases to the appropriate consultation screen.
     Pass { autoEditWarning: true } (second arg) to pre-open the edit
     warning overlay when navigating to readonly (pen icon on card). */
  const handleOpenCase = (c, { autoEditWarning = false } = {}) => {
    const initialReligion = c.religion.toLowerCase();
    const savedWorkflow   = caseWorkflowStates[c.caseNumber] || "draft";
    const seedData        = SEED_CASE_DATA[c.caseNumber];   // pre-seeded arrangement data, if any

    // Shared state set for all routing paths
    setCaseDetails({
      quotationId: c.caseNumber,
      contact:     c.contact,
      phone:       c.phone,
      deceased:    c.deceased,
      religion:    initialReligion,
      brand:       c.brand,
      caseNumber:  c.caseNumber,
      needType:    c.needType || "as_need",
      relationship: c.relationship || "",
      contactId:   c.contactId || null,
    });
    setViewBrand(workspaceFromCase(c));
    // For Pre-Need cases the person is still living — clear the default
    // dateOfDeath that makeBlankServiceData() seeds to today's date.
    const caseIsPreNeed = (c.needType || "as_need") === "pre_need";
    setServiceDataState({
      ...makeBlankServiceData(),
      ...(caseIsPreNeed ? { dateOfDeath: "" } : {}),
      deceasedName:     c.deceased || "",
      religion:         initialReligion,
      primaryContactId: c.contactId || null,
      ...(seedData?.serviceData || {}),  // merge pre-seeded service data if available
    });
    setConfirmationStatus(savedWorkflow);
    setAmendmentSnapshot(null);
    setNotes(seedData?.notes || "");  // restore arrangement notes from seed data if available

    // ── Confirmed / Awaiting: both open the read-only details screen ────────
    if (
      savedWorkflow === "confirmed"       ||
      savedWorkflow === "confirmed_updated" ||
      savedWorkflow === "awaiting"        ||
      savedWorkflow === "amended_awaiting"
    ) {
      const pkg  = PACKAGES.find((p) => p.name === c.package)
                || (c.package && c.package !== "Not selected" ? PACKAGES[1] : null);
      const cask = CASKETS.find((k) => `${k.name} (${k.finish})` === c.casket)
                || (c.casket && c.casket !== "Not selected"   ? CASKETS[1] : null);
      setSelectedPackage(pkg);
      setSelectedCasket(cask);
      setSelectedAddOns(seedData?.addOns || []);  // use pre-seeded add-ons if available
      // Backfill confirmedAt for seed cases that have no in-session timestamp
      if ((savedWorkflow === "confirmed" || savedWorkflow === "confirmed_updated") && !confirmedAt) {
        setConfirmedAt(seedData?.confirmedAt || c.updatedAt || null);
      }
      setReadOnlyInitialEditWarning(autoEditWarning);
      setScreen("readonly");
      return;
    }

    // ── Amendment draft: resume editing at add-ons screen ───────
    if (savedWorkflow === "amendment_draft") {
      const pkg  = PACKAGES.find((p) => p.name === c.package) || PACKAGES[1];
      const cask = CASKETS.find((k) => `${k.name} (${k.finish})` === c.casket) || CASKETS[1];
      setSelectedPackage(pkg);
      setSelectedCasket(cask);
      setSelectedAddOns([]);
      setScreen("addons");
      return;
    }

    // ── Draft / active: route by what's already selected ────────
    if (!c.package || c.package === "Not selected") {
      setSelectedPackage(null);
      setSelectedCasket(null);
      setSelectedAddOns([]);
      setScreen("package");
    } else if (!c.casket || c.casket === "Not selected") {
      const pkg = PACKAGES.find((p) => p.name === c.package) || PACKAGES[1];
      setSelectedPackage(pkg);
      setSelectedCasket(null);
      setSelectedAddOns([]);
      setScreen("casket");
    } else {
      const pkg  = PACKAGES.find((p) => p.name === c.package) || PACKAGES[1];
      const cask = CASKETS.find((k) => `${k.name} (${k.finish})` === c.casket) || CASKETS[1];
      setSelectedPackage(pkg);
      setSelectedCasket(cask);
      setSelectedAddOns([]);
      setScreen("casket");
    }
  };

  const handleCreateNewCase = () => {
    setCaseDetails(null);
    setSelectedPackage(null);
    setSelectedCasket(null);
    setSelectedAddOns([]);
    setAppliedCoupon(null);
    setServiceDataState(makeBlankServiceData());
    setScreen("start");
  };

  /* ---------- Cancel / restore handlers ----------
     Cancelling a case sets its status to Cancelled AND files it to
     the archive (so it stops cluttering My Cases). Restoring reverses
     both, returning the case to its original status. */
  const handleArchiveRequest = (c) => setArchiveTarget(c);
  const handleArchiveConfirm = () => {
    if (!archiveTarget) return;
    setArchivedSet((prev) => { const n = new Set(prev); n.add(archiveTarget.caseNumber); return n; });
    setCancelledSet((prev) => { const n = new Set(prev); n.add(archiveTarget.caseNumber); return n; });
    showToast(`${archiveTarget.deceased}'s case cancelled and moved to Case Archive.`);
    setArchiveTarget(null);
    // Return to My Cases if FD cancelled from the read-only or pdf-review screen
    if (screen === "readonly" || screen === "pdf-review") setScreen("cases");
  };
  const handleRestoreCase = (c) => {
    setArchivedSet((prev) => { const n = new Set(prev); n.delete(c.caseNumber); return n; });
    setCancelledSet((prev) => { const n = new Set(prev); n.delete(c.caseNumber); return n; });
    showToast(`${c.deceased}'s case restored to My Cases.`);
  };

  /* ---------- Contact CRUD (mocked Odoo sync) ---------- */
  const handleCreateContact = async (payload) => {
    // Optimistic add — replace temp id with server-returned id when promise resolves.
    const tempId = "tmp-" + Date.now();
    setContacts((cs) => [{ ...payload, id: tempId, updatedAt: new Date().toISOString() }, ...cs]);
    showToast(`${payload.name} added to contacts.`);
    try {
      const saved = await odooCreateContact(payload);
      setContacts((cs) => cs.map((c) => (c.id === tempId ? saved : c)));
    } catch (e) {
      markSaveError();
    }
  };

  const handleUpdateContact = async (id, patch) => {
    setContacts((cs) => cs.map((c) => (c.id === id ? { ...c, ...patch, updatedAt: new Date().toISOString() } : c)));
    showToast(`${patch.name || "Contact"} updated.`);
    try { await odooUpdateContact(id, patch); }
    catch (e) { markSaveError(); }
  };

  const handleDeleteContact = async (id) => {
    const snapshot = contacts;
    setContacts((cs) => cs.filter((c) => c.id !== id));
    showToast("Contact deleted.");
    try { await odooDeleteContact(id); }
    catch (e) {
      // Roll back on failure.
      setContacts(snapshot);
      markSaveError();
    }
  };

  const handleStartCaseWithContact = (contact) => {
    setContactPreselect(contact);
    setCaseDetails(null);
    setSelectedPackage(null);
    setSelectedCasket(null);
    setSelectedAddOns([]);
    setScreen("start");
  };

  /* ---------- Contact modal dispatch (open from any screen) ---------- */
  const openContactDetails = (contactId) => {
    if (contactId) setViewingContactId(contactId);
  };
  const openContactEdit = (contact) => {
    // Close details if open, then open edit form
    setViewingContactId(null);
    setEditingContactState(contact || "new");
  };
  const openContactCreate = () => {
    setViewingContactId(null);
    setEditingContactState("new");
  };

  const handleArrangementStarted = (draft) => {
    setPendingDraft(draft);
    setShowDraftModal(true);
  };

  const handleDraftContinue = async () => {
    const d = pendingDraft;
    const initialReligion = d.religion.toLowerCase().replace(/\s+/g, "-");

    // ── Auto-create contact for brand-new contacts ──────────────────
    // When the FD typed a new contact on the Start Arrangement screen
    // (isNewContact = true, no contactId yet), create the contact record
    // immediately so it appears in the contacts list and can be set as
    // Primary Family Contact without any additional manual step.
    let resolvedContactId = d.contactId || null;
    let resolvedEmail     = d.email || "";
    if (d.isNewContact && !d.contactId && d.contact) {
      try {
        const saved = await odooCreateContact({
          name:     d.contact,
          mobile:   d.phone || "",
          phone:    d.phone || "",
          email:    d.email || "",
          type:     "individual",
          category: "family",
          tags:     [],
        });
        setContacts((cs) => [{ ...saved, updatedAt: new Date().toISOString() }, ...cs]);
        resolvedContactId = saved.id;
        resolvedEmail     = saved.email || d.email || "";
        showToast(`${d.contact} added to contacts and set as Primary Contact.`);
      } catch (_) {
        // Non-fatal — FD can still assign manually in Service Summary
      }
    }

    // contactFromDirectory: true only when the user explicitly selected a
    // pre-existing contact from the directory (d.contactId was already set).
    // Auto-created contacts (typed new name) are NOT from the directory, so
    // their name/phone stay editable in the Edit Case Information modal.
    const contactFromDirectory = !!d.contactId;

    setCaseDetails({
      quotationId: d.quotationId,
      contact:  d.contact,
      phone:    d.phone || "",
      deceased: d.deceased,
      religion: initialReligion,
      brand:    workspaceBrandLabel(workspace),
      needType: d.needType || "as_need",
      relationship: d.relationship || "",
      contactId: resolvedContactId,
      contactFromDirectory,
    });
    setViewBrand(workspace);
    setSelectedPackage(null);
    setSelectedCasket(null);
    setSelectedAddOns([]);
    setAppliedCoupon(null);
    // Seed Service Summary with the bits captured in Start Arrangement so
    // the FD doesn't retype the deceased name / religion. For At-Need cases
    // the deceased has already passed, so default Date of Death to today.
    const isAsNeed = (d.needType || "as_need") === "as_need";
    const todayISO = new Date().toISOString().slice(0, 10);
    setServiceDataState({
      ...makeBlankServiceData(),
      deceasedName: d.deceased || "",
      religion:     titleCase(d.religion || ""),
      dateOfDeath:  isAsNeed ? todayISO : "",
      // Pre-fill Primary Family Contact with the newly created (or selected)
      // contact so the FD doesn't need to pick them again in Case Contacts.
      primaryContactId:    resolvedContactId,
      primaryContactEmail: resolvedEmail,
      primaryRelationship: d.relationship || "",
    });
    setConfirmationStatus("draft");
    setShowDraftModal(false);
    // New cases land on Service Summary first — the FD captures care &
    // family details before product selection.
    setScreen("service");
  };

  const handleStartOver = () => {
    setCaseDetails(null);
    setSelectedPackage(null);
    setSelectedCasket(null);
    setSelectedAddOns([]);
    setAppliedCoupon(null);
    setComparePkgs([]);
    setComparing(false);
    setShowStartOver(false);
    setConfirmationStatus("draft");
    setScreen("cases");
  };

  const handleEditCaseSave = (draft) => {
    const newBrand = brandForReligion(draft.religion);
    const prevBrand = brandForReligion(caseDetails.religion);
    // If this contact was auto-created (not from directory), propagate
    // any name/phone edits back to the contacts record so they stay in sync.
    if (!draft.contactFromDirectory && draft.contactId) {
      const nameChanged  = draft.contact !== caseDetails.contact;
      const phoneChanged = (draft.phone || "") !== (caseDetails.phone || "");
      if (nameChanged || phoneChanged) {
        setContacts(prev => prev.map(c =>
          c.id === draft.contactId
            ? { ...c, name: draft.contact, mobile: draft.phone || c.mobile, phone: draft.phone || c.phone }
            : c
        ));
      }
    }
    setCaseDetails({ ...draft, brand: caseDetails.brand });
    // Any case-detail edit while awaiting resets the stage — the
    // arrangement data no longer matches what was sent to Odoo.
    if (confirmationStatus === "awaiting") {
      setConfirmationStatus("draft");
      showToast("Arrangement updated — re-send for confirmation when ready.");
    }
    if (newBrand !== prevBrand) {
      setViewBrand(newBrand === "all" ? workspace : newBrand);
      // Religion changed -> reset package
      setSelectedPackage(null);
      setSelectedCasket(null);
      setShowEditCase(false);
      setScreen("package");
    } else {
      setShowEditCase(false);
    }
  };

  /* ── Confirmation workflow handlers ─────────────────────────────── */

  // FD confirms the "Send for Confirmation" overlay.
  // Shows a 3-step processing animation:
  //   1. Generating Service Summary Form (FD App)
  //   2. Preparing final arrangement snapshot (freeze point)
  //   3. Sending to Odoo for signature
  // After completion: navigates to the PDF Review screen (new flow).
  const handleSendConfirmation = () => {
    const now = new Date().toISOString();
    setSendingStep("generating");
    setTimeout(() => {
      setSendingStep("snapshot");
      setTimeout(() => {
        setSendingStep("sending");
        setTimeout(() => {
          setSendingStep(null);
          setShowSendModal(false);
          setPdfGeneratedAt(now);
          setConfirmationStatus("awaiting");
          setReadOnlyInitialEditWarning(false);
          setPdfReviewOrigin("addons");
          setScreen("pdf-review");
          showToast("Service Summary Form generated — ready for review and signing.");
        }, 1500);
      }, 1300);
    }, 1400);
  };

  // Opens the Send for Confirmation modal, running validation first.
  const handleOpenSendModal = () => {
    const errors = [];
    if (!caseDetails?.deceased?.trim())
      errors.push("Deceased name is required");
    if (!caseDetails?.contact?.trim())
      errors.push("Primary contact is required");
    if (!caseDetails?.religion)
      errors.push("Religion is required");
    if (!selectedPackage)
      errors.push("A funeral package must be selected");
    if (!serviceData?.cremationOrBurial)
      errors.push("Funeral arrangement (Cremation or Burial) must be specified in Service Details");
    setSendValidation({ errors });
    setShowSendModal(true);
  };

  // FD clicks "Edit" inside the PDF Review screen.
  // Awaiting cases → reset to draft, return to Service Summary.
  // Confirmed cases → enter amendment mode, go to Add-ons.
  const handleEditFromPdfReview = () => {
    if (confirmationStatus === "confirmed" || confirmationStatus === "confirmed_updated") {
      handleEnterEditModeFromReadOnly(); // snapshot + amendment_draft + addons screen
    } else {
      // Awaiting → undo the send, return to consultation
      setConfirmationStatus("draft");
      setScreen("service");
    }
  };

  // FD clicks "Sign" inside the PDF Review screen.
  // The signing animation runs inside ServiceSummaryPdfScreen,
  // then calls this once the simulation completes.
  // Signing is the canonical moment a version is recorded.
  const handleSignFromPdfReview = () => {
    const now = new Date().toISOString();
    setSignedAnimation(true);
    setConfirmedAt(now);
    setConfirmationStatus("confirmed");
    const caseNumber = caseDetails?.caseNumber;
    if (caseNumber) {
      setCaseVersionHistories((prev) => {
        const history = prev[caseNumber] || [];
        return { ...prev, [caseNumber]: [...history, buildVersionRecord(history.length + 1, now)] };
      });
    }
    setTimeout(() => { setSignedAnimation(false); setScreen("thankyou"); }, 400);
  };

  // FD requests a re-send (e.g. contact details changed on the Odoo side).
  const handleResend = () => {
    setConfirmationStatus("awaiting");
    showToast("Arrangement re-sent — awaiting customer confirmation.");
  };

  // FD delivers the already-signed PDF to the customer (notification only).
  const handleSendSignedForm = () => {
    showToast(`Signed Service Summary Form sent to ${caseDetails?.contact || "customer"}.`);
  };

  // "Edit Arrangement" button in the sidebar while in Awaiting state.
  const handleEditArrangement = () => setShowEditWarning(true);

  // FD confirms the edit warning → reset stage, stay on current screen
  // so they can immediately modify selections.
  const handleEditWarningConfirm = () => {
    setShowEditWarning(false);
    setConfirmationStatus("draft");
  };

  // Prototype-only: simulates Odoo reporting that the customer signed.
  // Briefly shows "Signed in Odoo" animation before settling to "Confirmation Synced".
  const handleSimulateConfirmed = () => {
    const now = new Date().toISOString();
    setShowPdfModal(false);
    setSignedAnimation(true);
    setConfirmedAt(now);
    setConfirmationStatus("confirmed");
    const caseNumber = caseDetails?.caseNumber;
    if (caseNumber) {
      setCaseVersionHistories((prev) => {
        const history = prev[caseNumber] || [];
        return { ...prev, [caseNumber]: [...history, buildVersionRecord(history.length + 1, now)] };
      });
    }
    setTimeout(() => setSignedAnimation(false), 3500);
    showToast("Signed in Odoo — confirmation synced.");
  };

  /* ── Amendment workflow handlers ─────────────────────────────────── */

  // FD enters edit mode on a confirmed case → capture snapshot, set amendment_draft
  const handleEnterEditMode = React.useCallback(() => {
    const snap = {
      package:        selectedPackage,
      casket:         selectedCasket,
      addOns:         selectedAddOns,
      coupon:         appliedCoupon,
      previousStatus: confirmationStatus,
      enteredAt:      new Date().toISOString(),
      confirmedTotal:
        (selectedPackage ? selectedPackage.price : 0) +
        (selectedCasket  ? (selectedCasket.included ? 0 : selectedCasket.upgrade) : 0) +
        addOnTotal,
    };
    setAmendmentSnapshot(snap);
    setConfirmationStatus("amendment_draft");
  }, [selectedPackage, selectedCasket, selectedAddOns, appliedCoupon, confirmationStatus, addOnTotal]);

  // Called from ReadOnlyArrangementScreen "Enter Edit Mode" confirm:
  // captures snapshot, sets amendment_draft, navigates to add-ons.
  const handleEnterEditModeFromReadOnly = React.useCallback(() => {
    handleEnterEditMode();
    setScreen("addons");
  }, [handleEnterEditMode]);

  /* ── Version record helper ───────────────────────────────────────
     Builds a full version object capturing the entire arrangement
     state at the moment the customer signs. This frozen snapshot is
     used by HistoricalVersionScreen to render the exact arrangement
     as it was at that point in time. */
  const buildVersionRecord = (versionNumber, timestamp) => {
    const signedTotal =
      (selectedPackage ? selectedPackage.price : 0) +
      (selectedCasket  ? (selectedCasket.included ? 0 : selectedCasket.upgrade) : 0) +
      addOnTotal;
    return {
      label:          `Version ${versionNumber} — Signed`,
      timestamp,
      isSigned:       true,
      confirmedTotal: signedTotal,
      /* Full arrangement snapshot — frozen at signing time */
      package:        selectedPackage,
      casket:         selectedCasket,
      addOns:         [...(selectedAddOns || [])],
      addOnTotal,
      serviceData:    { ...serviceData },
      caseDetails:    { ...caseDetails },
      appliedCoupon:  [...(appliedCoupon || [])],
      notes,
      contacts:       contacts.map(c => ({ ...c })),
    };
  };

  /* Navigate to the historical version view */
  const handleViewHistoricalVersion = (version) => {
    setViewingHistoricalVersion(version);
    setScreen("history-view");
  };

  /* ── Edit Mode entry — always show a warning modal first ────────
     handleRequestEnterEditMode: intercepts all explicit button clicks;
       source "readonly" → after confirm, captures snapshot + nav to addons
       source "direct"   → after confirm, captures snapshot only (no nav)
     handleConfirmEnterEditMode: called after FD confirms the modal.
     These replace direct calls to handleEnterEditMode[FromReadOnly] in all
     prop passes so auto-entry from add-on modification stays unblocked. */
  const handleRequestEnterEditMode = (source) => {
    setEditModeSource(source || "direct");
    setShowEnterEditModeWarning(true);
  };
  const handleConfirmEnterEditMode = () => {
    setShowEnterEditModeWarning(false);
    if (editModeSource === "readonly") {
      handleEnterEditModeFromReadOnly();
    } else {
      handleEnterEditMode();
    }
    setEditModeSource(null);
  };

  /* Auto-fire the Enter Edit Mode warning when the case card's edit
     button was clicked. readOnlyInitialEditWarning is set by handleOpenCase
     and consumed here once the readonly screen has mounted. */
  React.useEffect(() => {
    if (readOnlyInitialEditWarning && screen === "readonly") {
      setReadOnlyInitialEditWarning(false);
      handleRequestEnterEditMode("readonly");
    }
  }, [readOnlyInitialEditWarning, screen]);

  /* ── Discard amendment — show confirmation modal first ────────── */
  const handleRequestDiscard = () => setShowDiscardWarning(true);

  /* ── Navigation safety: intercept top-Nav clicks during amendment ─
     Auto-save is off in amendment mode, so we ask before leaving. */
  const handleNavRequest = (destination) => {
    if (confirmationStatus === "amendment_draft") {
      setPendingExitNav({ destination });
      return;
    }
    if (destination === "cases")    setScreen("cases");
    if (destination === "contacts") setScreen("contacts");
    if (destination === "archived") setScreen("archived");
  };
  const handleExitConfirm = () => {
    const dest = pendingExitNav?.destination;
    setPendingExitNav(null);
    // Roll back amendment to last confirmed state before navigating
    handleDiscardAmendment();
    if (dest === "cases")    setScreen("cases");
    if (dest === "contacts") setScreen("contacts");
    if (dest === "archived") setScreen("archived");
  };
  const handleConfirmDiscard = () => {
    setShowDiscardWarning(false);
    handleDiscardAmendment();
    setScreen("readonly");
  };

  // FD discards amendment → restore snapshot
  const handleDiscardAmendment = () => {
    if (!amendmentSnapshot) return;
    setSelectedPackage(amendmentSnapshot.package);
    setSelectedCasket(amendmentSnapshot.casket);
    setSelectedAddOns(amendmentSnapshot.addOns);
    setAppliedCoupon(amendmentSnapshot.coupon || null);
    setConfirmationStatus(amendmentSnapshot.previousStatus);
    setAmendmentSnapshot(null);
  };

  // FD clicks "Finalise Amendment" → open review modal
  const handleFinalizeAmendment = () => setShowAmendmentReview(true);

  // FD confirms finalisation → save amendment, navigate to PDF review for sign-off.
  // Versions are NOT created here — they are only recorded when the customer signs.
  // Unsigned amendments overwrite the current working state.
  const handleFinalizeAmendmentConfirm = () => {
    setShowAmendmentReview(false);
    setAmendmentSnapshot(null);
    setConfirmationStatus("amended_awaiting");
    setReadOnlyInitialEditWarning(false);
    setScreen("readonly");
    showToast("Amendment saved — awaiting customer re-confirmation.");
  };

  // FD sends updated confirmed arrangement to customer (from confirmed_updated)
  const handleSendToCustomer = () => {
    setConfirmationStatus("amended_awaiting");
    showToast("Updated arrangement sent — awaiting customer confirmation.");
  };

  // Prototype-only: simulate customer signing the amended arrangement.
  // Same two-step animation as the original: "Signed in Odoo" → "Confirmation Synced".
  const handleSimulateAmendmentConfirmed = () => {
    const now = new Date().toISOString();
    setShowPdfModal(false);
    setSignedAnimation(true);
    setConfirmedAt(now);
    setConfirmationStatus("confirmed_updated");
    const caseNumber = caseDetails?.caseNumber;
    if (caseNumber) {
      setCaseVersionHistories((prev) => {
        const history = prev[caseNumber] || [];
        return { ...prev, [caseNumber]: [...history, buildVersionRecord(history.length + 1, now)] };
      });
    }
    setTimeout(() => setSignedAnimation(false), 3500);
    showToast("Amendment signed in Odoo — confirmation synced.");
  };

  // Derives the correct "simulate customer signed" handler for the current
  // workflow state. Passed as `onSimulateSign` to sidebar-equipped screens.
  const simulateSignHandler =
    confirmationStatus === "awaiting"         ? handleSimulateConfirmed :
    confirmationStatus === "amended_awaiting" ? handleSimulateAmendmentConfirmed :
    undefined;

  const onJumpTo = (step) => {
    if (!caseDetails) return;
    if (step === "service") setScreen("service");
    if (step === "package") setScreen("package");
    if (step === "casket")  setScreen("casket");
    if (step === "addons")  setScreen("addons");
  };

  const togglePkgCompare = (pkg) => {
    if (comparePkgs.find((p) => p.id === pkg.id)) {
      setComparePkgs(comparePkgs.filter((p) => p.id !== pkg.id));
    } else if (comparePkgs.length < 3) {
      setComparePkgs([...comparePkgs, pkg]);
    }
  };

  // ============ Render ============
  const showNav = true;

  return (
    <div style={{ minHeight: "100vh", display: "flex", flexDirection: "column", background: "#FAFAFA" }}>
      {showNav && (
        <Nav
          active={
            screen === "cases" ? "cases" :
            screen === "archived" ? "archived" :
            screen === "contacts" ? "contacts" :
            "consultation"
          }
          onNavigate={(t) => {
            handleNavRequest(t);
          }}
          workspace={workspace}
          onSetWorkspace={handleSetWorkspace}
          onLogout={onLogout}
        />
      )}

      {/* ── Persistent Focus Mode bar ─────────────────────────────
           Sits directly below the Nav on Arrangement / Amendment /
           Read-only screens. Reinforces save semantics + mode at
           every moment of the consultation. */}
      <ModeBar
        mode={deriveFocusMode(screen, confirmationStatus)}
        signedAt={confirmedAt ? new Date(confirmedAt).toLocaleDateString("en-SG", { day: "2-digit", month: "short", year: "numeric" }) : null}
        dirty={confirmationStatus === "amendment_draft" ? !!amendmentSnapshot : false}
      />

      {screen === "cases" && (
        <MyCasesScreen
          cases={visibleCases}
          onOpenCase={handleOpenCase}
          onCreateNew={handleCreateNewCase}
          onArchiveCase={handleArchiveRequest}
          onOpenContact={openContactDetails}
          onGoToArchive={() => setScreen("archived")}
          archivedCount={archivedWorkspaceCount}
          onEnterEditMode={(c) => {
            // Open the read-only screen with the edit-mode warning pre-triggered
            handleOpenCase(c, { autoEditWarning: true });
          }}
        />
      )}

      {screen === "readonly" && caseDetails && (
        <ReadOnlyArrangementScreen
          caseDetails={caseDetails}
          serviceData={serviceData}
          contacts={contacts}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal}
          notes={notes}
          confirmationStatus={confirmationStatus}
          caseVersionHistory={caseVersionHistories[caseDetails?.caseNumber] || []}
          initialShowEditWarning={readOnlyInitialEditWarning}
          confirmedAt={confirmedAt}
          showSignedAnimation={signedAnimation}
          appliedCoupon={appliedCoupon}
          onBack={() => setScreen("cases")}
          onViewPdf={() => { setPdfReviewOrigin("readonly"); setScreen("pdf-review"); }}
          onSendSignedForm={handleSendSignedForm}
          onSendReminder={() => showToast(`Reminder sent to ${caseDetails?.contact || "customer"} — awaiting their signature in Odoo.`)}
          onEnterEditMode={() => handleRequestEnterEditMode("readonly")}
          onSimulateSign={simulateSignHandler}
          onViewHistoricalVersion={handleViewHistoricalVersion}
          onCancelCase={() => {
            if (caseDetails) {
              setArchiveTarget({
                deceased:   caseDetails.deceased,
                caseNumber: caseDetails.caseNumber,
                status:     confirmationStatus,
              });
            }
          }}
        />
      )}

      {/* ── Historical Version viewer ───────────────────────────
           Full-screen read-only view of a frozen signed snapshot.
           PDF is viewed inline via overlay — no separate screen needed. */}
      {screen === "history-view" && viewingHistoricalVersion && caseDetails && (
        <HistoricalVersionScreen
          version={viewingHistoricalVersion}
          caseDetails={caseDetails}
          onBack={() => { setViewingHistoricalVersion(null); setScreen("readonly"); }}
        />
      )}

      {/* ── PDF Review screen ───────────────────────────────────
           Shown after FD clicks "Review and Sign" in the Send modal.
           Displays the generated Service Summary Form in Serenity style.
           FD (and/or customer on tablet) can Sign → triggers Odoo flow. */}
      {screen === "pdf-review" && caseDetails && (
        <ServiceSummaryPdfScreen
          caseDetails={caseDetails}
          serviceData={serviceData}
          contacts={contacts}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal}
          notes={notes}
          confirmationStatus={confirmationStatus}
          generatedAt={pdfGeneratedAt}
          confirmedAt={confirmedAt}
          appliedCoupon={appliedCoupon}
          backLabel={pdfReviewOrigin === "addons" ? "Edit" : "Back"}
          onBack={() => pdfReviewOrigin === "addons" ? handleEditFromPdfReview() : setScreen(pdfReviewOrigin)}
          onSign={handleSignFromPdfReview}
          onEdit={handleEditFromPdfReview}
          onSendToCustomer={handleSendSignedForm}
          onUpdateContact={handleUpdateContact}
          onCancelCase={() => {
            if (caseDetails) {
              setArchiveTarget({
                deceased:   caseDetails.deceased,
                caseNumber: caseDetails.caseNumber,
                status:     confirmationStatus,
              });
            }
          }}
        />
      )}

      {/* ── Thank You / Next Steps screen ────────────────────────
           Shown after the Odoo signing simulation completes.
           Helps FD conclude the consultation with emotional closure. */}
      {screen === "thankyou" && caseDetails && (
        <ThankYouScreen
          caseDetails={caseDetails}
          confirmedAt={confirmedAt}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal}
          appliedCoupon={appliedCoupon}
          onReturnToCases={() => setScreen("cases")}
          onViewSignedForm={() => { setPdfReviewOrigin("cases"); setScreen("pdf-review"); }}
          onViewCase={() => { setReadOnlyInitialEditWarning(false); setScreen("readonly"); }}
          onEditCase={() => handleRequestEnterEditMode("readonly")}
          onStartNewCase={handleCreateNewCase}
        />
      )}

      {screen === "archived" && (
        <CaseArchiveScreen
          cases={visibleCases}
          onOpenCase={handleOpenCase}
          onRestoreCase={handleRestoreCase}
          onOpenContact={openContactDetails}
          onBackToCases={() => setScreen("cases")}
        />
      )}

      {screen === "contacts" && (
        <ContactsScreen
          contacts={contacts}
          cases={SEED_CASES}
          onOpenContact={openContactDetails}
          onCreateContact={openContactCreate}
          onEditContact={openContactEdit}
          onDeleteContact={(c) => setDeletingContact(c)}
          onStartCaseWith={handleStartCaseWithContact}
        />
      )}

      {screen === "start" && (
        <StartArrangementScreen
          contacts={contacts}
          onStarted={handleArrangementStarted}
          workspace={workspace}
          workspaceTick={workspaceTick}
          preselectedContact={contactPreselect}
          onConsumePreselect={() => setContactPreselect(null)}
        />
      )}

      {screen === "service" && caseDetails && (
        <ServiceSummaryScreen
          caseDetails={caseDetails}
          workspace={workspace}
          serviceData={serviceData}
          setServiceData={setServiceData}
          contacts={contacts}
          onCreateContact={async (payload) => {
            const tempId = "tmp-" + Date.now();
            const optimistic = { ...payload, id: tempId, updatedAt: new Date().toISOString() };
            setContacts((cs) => [optimistic, ...cs]);
            try {
              const saved = await odooCreateContact(payload);
              setContacts((cs) => cs.map((c) => (c.id === tempId ? saved : c)));
              return saved;
            } catch { markSaveError(); return optimistic; }
          }}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal} addOnCount={selectedAddOns.length}
          notes={notes} setNotes={setNotes}
          onEditCase={() => setShowEditCase(true)}
          onJumpTo={onJumpTo}
          onContinue={() => setScreen("cases")}
          confirmationStatus={confirmationStatus}
          onSendForConfirmation={handleOpenSendModal}
          onViewPdf={() => setScreen("pdf-review")}
          onResend={handleResend}
          onEditArrangement={handleEditArrangement}
          amendmentSnapshot={amendmentSnapshot}
          onEnterEditMode={() => handleRequestEnterEditMode("direct")}
          onFinalizeAmendment={handleFinalizeAmendment}
          onDiscardAmendment={handleRequestDiscard}
          onSendToCustomer={handleSendToCustomer}
          onSimulateSign={simulateSignHandler}
          onSendSignedForm={handleSendSignedForm}
          appliedCoupon={appliedCoupon}
          onApplyCoupon={handleApplyCoupon}
          onRemoveCoupon={handleRemoveCoupon}
        />
      )}

      {screen === "package" && caseDetails && (
        <PackageScreen
          caseDetails={caseDetails}
          workspace={workspace}
          serviceData={serviceData}
          viewBrand={viewBrand}   setViewBrand={setViewBrand}
          venueFilter={venueFilter} setVenueFilter={setVenueFilter}
          selectedPackage={selectedPackage}
          onSelect={setSelectedPackage}
          comparePkgs={comparePkgs} toggleCompare={togglePkgCompare}
          isComparing={comparing} setComparing={setComparing}
          detailsModal={pkgDetails} setDetailsModal={setPkgDetails}
          onBack={() => setScreen("cases")}
          onStartOver={() => setShowStartOver(true)}
          onEditCase={() => setShowEditCase(true)}
          onContinue={() => setScreen("cases")}
          onJumpTo={onJumpTo}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal} addOnCount={selectedAddOns.length}
          confirmationStatus={confirmationStatus}
          onSendForConfirmation={handleOpenSendModal}
          onViewPdf={() => setScreen("pdf-review")}
          onResend={handleResend}
          onEditArrangement={handleEditArrangement}
          amendmentSnapshot={amendmentSnapshot}
          onEnterEditMode={() => handleRequestEnterEditMode("direct")}
          onFinalizeAmendment={handleFinalizeAmendment}
          onDiscardAmendment={handleRequestDiscard}
          onSendToCustomer={handleSendToCustomer}
          onSimulateSign={simulateSignHandler}
          onSendSignedForm={handleSendSignedForm}
          notes={notes}
          setNotes={setNotes}
          appliedCoupon={appliedCoupon}
          onApplyCoupon={handleApplyCoupon}
          onRemoveCoupon={handleRemoveCoupon}
        />
      )}

      {screen === "casket" && caseDetails && (
        <CasketScreen
          caseDetails={caseDetails}
          serviceData={serviceData}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          onSelect={setSelectedCasket}
          selectedAddOns={selectedAddOns}
          addOnTotal={addOnTotal} addOnCount={selectedAddOns.length}
          notes={notes} setNotes={setNotes}
          onBack={() => setScreen("package")}
          onContinue={() => setScreen("cases")}
          onEditCase={() => setShowEditCase(true)}
          onJumpTo={onJumpTo}
          confirmationStatus={confirmationStatus}
          onSendForConfirmation={handleOpenSendModal}
          onViewPdf={() => setScreen("pdf-review")}
          onResend={handleResend}
          onEditArrangement={handleEditArrangement}
          amendmentSnapshot={amendmentSnapshot}
          onEnterEditMode={() => handleRequestEnterEditMode("direct")}
          onFinalizeAmendment={handleFinalizeAmendment}
          onDiscardAmendment={handleRequestDiscard}
          onSendToCustomer={handleSendToCustomer}
          onSimulateSign={simulateSignHandler}
          onSendSignedForm={handleSendSignedForm}
          appliedCoupon={appliedCoupon}
          onApplyCoupon={handleApplyCoupon}
          onRemoveCoupon={handleRemoveCoupon}
        />
      )}

      {screen === "addons" && caseDetails && (
        <AddOnScreen
          caseDetails={caseDetails}
          serviceData={serviceData}
          selectedPackage={selectedPackage}
          selectedCasket={selectedCasket}
          selectedAddOns={selectedAddOns}
          setSelectedAddOns={(next) => {
            // Any add-on change while awaiting resets the stage so
            // the FD must re-send the updated arrangement to Odoo.
            if (confirmationStatus === "awaiting") {
              setConfirmationStatus("draft");
              showToast("Arrangement updated — re-send for confirmation when ready.");
            } else if (confirmationStatus === "confirmed" || confirmationStatus === "confirmed_updated") {
              // Auto-enter edit mode so confirmed data is protected by snapshot
              handleEnterEditMode();
            }
            setSelectedAddOns(next);
          }}
          notes={notes} setNotes={setNotes}
          onBack={() => setScreen("casket")}
          onContinue={() => setScreen("cases")}
          onEditCase={() => setShowEditCase(true)}
          onJumpTo={onJumpTo}
          confirmationStatus={confirmationStatus}
          onSendForConfirmation={handleOpenSendModal}
          onViewPdf={() => setScreen("pdf-review")}
          onResend={handleResend}
          onEditArrangement={handleEditArrangement}
          amendmentSnapshot={amendmentSnapshot}
          onEnterEditMode={() => handleRequestEnterEditMode("direct")}
          onFinalizeAmendment={handleFinalizeAmendment}
          onDiscardAmendment={handleRequestDiscard}
          onSendToCustomer={handleSendToCustomer}
          onSimulateSign={simulateSignHandler}
          onSendSignedForm={handleSendSignedForm}
          caseVersionHistory={caseVersionHistories[caseDetails?.caseNumber] || []}
          appliedCoupon={appliedCoupon}
          onApplyCoupon={handleApplyCoupon}
          onRemoveCoupon={handleRemoveCoupon}
        />
      )}

      {/* ===== Modals ===== */}

      {/* Draft quotation created modal */}
      <Modal
        open={showDraftModal}
        onClose={() => setShowDraftModal(false)}
        title="New Arrangement Started"
        icon={<span style={{ color: "#1F3A60", display: "flex" }}><SerIcons.CheckCircle size={22} /></span>}
        secondary={{ label: "Back to My Cases", onClick: () => { setShowDraftModal(false); setScreen("cases"); } }}
        primary={{   label: "Continue", onClick: handleDraftContinue }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px" }}>
          A new arrangement has been started successfully.
        </p>
        {pendingDraft && (
          <DetailGrid rows={[
            { label: "Quotation Number", value: pendingDraft.quotationId },
            { label: "Contact",          value: pendingDraft.contact },
            { label: "Deceased",         value: pendingDraft.deceased },
            { label: "Religion",         value: titleCase(pendingDraft.religion) },
          ]} />
        )}
        <p style={{ fontSize: 13, color: "#6B7A8F", margin: 0, letterSpacing: "-0.15px" }}>
          Continue to select a package and begin the consultation.
        </p>
      </Modal>

      {/* Edit case modal */}
      <EditCaseModal
        open={showEditCase}
        caseDetails={caseDetails || { contact: "", phone: "", deceased: "", religion: "free-thinker" }}
        workspace={workspace}
        onClose={() => setShowEditCase(false)}
        onSave={handleEditCaseSave}
      />

      {/* Start Over confirmation */}
      <Modal
        open={showStartOver}
        onClose={() => setShowStartOver(false)}
        title="Start Over?"
        secondary={{ label: "Cancel", onClick: () => setShowStartOver(false) }}
        primary={{   label: "Start Over", onClick: handleStartOver }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px" }}>
          This will reset the current consultation and clear all selections. This action cannot be undone.
        </p>
      </Modal>

      {/* Cancel confirmation */}
      <Modal
        open={!!archiveTarget}
        onClose={() => setArchiveTarget(null)}
        title="Cancel this case?"
        secondary={{ label: "Keep Case", onClick: () => setArchiveTarget(null) }}
        primary={{   label: "Cancel Case", onClick: handleArchiveConfirm }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px" }}>
          This case will be marked as Cancelled and moved to Case Archive. You can restore it at any time.
        </p>
        {archiveTarget && (
          <DetailGrid rows={[
            { label: "Deceased",    value: archiveTarget.deceased },
            { label: "Case Number", value: archiveTarget.caseNumber },
            { label: "Status",      value: archiveTarget.status },
          ]} />
        )}
      </Modal>

      {/* Contact details — opens from any clickable contact name */}
      <ContactDetailsModal
        open={!!viewingContactId}
        contact={contactsById[viewingContactId]}
        cases={SEED_CASES}
        contactsById={contactsById}
        onClose={() => setViewingContactId(null)}
        onEdit={() => openContactEdit(contactsById[viewingContactId])}
        onOpenCase={(c) => {
          setViewingContactId(null);
          handleOpenCase(c);
        }}
        onStartCase={() => {
          const c = contactsById[viewingContactId];
          setViewingContactId(null);
          if (c) handleStartCaseWithContact(c);
        }}
      />

      {/* Contact create / edit form — lifted from ContactsScreen so it can
          open from anywhere (e.g. via "Edit" inside ContactDetailsModal) */}
      <ContactFormModal
        open={!!editingContactState}
        contact={editingContactState === "new" ? null : editingContactState}
        onClose={() => setEditingContactState(null)}
        onSave={(payload) => {
          if (editingContactState === "new") {
            handleCreateContact(payload);
          } else {
            handleUpdateContact(editingContactState.id, payload);
          }
          setEditingContactState(null);
        }}
      />

      {/* Contact delete confirm */}
      <Modal
        open={!!deletingContact}
        onClose={() => setDeletingContact(null)}
        title="Delete this contact?"
        secondary={{ label: "Cancel", onClick: () => setDeletingContact(null) }}
        primary={{
          label: "Delete Contact",
          onClick: () => { handleDeleteContact(deletingContact.id); setDeletingContact(null); },
        }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px" }}>
          {deletingContact && getLinkedCasesForContact(deletingContact, SEED_CASES).length > 0
            ? `This contact is linked to ${getLinkedCasesForContact(deletingContact, SEED_CASES).length} case(s). The linkage will be preserved but the contact will be removed from your directory.`
            : "This action removes the contact from your directory. It cannot be undone."}
        </p>
      </Modal>

      {/* ── Send for Confirmation modal ──────────────────────────────
           Shown when FD clicks "Send for Confirmation" in the sidebar.
           Step 1: Confirmation overlay — summarise intent.
           Step 2 (sendingStep !== null): 3-step processing animation.
               Generating form → Freezing snapshot → Sending to Odoo
           The modal is non-closeable while processing — no accidental dismiss. */}
      {/* ── Send for Confirmation modal ──────────────────────────────
           Validates required arrangement fields before allowing progression.
           Step 1: Shows summary, validation errors, updated copy.
           Step 2 (sendingStep ≠ null): 3-step processing animation.
           The "Review and Sign" CTA is disabled if validation fails.
           Non-closeable while processing to prevent accidental dismiss. */}
      {(() => {
        const hasErrors = sendValidation?.errors?.length > 0;
        const realAddOnCount = selectedAddOns.filter((it) => it.itemType !== "section" && it.itemType !== "note").length;
        const sendTotal = (selectedPackage ? selectedPackage.price : 0) +
          (selectedCasket ? (selectedCasket.included ? 0 : selectedCasket.upgrade) : 0) +
          addOnTotal;
        return (
          <Modal
            open={showSendModal}
            onClose={sendingStep ? undefined : () => setShowSendModal(false)}
            title={sendingStep ? "Generating & Sending…" : "Send for Confirmation"}
            icon={sendingStep
              ? null
              : <span style={{ color: "#1F3A60", display: "flex" }}><SerIcons.CheckCircle size={22} /></span>
            }
            secondary={sendingStep ? undefined : { label: "Cancel", onClick: () => setShowSendModal(false) }}
            primary={sendingStep ? undefined : {
              label: "Review and Sign",
              iconRight: <SerIcons.Arrow size={16} />,
              disabled: hasErrors,
              onClick: handleSendConfirmation,
            }}
          >
            {sendingStep ? (
              <SendOdooProcessingView step={sendingStep} />
            ) : (
              <React.Fragment>

                {/* Validation errors — design system red */}
                {hasErrors && (
                  <div style={{
                    padding: "12px 16px", borderRadius: 8,
                    background: "#FCE7E5", border: "1px solid #F0C4C1",
                    display: "flex", flexDirection: "column", gap: 8,
                  }}>
                    <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                      <span style={{ display: "flex", flexShrink: 0 }}>
                        <SerIcons.Close size={14} color="#A35854" />
                      </span>
                      <span style={{ fontSize: 13, fontWeight: 600, color: "#A35854", letterSpacing: "-0.1px", lineHeight: "20px" }}>
                        Please complete all required arrangement details before generating the Service Summary Form.
                      </span>
                    </div>
                    <ul style={{ margin: 0, padding: "0 0 0 22px", display: "flex", flexDirection: "column", gap: 4 }}>
                      {sendValidation.errors.map((err, i) => (
                        <li key={i} style={{ fontSize: 13, color: "#A35854", letterSpacing: "-0.1px", lineHeight: "20px" }}>{err}</li>
                      ))}
                    </ul>
                  </div>
                )}

                <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px", lineHeight: "22px" }}>
                  This will generate the official Service Summary Form from the FD App and send it to Odoo for customer signing. The arrangement snapshot will be frozen for review and confirmation.
                </p>

                {caseDetails && (
                  <DetailGrid rows={[
                    { label: "Case",    value: caseDetails.quotationId },
                    { label: "Contact", value: caseDetails.contact },
                    { label: "Package", value: selectedPackage ? selectedPackage.name : "—" },
                    { label: "Casket",  value: selectedCasket  ? `${selectedCasket.name} (${selectedCasket.finish})` : "—" },
                    { label: "Add-ons", value: realAddOnCount > 0 ? `${realAddOnCount} item(s)` : "None" },
                    { label: "Total",   value: formatPrice(sendTotal) },
                  ]} />
                )}


              </React.Fragment>
            )}
          </Modal>
        );
      })()}

      {/* ── PDF / Document Viewer modal ───────────────────────────────
           Opens when FD clicks "View Service Summary Form" or
           "View Signed Form". Represents the Odoo-hosted document.
           In production: live Odoo PDF iframe embedded here.
           In prototype: labelled placeholder with Odoo attribution
           so stakeholders understand the source-of-truth model. */}
      {(() => {
        const isSigned = confirmationStatus === "confirmed" || confirmationStatus === "confirmed_updated";
        const isAwaiting = confirmationStatus === "awaiting" || confirmationStatus === "amended_awaiting";
        const simulateHandler = confirmationStatus === "amended_awaiting"
          ? handleSimulateAmendmentConfirmed
          : handleSimulateConfirmed;

        return (
          <Modal
            open={showPdfModal}
            onClose={() => setShowPdfModal(false)}
            title={isSigned ? "Signed Service Summary Form" : "Service Summary Form"}
            icon={
              isSigned
                ? <span style={{ color: "#5B7D4F", display: "flex" }}><SerIcons.CheckCircle size={22} /></span>
                : <span style={{ color: "#1F3A60", display: "flex" }}><SerIcons.Document size={22} /></span>
            }
            secondary={{ label: "Close", onClick: () => setShowPdfModal(false) }}
          >
            {/* Origin note — always visible; reinforces the source-of-truth model */}
            <div style={{
              display: "flex", alignItems: "center", gap: 8,
              padding: "8px 12px", borderRadius: 8,
              background: "#F0F4F8", border: "1px solid #E5E7EB",
            }}>
              <SerIcons.Building size={13} color="#9CA8B8" />
              <span style={{ fontSize: 12, color: "#6B7A8F", letterSpacing: "-0.1px" }}>
                Service Summary Form is <strong style={{ color: "#1F3A60" }}>generated by FD App</strong> and sent to <strong style={{ color: "#1F3A60" }}>Odoo</strong> for customer signing. The signed version is retrieved from Odoo and shown here.
              </span>
            </div>

            {/* Placeholder PDF frame */}
            <div style={{
              height: 300, borderRadius: 10,
              border: isSigned ? "1.5px solid #C4D9B8" : "1px dashed #D1D5DB",
              background: isSigned ? "#F5FAF3" : "#F9F8F5",
              display: "flex", flexDirection: "column",
              alignItems: "center", justifyContent: "center", gap: 14,
              position: "relative", overflow: "hidden",
            }}>
              {/* Watermark-style Odoo badge */}
              <div style={{
                position: "absolute", top: 12, right: 14,
                fontSize: 10, fontWeight: 700, letterSpacing: "0.08em",
                color: isSigned ? "#9CAE82" : "#C8B597",
                textTransform: "uppercase",
                padding: "3px 8px", borderRadius: 4,
                background: isSigned ? "rgba(156,174,130,0.12)" : "rgba(200,181,151,0.12)",
                border: `1px solid ${isSigned ? "#C4D9B8" : "#E0D0B8"}`,
              }}>
                Odoo · {isSigned ? "Signed" : "Pending signature"}
              </div>

              <SerIcons.Document size={44} color={isSigned ? "#9CAE82" : "#9CA8B8"} />
              <div style={{ textAlign: "center", display: "flex", flexDirection: "column", gap: 4 }}>
                <span style={{ fontSize: 15, fontWeight: 500, color: isSigned ? "#5B7D4F" : "#6B7A8F", letterSpacing: "-0.15px" }}>
                  {isSigned ? "Signed Service Summary Form" : "Service Summary Form"}
                </span>
                <span style={{ fontSize: 12, color: "#9CA8B8", letterSpacing: "-0.1px" }}>
                  {caseDetails ? `${caseDetails.quotationId} · ${caseDetails.deceased}` : ""}
                </span>
              </div>
              {isSigned && (
                <div style={{
                  display: "flex", alignItems: "center", gap: 6,
                  padding: "6px 14px", borderRadius: 20,
                  background: "#E8F0E4", border: "1px solid #C4D9B8",
                  fontSize: 12, fontWeight: 500, color: "#5B7D4F",
                }}>
                  <SerIcons.Check size={13} color="#5B7D4F" />
                  {confirmedAt
                    ? `Signed ${new Date(confirmedAt).toLocaleDateString("en-SG", { day: "numeric", month: "short" })} · Customer confirmed via Odoo`
                    : "Customer signature confirmed via Odoo"
                  }
                </div>
              )}
            </div>

            {/* Prototype-only simulate button — visually distinct from real UI */}
            {isAwaiting && (
              <div style={{
                padding: "12px 14px", borderRadius: 8,
                border: "1px dashed #D1D5DB", background: "#FAFAFA",
              }}>
                <p style={{ fontSize: 11, fontWeight: 500, color: "#9CA8B8", margin: "0 0 10px", letterSpacing: "0.04em", textTransform: "uppercase" }}>
                  Prototype only
                </p>
                <Button
                  variant="dashed" size="sm"
                  style={{ width: "100%", fontSize: 13 }}
                  onClick={simulateHandler}>
                  ⚙ Simulate: Customer Signs &amp; Confirms in Odoo
                </Button>
              </div>
            )}
          </Modal>
        );
      })()}

      {/* ── Amendment Review overlay ─────────────────────────────────
           Premium custom overlay — full layout control, no Modal wrapper.
           Shown before FD finalises an amendment. */}
      {showAmendmentReview && amendmentSnapshot && (() => {
        const origSubtotal  = amendmentSnapshot.confirmedTotal;
        const newSubtotal   =
          (selectedPackage ? selectedPackage.price : 0) +
          (selectedCasket  ? (selectedCasket.included ? 0 : selectedCasket.upgrade) : 0) +
          addOnTotal;
        const origGst       = origSubtotal * 0.09;
        const newGst        = newSubtotal  * 0.09;
        const gstDelta      = newGst - origGst;
        const origPayable   = origSubtotal * 1.09;
        const newPayable    = newSubtotal  * 1.09;
        const totalDelta    = newPayable - origPayable;
        const noChange      = Math.abs(totalDelta) < 0.005;
        const isIncrease    = totalDelta > 0.005;

        const fmtSign = (v) =>
          (v >= 0 ? "+" : "−") + "$" + Math.abs(v).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");

        const deltaLabel = noChange
          ? "—"
          : isIncrease
            ? `+$${Math.abs(totalDelta).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
            : `−$${Math.abs(totalDelta).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`;

        /* Calm financial palette — no amber/warning tones */
        const heroColor  = noChange ? "#9CA8B8" : isIncrease ? "#1F3A60"  : "#2A6B4F";
        const heroBg     = noChange ? "#F8FAFC"  : isIncrease ? "#EEF4FC"  : "#EDF6EE";
        const heroBorder = noChange ? "#E8EDF5"  : isIncrease ? "#C8D8F0"  : "#B8D8C0";
        const heroLabel  = noChange
          ? "No Change to Total"
          : isIncrease ? "Additional Amount Payable" : "Amount Reduced";

        return (
          <div
            onClick={(e) => { if (e.target === e.currentTarget) setShowAmendmentReview(false); }}
            style={{
              position: "fixed", inset: 0, zIndex: 200,
              background: "rgba(10,18,30,0.50)",
              backdropFilter: "blur(2px)",
              display: "flex", alignItems: "center", justifyContent: "center",
              padding: 24,
              animation: "fadeIn 180ms ease-out",
            }}
          >
            <div style={{
              width: "100%", maxWidth: 520,
              background: "#FFFFFF",
              borderRadius: 20,
              boxShadow:
                "0 32px 64px -16px rgba(10,18,30,0.28), " +
                "0 8px 24px -8px rgba(10,18,30,0.12)",
              display: "flex", flexDirection: "column",
              maxHeight: "calc(100vh - 48px)",
              overflow: "hidden",
            }}>

              {/* ── Header ── */}
              <div style={{ padding: "28px 32px 0" }}>
                <div style={{ display: "flex", alignItems: "flex-start", gap: 14, marginBottom: 16 }}>

                  {/* Icon badge */}
                  <div style={{
                    width: 40, height: 40, borderRadius: 10, flexShrink: 0,
                    background: "#EEF3FB", border: "1px solid #D0DCF0",
                    display: "flex", alignItems: "center", justifyContent: "center",
                    marginTop: 2,
                  }}>
                    <SerIcons.Edit size={17} color="#1F3A60" />
                  </div>

                  {/* Title + subtitle */}
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <h2 style={{
                      margin: 0, fontSize: 19, fontWeight: 600,
                      color: "#1F3A60", letterSpacing: "-0.4px", lineHeight: "27px",
                    }}>
                      Finalise Amendment
                    </h2>
                    <p style={{
                      margin: "3px 0 0", fontSize: 13,
                      color: "#9CA8B8", letterSpacing: "-0.1px", lineHeight: "19px",
                    }}>
                      Review the financial impact before saving changes.
                    </p>
                  </div>

                  {/* Close */}
                  <button
                    onClick={() => setShowAmendmentReview(false)}
                    onMouseEnter={(e) => {
                      e.currentTarget.style.background = "#F0F4F8";
                      e.currentTarget.style.color = "#1F3A60";
                    }}
                    onMouseLeave={(e) => {
                      e.currentTarget.style.background = "transparent";
                      e.currentTarget.style.color = "#9CA8B8";
                    }}
                    style={{
                      all: "unset", cursor: "pointer", flexShrink: 0,
                      width: 32, height: 32, borderRadius: 8,
                      display: "flex", alignItems: "center", justifyContent: "center",
                      color: "#9CA8B8",
                      transition: "background 120ms, color 120ms",
                    }}
                  >
                    <SerIcons.Close size={18} />
                  </button>
                </div>

                {/* Context pill */}
                <div style={{ paddingBottom: 22 }}>
                  <span style={{
                    display: "inline-flex", alignItems: "center", gap: 6,
                    padding: "4px 10px", borderRadius: 6,
                    background: "#F4F7FB", border: "1px solid #E0E8F2",
                    fontSize: 11, color: "#6B7A8F", fontWeight: 500, letterSpacing: "-0.05px",
                  }}>
                    <span style={{
                      width: 5, height: 5, borderRadius: "50%",
                      background: "#9CA8B8", flexShrink: 0,
                    }} />
                    Amending a confirmed arrangement
                  </span>
                </div>

                {/* Header bottom border */}
                <div style={{ height: 1, background: "#F0F2F6", margin: "0 -32px" }} />
              </div>

              {/* ── Scrollable content ── */}
              <div style={{
                padding: "22px 32px 0",
                overflowY: "auto", flex: 1, minHeight: 0,
                display: "flex", flexDirection: "column", gap: 12,
              }}>

                {/* ① Financial Comparison Card */}
                <div style={{ border: "1px solid #EDF0F5", borderRadius: 12, overflow: "hidden" }}>

                  {/* Card label */}
                  <div style={{
                    padding: "11px 20px 10px",
                    background: "#FAFBFD",
                    borderBottom: "1px solid #F0F2F6",
                  }}>
                    <span style={{
                      fontSize: 10, fontWeight: 700, color: "#C0CAD6",
                      textTransform: "uppercase", letterSpacing: "0.14em",
                    }}>
                      Financial Comparison
                    </span>
                  </div>

                  {/* Original Subtotal */}
                  <div style={{
                    display: "flex", justifyContent: "space-between", alignItems: "center",
                    padding: "13px 20px",
                  }}>
                    <span style={{ fontSize: 13, color: "#9CA8B8", letterSpacing: "-0.1px" }}>
                      Original Subtotal
                      <span style={{ fontSize: 11, marginLeft: 4 }}>(before GST)</span>
                    </span>
                    <span style={{
                      fontSize: 13, color: "#9CA8B8",
                      fontFeatureSettings: "'tnum'", letterSpacing: "-0.1px",
                    }}>
                      {formatPrice(origSubtotal)}
                    </span>
                  </div>

                  <div style={{ height: 1, background: "#F4F6F9", margin: "0 20px" }} />

                  {/* Updated Subtotal */}
                  <div style={{
                    display: "flex", justifyContent: "space-between", alignItems: "center",
                    padding: "13px 20px",
                  }}>
                    <span style={{ fontSize: 13, color: "#1F3A60", fontWeight: 500, letterSpacing: "-0.1px" }}>
                      Updated Subtotal
                      <span style={{ fontSize: 11, fontWeight: 400, color: "#9CA8B8", marginLeft: 4 }}>(before GST)</span>
                    </span>
                    <span style={{
                      fontSize: 15, fontWeight: 600, color: "#1F3A60",
                      fontFeatureSettings: "'tnum'", letterSpacing: "-0.2px",
                    }}>
                      {formatPrice(newSubtotal)}
                    </span>
                  </div>

                  <div style={{ height: 1, background: "#F4F6F9", margin: "0 20px" }} />

                  {/* GST Adjustment */}
                  <div style={{
                    display: "flex", justifyContent: "space-between", alignItems: "center",
                    padding: "13px 20px 14px",
                    background: "#FAFBFD",
                  }}>
                    <span style={{ fontSize: 13, color: "#6B7A8F", letterSpacing: "-0.1px" }}>
                      GST Adjustment (9%)
                    </span>
                    <span style={{
                      fontSize: 13, fontWeight: 500,
                      color: noChange ? "#9CA8B8" : isIncrease ? "#4A6E9A" : "#2A6B4F",
                      fontFeatureSettings: "'tnum'", letterSpacing: "-0.1px",
                    }}>
                      {noChange ? "—" : fmtSign(gstDelta)}
                    </span>
                  </div>
                </div>

                {/* ② Hero Delta Block */}
                <div style={{
                  padding: "22px 24px",
                  borderRadius: 12,
                  background: heroBg,
                  border: `1px solid ${heroBorder}`,
                  display: "flex", justifyContent: "space-between", alignItems: "center",
                  gap: 16,
                }}>
                  <div style={{ minWidth: 0 }}>
                    <div style={{
                      fontSize: 10, fontWeight: 700, color: heroColor,
                      textTransform: "uppercase", letterSpacing: "0.13em",
                      marginBottom: 8, opacity: 0.65,
                    }}>
                      {heroLabel}
                    </div>
                    <div style={{
                      fontSize: 36, fontWeight: 700, color: heroColor,
                      fontFeatureSettings: "'tnum'", letterSpacing: "-1px",
                      lineHeight: 1,
                    }}>
                      {deltaLabel}
                    </div>
                    <div style={{
                      fontSize: 12, color: "#9CA8B8", marginTop: 10,
                      letterSpacing: "-0.05px",
                    }}>
                      {formatPrice(origPayable)}&nbsp;→&nbsp;{formatPrice(newPayable)}&nbsp;·&nbsp;incl. 9% GST
                    </div>
                  </div>

                  {/* Direction indicator */}
                  {!noChange && (
                    <div style={{
                      width: 44, height: 44, borderRadius: "50%", flexShrink: 0,
                      background: "rgba(255,255,255,0.72)",
                      border: `1.5px solid ${heroBorder}`,
                      display: "flex", alignItems: "center", justifyContent: "center",
                    }}>
                      <svg
                        width="16" height="16" viewBox="0 0 16 16" fill="none"
                        style={{ transform: isIncrease ? "rotate(-90deg)" : "rotate(90deg)" }}
                      >
                        <path d="M8 3 L8 13 M4 9 L8 13 L12 9" stroke={heroColor} strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
                      </svg>
                    </div>
                  )}
                </div>

                {/* ③ Info Banner */}
                <div style={{
                  display: "flex", gap: 12, alignItems: "flex-start",
                  padding: "14px 16px", borderRadius: 10,
                  background: "#F8FAFC", border: "1px solid #E2E8F0",
                  marginBottom: 22,
                }}>
                  <div style={{
                    width: 22, height: 22, borderRadius: 6, flexShrink: 0,
                    background: "#EEF3FB", border: "1px solid #D0DCF0",
                    display: "flex", alignItems: "center", justifyContent: "center",
                  }}>
                    <SerIcons.Notes size={12} color="#6B7A8F" />
                  </div>
                  <p style={{
                    margin: 0, fontSize: 12, color: "#6B7A8F",
                    lineHeight: "19px", letterSpacing: "-0.05px",
                  }}>
                    This amendment updates the Sales Order immediately. Previously signed Service Summary Forms will not reflect these changes unless a new form is generated and re-signed.
                  </p>
                </div>

              </div>

              {/* ── Footer ── */}
              <div style={{
                padding: "16px 32px 28px",
                borderTop: "1px solid #F0F2F6",
                display: "flex", justifyContent: "space-between", alignItems: "center",
              }}>
                {/* Secondary — ghost link */}
                <button
                  onClick={() => setShowAmendmentReview(false)}
                  onMouseEnter={(e) => { e.currentTarget.style.color = "#1F3A60"; }}
                  onMouseLeave={(e) => { e.currentTarget.style.color = "#6B7A8F"; }}
                  style={{
                    all: "unset", cursor: "pointer",
                    fontSize: 14, fontWeight: 500, color: "#6B7A8F",
                    letterSpacing: "-0.1px", padding: "6px 0",
                    fontFamily: "inherit",
                    transition: "color 120ms",
                  }}
                >
                  Keep Editing
                </button>
                {/* Primary */}
                <Button
                  variant="primary-navy" size="md"
                  iconRight={<SerIcons.Arrow size={16} />}
                  onClick={handleFinalizeAmendmentConfirm}
                >
                  Finalise &amp; Save
                </Button>
              </div>

            </div>
          </div>
        );
      })()}

      {/* ── Edit Arrangement warning modal ────────────────────────────
           Shown when FD clicks "Edit Arrangement" while awaiting.
           Warns that editing resets the stage and requires a re-send. */}
      <Modal
        open={showEditWarning}
        onClose={() => setShowEditWarning(false)}
        title="Edit Arrangement?"
        secondary={{ label: "Keep Awaiting Confirmation", onClick: () => setShowEditWarning(false) }}
        primary={{   label: "Edit Arrangement",           onClick: handleEditWarningConfirm }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px" }}>
          Making changes to this arrangement will return it to working status. You will need to re-send for customer confirmation once your changes are complete.
        </p>
      </Modal>

      {/* ── Enter Edit Mode warning ───────────────────────────────────
           Shown whenever FD explicitly clicks "Enter Edit Mode" or
           "Edit Case" on a confirmed arrangement. Forces intentional
           acknowledgement before the amendment workflow begins.
           Auto-entry from add-on modification bypasses this modal. */}
      <Modal
        open={showEnterEditModeWarning}
        onClose={() => { setShowEnterEditModeWarning(false); setEditModeSource(null); }}
        title="Edit Confirmed Arrangement?"
        icon={<span style={{ color: "#9A6B1F", display: "flex" }}><SerIcons.Edit size={20} /></span>}
        secondary={{ label: "Cancel",         onClick: () => { setShowEnterEditModeWarning(false); setEditModeSource(null); } }}
        primary={{   label: "Enter Edit Mode", onClick: handleConfirmEnterEditMode }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px", lineHeight: "22px" }}>
          You are editing a previously confirmed arrangement. Changes made may affect the current Service Summary Form and pricing. Please review all amendments carefully before saving.
        </p>
        <div style={{
          padding: "10px 14px", borderRadius: 8,
          background: "#F9F8F5", border: "1px solid #ECEEF2",
          fontSize: 13, color: "#6B7A8F", letterSpacing: "-0.1px", lineHeight: "20px",
        }}>
          Auto-save is disabled while editing. All changes must be reviewed and saved via <strong style={{ color: "#1F3A60" }}>Confirm Amendment</strong>.
        </div>
      </Modal>

      {/* ── Discard Amendment warning ─────────────────────────────────
           Shown when FD clicks "Discard" on the amendment banner or
           "Discard Changes" in the sidebar. Prevents accidental loss
           of in-progress amendment work. */}
      <Modal
        open={showDiscardWarning}
        onClose={() => setShowDiscardWarning(false)}
        title="Discard Amendment Changes?"
        secondary={{ label: "Continue Editing",  onClick: () => setShowDiscardWarning(false) }}
        primary={{   label: "Discard Changes",   onClick: handleConfirmDiscard }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px", lineHeight: "22px" }}>
          Unsaved amendment changes will be lost and the arrangement will be restored to its last confirmed state. This action cannot be undone.
        </p>
      </Modal>

      {/* ── Exit Amendment Mode warning ───────────────────────────────
           Triggered when the FD tries to navigate to My Cases / My
           Contacts / Case Archive while in amendment_draft. Protects
           against silent data loss since auto-save is off in this mode.
           Brief: "calm but highly visible warning overlay." */}
      <Modal
        open={!!pendingExitNav}
        onClose={() => setPendingExitNav(null)}
        title="Leave Amendment Mode?"
        icon={<span style={{ color: "#B45309", display: "flex" }}><SerIcons.AlertDot size={22} /></span>}
        secondary={{ label: "Continue Editing", onClick: () => setPendingExitNav(null) }}
        primary={{   label: "Discard & Leave",   onClick: handleExitConfirm }}
      >
        <p style={{ fontSize: 14, color: "#574F40", margin: 0, letterSpacing: "-0.15px", lineHeight: "22px" }}>
          Your amendment hasn't been finalised yet. Leaving now will discard your changes and restore the arrangement to its last confirmed state.
        </p>
        <div style={{
          padding: "10px 14px", borderRadius: 8,
          background: "#FFFBF4", border: "1px solid #F4D6A0",
          fontSize: 13, color: "#7A4A0A", letterSpacing: "-0.1px", lineHeight: "20px",
        }}>
          To keep your changes, choose <strong style={{ color: "#1F3A60" }}>Continue Editing</strong> and then use <strong style={{ color: "#1F3A60" }}>Review &amp; Confirm Amendment</strong> in the sidebar to finalise.
        </div>
      </Modal>

      {/* Toast */}
      <Toast message={toast} />
    </div>
  );
}

/* ----- Subtle toast (top-center, slate fill) ----- */
function Toast({ message }) {
  return (
    <div
      aria-live="polite"
      style={{
        position: "fixed",
        top: 100,
        left: "50%",
        transform: `translate(-50%, ${message ? "0" : "-12px"})`,
        opacity: message ? 1 : 0,
        pointerEvents: "none",
        transition: "opacity 220ms ease, transform 220ms ease",
        zIndex: 400,
        background: "var(--slate-700)",
        color: "#FFFFFF",
        padding: "10px 16px",
        borderRadius: "var(--radius-pill)",
        boxShadow: "var(--shadow-overlay)",
        fontSize: 13,
        fontWeight: 500,
        letterSpacing: "-0.15px",
        maxWidth: 420,
      }}
    >
      {message || "\u00A0"}
    </div>
  );
}

function navTitle(screen) {
  switch (screen) {
    case "cases":    return "My Cases";
    case "archived": return "Case Archive";
    case "start":    return "Start New Arrangement";
    case "service":  return "Service Summary";
    case "package":  return "Select Package";
    case "casket":   return "Select Casket";
    case "addons":   return "Add-on Items";
    default:         return "";
  }
}

Object.assign(window, { App, AppContent, Toast });
