/* =============================================================
   page-underlay.jsx — Underlay (circuit) tracker
   ============================================================= */
function PageUnderlay({ model, filters, setFilters, onOpenSite, onOpenInPM, role }) {
  const { underlay } = model;
  const [search, setSearch] = React.useState("");
  const [editingRow, setEditingRow] = React.useState(null);
  const [editingReadiness, setEditingReadiness] = React.useState(null);
  const canEdit = role === "engineer" || role === "po" || role === "admin";
  const newUnderlayIndex = React.useMemo(
    () => buildNewUnderlayReadinessIndex(model.newUnderlay || []),
    [model.newUnderlay]
  );

  // Distinct filter options derived from data
  const opts = React.useMemo(() => {
    return {
      region: PRData.uniqueValues(underlay, "region"),
      country: PRData.uniqueValues(underlay, "country"),
      vendor: PRData.uniqueValues(underlay, "vendor"),
      batch: PRData.uniqueValues(underlay, "batch"),
      criticality: PRData.uniqueValues(underlay, "criticality"),
      circuit: PRData.uniqueValues(underlay, "circuitStatus"),
      risk: PRData.uniqueValues(underlay, "riskLevel"),
    };
  }, [underlay]);

  // Applied filters
  const filtered = React.useMemo(() => {
    const s = search.trim().toLowerCase();
    return underlay.filter((u) => {
      if (filters.region && u.region !== filters.region) return false;
      if (filters.country && u.country !== filters.country) return false;
      if (filters.vendor && u.vendor !== filters.vendor) return false;
      if (filters.batch && u.batch !== filters.batch) return false;
      if (filters.criticality && u.criticality !== filters.criticality) return false;
      if (filters.circuitStatus && u.circuitStatus !== filters.circuitStatus) return false;
      if (filters.risk && u.riskLevel !== filters.risk) return false;
      if (filters.onlyAtRisk && !PRData.isRiskLink(u)) return false;
      if (filters.onlyBlocked && !u.blocker) return false;
      if (s) {
        const hay = [u.siteName, u.linkRef, u.country, u.vendor, u.batch, u.owner, u.blocker, u.nextSteps, u.changeRequestNo, u.changeNo].join(" ").toLowerCase();
        if (!hay.includes(s)) return false;
      }
      return true;
    });
  }, [underlay, filters, search]);

  // KPIs based on filtered set
  const stats = PRData.underlayStats(filtered);

  // Vendor breakdown of filtered
  const vendorBreakdown = React.useMemo(() => PRData.vendorPerformance(filtered), [filtered]);

  // Slipped histogram
  const slipBuckets = React.useMemo(() => PRData.slipBuckets(filtered), [filtered]);

  // Circuit status distribution
  const circuitDist = React.useMemo(() => PRData.statusDistribution(filtered, "circuitStatus", "circuit"), [filtered]);

  const columns = [
    { key: "linkRef", label: "Ref", width: 70, cellClass: "mono strong", render: (r) => r.linkRef || "—" },
    { key: "siteName", label: "Site", render: (r) => (
        <div style={{display:"flex", flexDirection: "column"}}>
          <span className="strong">{r.siteName || "—"}</span>
          <span className="muted" style={{fontSize: 11}}>{r.country} · {r.region}</span>
        </div>
      )},
    { key: "vendor", label: "Vendor", width: 90, render: (r) => r.vendor ? <Pill kind="info">{r.vendor}</Pill> : <span className="muted">—</span> },
    { key: "batch", label: "Batch", width: 90, render: (r) => r.batch || "—" },
    { key: "criticality", label: "Crit.", width: 70, render: (r) => <CriticalityPill value={r.criticality}/> },
    { key: "prOrderStatus", label: "Order", width: 100, render: (r) => <StatusCell value={r.prOrderStatus} kind="task"/> },
    { key: "circuitStatus", label: "Delivery", render: (r) => <StatusCell value={r.circuitStatus} kind="circuit"/>, sortVal: (r) => r.circuitStatus },
    { key: "daysSlipped", label: "Slip", width: 70, cellClass: "num", render: (r) => {
        if (typeof r.daysSlipped !== "number") return <span className="muted">—</span>;
        return <span style={{color: r.daysSlipped > 60 ? "var(--risk)" : r.daysSlipped > 30 ? "var(--warn)" : r.daysSlipped > 0 ? "var(--info)" : "var(--ok)"}}>{r.daysSlipped}d</span>;
      }, sortVal: (r) => r.daysSlipped },
    { key: "atRisk", label: "Risk", width: 110, render: (r) => <RiskPill level={r.riskLevel} atRisk={r.atRisk}/>, sortVal: (r) => (r.atRisk === "Yes" ? 2 : 0) + (r.riskLevel === "High" ? 3 : r.riskLevel === "Medium" ? 2 : r.riskLevel === "Low" ? 1 : 0) },
    { key: "changeRequestNo", label: "CR", width: 115, cellClass: "mono",
      render: (r) => r.changeRequestNo || r.changeNo || <span className="muted">â€”</span>,
      sortVal: (r) => r.changeRequestNo || r.changeNo || "" },
    { key: "plannedChangeDate", label: "Change date", width: 108, cellClass: "mono",
      render: (r) => PRData.fmtDate(r.plannedChangeDate), sortVal: (r) => r.plannedChangeDate },
    { key: "newUnderlayReadiness", label: "Readiness", width: 150, sortable: false,
      render: (r) => <UnderlayReadinessProgress underlayRow={r} match={resolveNewUnderlayReadinessMatch(r, newUnderlayIndex)}/> },
    { key: "relationshipWarnings", label: "Warnings", width: 120, render: (r) => <RelationshipWarningCell row={r}/>, sortVal: (r) => (r._relationshipWarnings || []).length },
    { key: "planned", label: "Planned", width: 100, cellClass: "mono", render: (r) => PRData.fmtDate(r.planned), sortVal: (r) => r.planned },
    { key: "_actions", label: "Actions", width: 255, sortable: false, render: (r) => {
      const readinessMatch = resolveNewUnderlayReadinessMatch(r, newUnderlayIndex);
      const openReadinessEditor = (event) => {
        event.preventDefault();
        event.stopPropagation();
        setEditingReadiness({ underlayRow: r, match: readinessMatch });
      };
      return (
      <div className="flex gap-sm" onClick={(e) => e.stopPropagation()}>
        {canEdit && <button className="btn" style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => setEditingRow(r)}><Glyph name="edit" size={11}/> Edit status</button>}
        <button
          className="btn ul-readiness-action"
          data-match={readinessMatch.confidence}
          style={{ padding: "2px 8px", fontSize: 11 }}
          onMouseDown={openReadinessEditor}
          onClick={openReadinessEditor}
        >
          <Glyph name="edit" size={11}/> Edit readiness
        </button>
        {/* G7: pass linkRef only; if missing, fall back to siteName so the editor at least
            switches to the right sub-tab and search filters to the right site. */}
        {onOpenInPM && <button className="btn ghost" style={{ padding: "2px 8px", fontSize: 11 }} onClick={() => onOpenInPM("underlay", r.linkRef || r.siteName)}>Open in PM</button>}
      </div>
      );
    } },
  ];

  return (
    <div className="page">
      <PageHeader
        title="Underlay tracker"
        subtitle="Circuit ordering, delivery, and risk across all WAN links."
        right={
          <SearchInput value={search} onChange={setSearch} placeholder="Search site, link, vendor, owner…"/>
        }
      />

      {/* KPI strip */}
      <div className="grid g-4 mb-md">
        <KPI accent label="Links in view" value={stats.totalLinks.toLocaleString()} hint={`of ${underlay.length}`} delta=""/>
        <KPI label="Activated" value={stats.activated} hint={`${Math.round((stats.activated/Math.max(1,stats.totalLinks))*100)}%`} delta={`${stats.buildComplete} build complete`} />
        <KPI label="Ordered" value={stats.ordered} hint="PR order placed" delta=""/>
        <KPI label="At risk" value={stats.atRisk} hint={stats.atRisk ? "needs intervention" : "all clear"} delta="" />
      </div>

      {/* Filters */}
      <div className="flex gap-sm mb-md" style={{flexWrap: "wrap"}}>
        <FilterSelect label="Region" value={filters.region} onChange={(v) => setFilters({ ...filters, region: v })} options={opts.region}/>
        <FilterSelect label="Country" value={filters.country} onChange={(v) => setFilters({ ...filters, country: v })} options={opts.country}/>
        <FilterSelect label="Vendor" value={filters.vendor} onChange={(v) => setFilters({ ...filters, vendor: v })} options={opts.vendor}/>
        <FilterSelect label="Batch" value={filters.batch} onChange={(v) => setFilters({ ...filters, batch: v })} options={opts.batch}/>
        <FilterSelect label="Criticality" value={filters.criticality} onChange={(v) => setFilters({ ...filters, criticality: v })} options={opts.criticality}/>
        <FilterSelect label="Status" value={filters.circuitStatus} onChange={(v) => setFilters({ ...filters, circuitStatus: v })} options={opts.circuit}/>
        <FilterSelect label="Risk" value={filters.risk} onChange={(v) => setFilters({ ...filters, risk: v })} options={opts.risk}/>
        <button className={"filter-chip" + (filters.onlyAtRisk ? " on" : "")}
                onClick={() => setFilters({...filters, onlyAtRisk: !filters.onlyAtRisk})}
                style={{cursor:"pointer", background: filters.onlyAtRisk ? "var(--risk-soft)" : undefined, color: filters.onlyAtRisk ? "var(--risk)" : undefined, borderColor: filters.onlyAtRisk ? "transparent" : undefined}}>
          At-risk only
        </button>
        <button className="filter-chip"
                onClick={() => setFilters({...filters, onlyBlocked: !filters.onlyBlocked})}
                style={{cursor:"pointer", background: filters.onlyBlocked ? "var(--warn-soft)" : undefined, color: filters.onlyBlocked ? "var(--warn)" : undefined, borderColor: filters.onlyBlocked ? "transparent" : undefined}}>
          With blocker
        </button>
        {(filters.region||filters.country||filters.vendor||filters.batch||filters.criticality||filters.circuitStatus||filters.risk||filters.onlyAtRisk||filters.onlyBlocked||search) && (
          <button className="btn ghost" onClick={() => { setFilters({}); setSearch(""); }} style={{fontSize: 11.5}}>
            Clear all
          </button>
        )}
      </div>

      <div className="grid g-3 mb-md">
        <div className="card">
          <div className="card-h"><div className="title">Delivery status</div></div>
          <BarChart data={circuitDist} />
        </div>
        <div className="card">
          <div className="card-h"><div className="title">Vendor performance</div></div>
          <table className="data" style={{fontSize: 12}}>
            <thead><tr><th>Vendor</th><th className="right">Links</th><th className="right">Active</th><th className="right">Risk</th><th className="right">Avg slip</th></tr></thead>
            <tbody>
              {vendorBreakdown.map((v) => (
                <tr key={v.vendor}>
                  <td className="strong">{v.vendor}</td>
                  <td className="num right">{v.total}</td>
                  <td className="num right" style={{color: "var(--ok)"}}>{v.activated}</td>
                  <td className="num right" style={{color: v.atRisk ? "var(--risk)" : "var(--ink-400)"}}>{v.atRisk}</td>
                  <td className="num right">{v.avgSlip === null ? "—" : v.avgSlip + "d"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        <div className="card">
          <div className="card-h"><div className="title">Days slipped distribution</div></div>
          <Histogram data={slipBuckets} />
        </div>
      </div>

      <div className="table-wrap">
        <div className="table-toolbar">
          <span className="mono" style={{fontSize: 11.5, color: "var(--ink-500)"}}>{filtered.length.toLocaleString()} of {underlay.length.toLocaleString()} links</span>
          <span style={{flex: 1}}/>
          <span style={{fontSize: 11.5, color: "var(--ink-400)"}}>Click any row for full link detail</span>
        </div>
        <DataTable columns={columns} rows={filtered} onRowClick={(r) => onOpenSite({ site: { siteName: r.siteName, country: r.country, region: r.region, criticality: r.criticality }, link: r })} maxHeight={"65vh"}/>
      </div>

      {editingRow && (
        <TrackerRowEditor
          row={editingRow}
          entity="underlay"
          idResolver={(r) => PRData.resolveOperationalEntityId("underlay", r, model)}
          fields={[
            { key: "prOrderStatus", label: "PR order status", type: "enum", options: ["Ordered", "Cancelled", "To be Ordered", "Pending PR"] },
            { key: "prOrderDate", label: "PR order date", type: "date" },
            { key: "circuitStatus", label: "Circuit delivery status", type: "enum", options: ["Order Submitted", "Order Validated & Accepted", "Survey Complete + CDD Confirmed", "Build Complete (Delivered, Ready for Testing)", "Activated", "On-hold", "Customer request", "Solution Infeasible"] },
            { key: "circuitDate", label: "Circuit delivery date", type: "date" },
            { key: "migrationDate", label: "Link migration date", type: "date" },
            { key: "cdd", label: "CDD", type: "date" },
            { key: "planned", label: "Planned delivery", type: "date" },
            { key: "atRisk", label: "At risk?", type: "boolean" },
            { key: "riskLevel", label: "Risk level", type: "enum", options: ["Low", "Medium", "High"] },
            { key: "blocker", label: "Blockers", type: "longtext" },
            { key: "nextSteps", label: "Next steps", type: "longtext" },
            { key: "owner", label: "Action owner", type: "text" },
            { key: "changeRequestNo", label: "Change request No", type: "text" },
            { key: "plannedChangeDate", label: "Planned change date", type: "date" },
            { key: "changeNo", label: "Change No.", type: "text" },
          ]}
          canEdit={canEdit}
          openInPM={onOpenInPM}
          onClose={() => setEditingRow(null)}
          onSaved={() => {/* GA-13: autosave — model re-renders via PMClient.subscribe; editor stays open */}}
        />
      )}

      {editingReadiness && (
        <UnderlayReadinessQuickEditor
          underlayRow={editingReadiness.underlayRow}
          match={editingReadiness.match}
          canEdit={canEdit}
          openInPM={onOpenInPM}
          onClose={() => setEditingReadiness(null)}
          onSaved={() => {/* GA-13: autosave — editor stays open; model re-renders via PMClient.subscribe */}}
        />
      )}
    </div>
  );
}

function normalizeUnderlayReadinessRef(value) {
  // PMNU-04: share the one canonical Link Ref normalizer used by the resolver.
  return PRData.pmNormalizeIdRef ? PRData.pmNormalizeIdRef("newUnderlay", value)
    : String(value || "").trim().toUpperCase().replace(/\s+/g, "");
}

function buildNewUnderlayReadinessIndex(rows) {
  const map = new Map();
  (rows || []).forEach((row) => {
    const key = normalizeUnderlayReadinessRef(row && row.linkRef);
    if (!key) return;
    if (!map.has(key)) map.set(key, []);
    map.get(key).push(row);
  });
  return map;
}

function resolveNewUnderlayReadinessMatch(underlayRow, index) {
  const key = normalizeUnderlayReadinessRef(underlayRow && underlayRow.linkRef);
  const matches = key && index ? (index.get(key) || []) : [];
  if (!key) return { confidence: "missing", reason: "Underlay row has no Link Ref.", key, matches: [] };
  if (matches.length === 1) return { confidence: "exact", key, row: matches[0], matches };
  if (matches.length > 1) return { confidence: "ambiguous", reason: `${matches.length} New Underlay rows share Link Ref. ${underlayRow.linkRef}`, key, matches };
  return { confidence: "missing", reason: `No New Underlay row exists for Link Ref. ${underlayRow.linkRef}`, key, matches: [] };
}

function underlayReadinessGroups() {
  // PMNU-14: derive the four phase blocks from the canonical classifier
  // (PRData.newUnderlayReadinessPhase) and phase labels — exactly the grouping the PM site
  // editor uses (page-pm.jsx::newUnderlayReadinessUiPhase). Deriving instead of hard-coding
  // keeps the Underlay tracker's phase membership from drifting out of sync with PM and the
  // New underlay editor tab.
  const phases = PRData.NEW_UNDERLAY_PHASES || {};
  const order = [
    { key: "preChangeReadiness", label: phases.preChangeReadiness || "Pre-Change Readiness" },
    { key: "changeManagement",   label: phases.changeManagement   || "Change Management" },
    { key: "linkMigration",      label: phases.linkMigration      || "Link Migration" },
    { key: "postMigration",      label: phases.postMigration      || "Post Migration" },
  ];
  const classify = PRData.newUnderlayReadinessPhase || (() => "postMigration");
  const fields = PRData.NEW_UNDERLAY_READINESS || [];
  return order.map((group) => ({
    ...group,
    fields: fields.filter((field) => classify(field.key) === group.key),
  }));
}

function readinessStats(row) {
  // PMNU-14: delegate to the one canonical New Underlay readiness classifier so the Underlay
  // tracker reports exactly the same readiness %, ready/total, blocked count, and per-phase
  // breakdown as the PM site editor and the monitoring pages (single source of truth, no drift).
  if (PRData.newUnderlayReadinessStats) {
    const s = PRData.newUnderlayReadinessStats(row || {});
    return {
      ready: s.readyReadiness || 0,
      blocked: s.blockedReadinessCount || 0,
      total: s.totalReadiness || 1,
      pct: s.readinessPercent || 0,
      phases: s.phases || {},
    };
  }
  // Fallback only if the canonical helper is unavailable (keeps the tracker resilient).
  const fields = PRData.NEW_UNDERLAY_READINESS || [];
  let ready = 0;
  let blocked = 0;
  fields.forEach((field) => {
    const value = row && row.readiness ? row.readiness[field.key] : "";
    const bucket = PRData.statusBucket(value, "task");
    if (bucket === "ok") ready++;
    if (bucket === "risk") blocked++;
  });
  const total = fields.length || 1;
  return { ready, blocked, total, pct: Math.round((ready / total) * 100), phases: {} };
}

function UnderlayReadinessProgress({ underlayRow, match }) {
  if (!match || match.confidence !== "exact") {
    return (
      <span className="ul-readiness-missing" title={match && match.reason ? match.reason : "No New Underlay row"}>
        No readiness row
      </span>
    );
  }
  const stats = readinessStats(match.row);
  return (
    <div className="ul-readiness-progress" title={`New Underlay readiness for ${underlayRow.linkRef || ""}`}>
      <div className="ul-readiness-top">
        <span className="mono">{stats.ready}/{stats.total}</span>
        {stats.blocked > 0 && <span className="ul-readiness-blocked">{stats.blocked} blocked</span>}
      </div>
      <div className="ul-readiness-bar">
        <span style={{ width: `${stats.pct}%` }}/>
      </div>
      <div className="ul-readiness-pct">{stats.pct}% complete</div>
    </div>
  );
}

// GA-13 fix: UnderlayReadinessQuickEditor converted from batched draft+Save button to
// per-field autosave. Each enum select commits immediately via PMClient.patchNewUnderlay
// (single field, with __meta provenance) — no explicit Save click needed. Keeps 9 mirror
// fields read-only (they are rendered as display-only via ReadinessMirrorValue, never
// patched here). Editor stays open after each autosave (onSaved is a no-op in the parent).
function UnderlayReadinessQuickEditor({ underlayRow, match, canEdit, openInPM, onClose, onSaved }) {
  const row = match && match.row;
  // GA-13: per-field local state + autosave — no batched draft, no Save button.
  const [localValues, setLocalValues] = React.useState(() => ({ ...((row && row.readiness) || {}) }));
  const [savingField, setSavingField] = React.useState(null);
  const [savedFlash, setSavedFlash] = React.useState(null);
  const [err, setErr] = React.useState("");
  const groups = React.useMemo(() => underlayReadinessGroups(), []);
  const stats = readinessStats({ readiness: localValues });
  const options = PRData.NEW_UNDERLAY_WORKFLOW_STATUS_OPTIONS || ["Not Started", "In Progress", "Ready", "Completed", "Blocked", "Failed", "Yes", "No"];
  const canWrite = canEdit && match && match.confidence === "exact" && row && row.linkRef;

  // Autosave a single readiness field immediately on change.
  const saveField = async (key, value) => {
    if (!canWrite) return;
    setErr("");
    setSavingField(key);
    const patchKey = `readiness.${key}`;
    const oldValue = (row && row.readiness) ? (row.readiness[key] || "") : "";
    try {
      await PMClient.patchNewUnderlay(row.linkRef, {
        [patchKey]: value,
        __meta: {
          [patchKey]: {
            workbookValue: oldValue,
            sourceEntity: "newUnderlay",
            sourceField: patchKey,
          },
        },
      });
      setSavedFlash(key);
      setTimeout(() => setSavedFlash((cur) => cur === key ? null : cur), 1500);
      onSaved && onSaved();
    } catch (error) {
      setErr("Save failed: " + (error.message || error));
    } finally {
      setSavingField((cur) => cur === key ? null : cur);
    }
  };

  return (
    <div className="pm-modal-backdrop" onClick={onClose}>
      <div className="pm-modal large ul-readiness-modal" data-readiness-linkref={(match && match.row && match.row.linkRef) || underlayRow.linkRef || ""} onClick={(e) => e.stopPropagation()}>
        <div className="pm-modal-h">
          <div className="title">
            Edit readiness
            <span className="mono" style={{ marginLeft: 8, color: "var(--ink-500)" }}>- {underlayRow.linkRef || "-"}</span>
            {match && match.confidence !== "exact" && (
              <span className={"pill " + (match.confidence === "ambiguous" ? "risk" : "warn")} style={{ marginLeft: 8 }}>{match.confidence}</span>
            )}
          </div>
          <button className="btn ghost" onClick={onClose}><Glyph name="close" size={14}/></button>
        </div>
        <div className="pm-modal-body">
          {/* GA-18: shared local-only vs server-backed indicator (same component as PM). */}
          <StorageModePill/>
          {!canEdit && <div className="pm-readonly-pill" style={{ marginBottom: 8 }}>Read-only - management cannot edit</div>}
          {canEdit && canWrite && (
            <div className="pm-readonly-pill" style={{ marginBottom: 8 }}>Changes autosave as you edit — no Save button.</div>
          )}
          {(!match || match.confidence !== "exact") && (
            <div className="pm-readonly-pill" style={{ marginBottom: 10, background: "var(--warn-soft)", color: "var(--warn)" }}>
              {match && match.reason ? match.reason : "No matching New Underlay row exists for this Underlay link."}
              <div style={{ marginTop: 4 }}>No fake New Underlay data was created. Select an existing safe row in Project Management if needed.</div>
            </div>
          )}

          <div className="ul-readiness-summary">
            <div>
              <div className="pm-field-label">Ready</div>
              <strong>{stats.ready}/{stats.total}</strong>
            </div>
            <div>
              <div className="pm-field-label">Blocked</div>
              <strong className={stats.blocked ? "risk-text" : ""}>{stats.blocked}</strong>
            </div>
            <div>
              <div className="pm-field-label">Completion</div>
              <strong>{stats.pct}%</strong>
            </div>
          </div>

          {row && stats.phases && (
            <div className="pm-fieldgroup">
              <div className="pm-fieldgroup-h">Phase readiness</div>
              <div className="pm-fieldgroup-desc">The same four New Underlay phases shown in Project Management for this Link Ref.</div>
              <div className="pm-nu-phasegrid" data-nu-phasegrid="true">
                {groups.map((group) => {
                  const p = stats.phases[group.key] || {};
                  return (
                    <div key={group.key} className="pm-nu-phase" data-phase={group.key}>
                      <span>{group.label}</span>
                      <strong>{(p.ready || 0)}/{(p.total || 0)}</strong>
                      <div className="pm-nu-bar"><i style={{ width: `${Math.max(0, Math.min(100, p.percent || 0))}%` }}/></div>
                      {(p.blocked || 0) > 0 && <em>{p.blocked} blocked</em>}
                    </div>
                  );
                })}
              </div>
            </div>
          )}

          <div className="pm-fieldgroup">
            <div className="pm-fieldgroup-h">Underlay mirrored values</div>
            <div className="pm-fieldgroup-desc">Read-only values mirrored from the Underlay link by Link Ref. Edit them on the Underlay row, not here.</div>
            <div className="ul-readiness-mirror-grid">
              {(PRData.NEW_UNDERLAY_MIRRORED_FIELDS || []).map((field) => (
                <ReadinessMirrorValue key={field.key} label={field.label} value={mirrorDisplayValue(field, row, underlayRow)}/>
              ))}
            </div>
          </div>

          {row && groups.map((group) => (
            <div key={group.key} className="pm-fieldgroup">
              <div className="pm-fieldgroup-h">{group.label}</div>
              <div className="ul-readiness-grid">
                {group.fields.map((field) => (
                  <div key={field.key} className="pm-field" data-field-key={`readiness.${field.key}`}>
                    <label className="pm-field-label">{field.label}</label>
                    <div className="pm-field-input">
                      <select
                        className="pm-input"
                        aria-label={field.label}
                        value={localValues[field.key] || ""}
                        disabled={!canWrite || savingField === field.key}
                        onChange={(event) => {
                          const v = event.target.value;
                          setLocalValues((cur) => ({ ...cur, [field.key]: v }));
                          saveField(field.key, v);
                        }}
                      >
                        <option value="">-</option>
                        {options.map((option) => <option key={option} value={option}>{option}</option>)}
                      </select>
                      {savedFlash === field.key && <span className="pm-saved-pill">Saved</span>}
                      {savingField === field.key && <span className="pm-saving-spinner"/>}
                    </div>
                  </div>
                ))}
              </div>
            </div>
          ))}
          {err && <div className="pm-readonly-pill" style={{ marginTop: 10, background: "var(--risk-soft)", color: "var(--risk)" }}>{err}</div>}
        </div>
        <div className="pm-modal-actions">
          {openInPM && row && row.linkRef && (
            <button className="btn" onClick={() => { onClose(); openInPM("newUnderlay", row.linkRef); }}>
              <Glyph name="ext" size={12}/> Open New Underlay in PM
            </button>
          )}
          <span style={{ flex: 1 }}/>
          <button className="btn primary" onClick={onClose}>Done</button>
        </div>
      </div>
    </div>
  );
}

const UL_MIRROR_DATE_KEYS = new Set(["cdd", "circuitDeliveryDate", "changeDate"]);

function mirrorDisplayValue(field, row, underlayRow) {
  // PMNU-14: render each of the 9 Underlay-mirrored New Underlay fields read-only, sourced
  // from the matched New Underlay row when present, otherwise from the Underlay row's source
  // fields per the data.js mirror contract (field.sourceFields). Mirrored values are never
  // editable from this surface, so the tracker cannot create a source-of-truth conflict.
  let raw = row ? row[field.key] : "";
  if ((raw === undefined || raw === null || raw === "") && underlayRow) {
    for (const sf of (field.sourceFields || [])) {
      if (underlayRow[sf] !== undefined && underlayRow[sf] !== null && underlayRow[sf] !== "") {
        raw = underlayRow[sf];
        break;
      }
    }
  }
  if (raw === undefined || raw === null || raw === "") return "—";
  if (UL_MIRROR_DATE_KEYS.has(field.key)) return PRData.fmtDate(raw) || "—";
  return String(raw);
}

function ReadinessMirrorValue({ label, value }) {
  return (
    <div className="ul-readiness-mirror">
      <span>{label}</span>
      <strong>{value || "-"}</strong>
      <em>from Underlay link</em>
    </div>
  );
}

window.PageUnderlay = PageUnderlay;
