/* ════════════════════════════════════════════════════════════════
   OWNER / CLIENT / HERD MANAGEMENT ENGINE
   ════════════════════════════════════════════════════════════════ */

/* ── Demo owners ── */
const DEMO_OWNERS = [
  {
    id: "ow1",
    herdCode: "LK-HRD-001",
    herdName: "Karimi Holstein Farm",
    breedFocus: "Holstein",
    established: "2019-03-01",
    name: "Ahmad Karimi",
    phone: "+93-700-111001",
    email: "ahmad.karimi@farm.af",
    district: "Paghman",
    province: "Kabul",
    address: "Farm No.12, Paghman Valley, Kabul",
    notes: "Primary Holstein grading-up program. AI from USA and Canadian bulls.",
  },
  {
    id: "ow2",
    herdCode: "LK-HRD-002",
    herdName: "Hassan Montbéliarde Herd",
    breedFocus: "Montbéliarde",
    established: "2020-07-15",
    name: "Mohammad Hassan",
    phone: "+93-799-222002",
    email: "m.hassan@livestock.af",
    district: "Guzara",
    province: "Herat",
    address: "Block 5, Guzara Dairy Zone, Herat",
    notes: "Montbéliarde crossbreeding for dual-purpose production and cheese yield.",
  },
  {
    id: "ow3",
    herdCode: "LK-HRD-003",
    herdName: "Mohammadi Dairy Cooperative",
    breedFocus: "Holstein",
    established: "2021-01-10",
    name: "Sara Mohammadi",
    phone: "+93-700-333003",
    email: "sara.m@agri.af",
    district: "Dehdadi",
    province: "Balkh",
    address: "Dehdadi Agricultural Cooperative, Balkh",
    notes: "Community cooperative. Grading-up with Holstein AI from F1 → F4 program.",
  },
];

/* ── Admin-only (no switching) ── */
const session = { role: "admin", ownerId: null };
function saveSession() {}

/* ── Owners storage ── */
let owners = [];
function loadOwners() {
  try {
    owners = JSON.parse(localStorage.getItem("lp_owners") || "null") || [];
  } catch (e) {
    owners = [];
  }
  if (!owners.length) {
    owners = JSON.parse(JSON.stringify(DEMO_OWNERS));
    saveOwners();
  }
}
function saveOwners() {
  try {
    localStorage.setItem("lp_owners", JSON.stringify(owners));
  } catch (e) {
    console.warn("saveOwners failed:", e);
  }
}
function ownerById(id) {
  return owners.find(o => o.id === id);
}

/* ── Animal filter by session ── */
function visibleAnimals() {
  return animals;
}
function isAdmin() {
  return true;
}

/* ── Populate owner dropdown in form ── */
function populateOwnerSelect(selectedId) {
  const sel = document.getElementById("f_ownerId");
  if (!sel) return;
  const herdOpts = owners
    .map(
      o =>
        `<option value="${o.id}"${o.id === selectedId ? " selected" : ""}>[Herd] ${o.herdName || o.name} · ${o.name} · ${o.province}</option>`
    )
    .join("");
  const indivOpts = indivOwners
    .map(
      o =>
        `<option value="${o.id}"${o.id === selectedId ? " selected" : ""}>[Individual] ${o.name} · ${o.province || ""}</option>`
    )
    .join("");
  sel.innerHTML = `<option value="">— Select owner —</option><optgroup label="Registered Herds">${herdOpts}</optgroup><optgroup label="Individual Owners">${indivOpts}</optgroup>`;
}

/* ── Owner filter state ── */
let ownerFilter = "";
function setOwnerF(id) {
  ownerFilter = id;
  renderSB();
} // kept for Herds page herd-view button

/* ── Render owner filter bar ── */
function renderOwnerBar() {
  // Populate the sire filter dropdown with registered breeding sires
  const sireEl = document.getElementById("sb_sire");
  if (sireEl) {
    const sires = animals.filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role));
    sireEl.innerHTML =
      '<option value="">🐂 All Sires</option>' +
      sires.map(a => `<option value="${a.id}">${a.name} (${a.regId || "—"})</option>`).join("");
  }
  // Populate province filter with AF provinces, bold ones that have animals
  const provEl = document.getElementById("sb_prov");
  if (provEl) {
    const usedProvs = new Set(animals.map(a => a.province).filter(Boolean));
    const allProvs = typeof getProvincesAF === "function" ? getProvincesAF() : [];
    provEl.innerHTML =
      '<option value="">📍 All Provinces</option>' +
      allProvs
        .map(
          p =>
            `<option value="${p}"${usedProvs.has(p) ? ' style="font-weight:bold"' : ""}>${p}</option>`
        )
        .join("");
  }
}

function renderSessionBadge() {}

let animals = [],
  curId = null,
  editId = null,
  curBreed = "Holstein",
  curTab = "pedigree";
let selSireId = null,
  selDamId = null,
  sbf = "all",
  kinSel = new Set(),
  pendPh = {},
  pedGens = 2;

/* ── Scalable DB mode (MySQL via api_db.php) ──
   When true, the herd register is served one page at a time and the
   global `animals` array is only a CACHE of currently-loaded animals
   (the visible sidebar page + the viewed animal, its parents & offspring).
   When false, the app uses its original in-memory / localStorage behaviour. */
let DB_MODE = false;
let sbPage = 1, sbTotal = 0, sbPages = 1, sbPerPage = 50, sbLoading = false;

/* Upsert rows into the in-memory cache (`animals`) without duplicating. */
function lpCacheMerge(rows) {
  if (!Array.isArray(rows)) rows = [rows];
  rows.forEach(r => {
    if (!r || !r.id) return;
    const i = animals.findIndex(a => a && String(a.id) === String(r.id));
    if (i >= 0) animals[i] = r;
    else animals.push(r);
  });
}

/* ── STARTUP — runs after the full page DOM is ready ── */

/* ════════════════════════════════════════════════════════════════
   AUTH ENGINE
   ════════════════════════════════════════════════════════════════ */
const AUTH_KEY = "lp_users_v1";
const SESS_KEY = "lp_session_v1";

/* Default admin account — password can be changed after first login */
const DEFAULT_ADMIN = {
  id: "admin",
  username: "admin",
  password: "larspeenghar2024",
  role: "admin",
  ownerId: null,
  name: "Administrator",
};

function getUsers() {
  try {
    const u = JSON.parse(localStorage.getItem(AUTH_KEY) || "null");
    return u && u.length ? u : [DEFAULT_ADMIN];
  } catch (e) {
    return [DEFAULT_ADMIN];
  }
}
function saveUsers(users) {
  try {
    localStorage.setItem(AUTH_KEY, JSON.stringify(users));
  } catch (e) {
    console.warn("saveUsers failed:", e);
  }
}

function getSession() {
  try {
    return JSON.parse(sessionStorage.getItem(SESS_KEY) || "null");
  } catch (e) {
    return null;
  }
}
function setSession(user) {
  const s = {
    id: user.id,
    username: user.username,
    role: user.role,
    ownerId: user.ownerId || null,
    name: user.name || user.username,
  };
  try {
    sessionStorage.setItem(SESS_KEY, JSON.stringify(s));
  } catch (e) {}
  return s;
}
function clearSession() {
  try {
    sessionStorage.removeItem(SESS_KEY);
  } catch (e) {}
}
function isAdminSession() {
  const s = getSession();
  return s && s.role === "admin";
}

/* ── Login action ── */
async function doLogin() {
  const uname = (document.getElementById("lg_user")?.value || "").trim().toLowerCase();
  const pass = (document.getElementById("lg_pass")?.value || "").trim();
  const errEl = document.getElementById("loginError");
  if (!uname || !pass) {
    if (errEl) {
      errEl.textContent = "Please enter username and password.";
      errEl.style.display = "block";
    }
    return;
  }

  // Scalable DB backend (api_db.php) takes precedence when present.
  if (DB_MODE) {
    try {
      const user = await window.LpDB.login(uname, pass);
      if (errEl) errEl.style.display = "none";
      refreshLogos();
      const sess = setSession(user);
      document.getElementById("loginGate").style.display = "none";
      if (sess.role === "admin") {
        document.querySelector("header").style.display = "";
        document.querySelector(".shell").style.display = "";
        startApp();
      } else {
        showUserPortal(sess);
      }
    } catch (e) {
      if (errEl) {
        errEl.textContent = "❌ " + (e && e.message ? e.message : "Login failed.");
        errEl.style.display = "block";
      }
      const pEl = document.getElementById("lg_pass");
      if (pEl) { pEl.value = ""; pEl.focus(); }
    }
    return;
  }

  // Authenticate against the server (api.php) if reachable; otherwise fall back
  // to the legacy localStorage check so the app still works locally / offline.
  let user = null;
  try {
    user = await window.LpSync.login(uname, pass);
  } catch (e) {
    if (e && e.noBackend) {
      // No PHP backend — local-only mode. Check against the in-browser users list.
      const users = getUsers();
      user = users.find(
        u =>
          u &&
          typeof u.username === "string" &&
          u.username.toLowerCase() === uname &&
          u.password === pass
      );
      if (!user) {
        if (errEl) {
          errEl.textContent = "❌ Incorrect username or password.";
          errEl.style.display = "block";
        }
        const pEl = document.getElementById("lg_pass");
        if (pEl) { pEl.value = ""; pEl.focus(); }
        return;
      }
    } else {
      if (errEl) {
        errEl.textContent = "❌ " + (e && e.message ? e.message : "Login failed.");
        errEl.style.display = "block";
      }
      const pEl = document.getElementById("lg_pass");
      if (pEl) { pEl.value = ""; pEl.focus(); }
      return;
    }
  }

  if (errEl) errEl.style.display = "none";
  refreshLogos();
  const sess = setSession(user);
  document.getElementById("loginGate").style.display = "none";
  if (sess.role === "admin") {
    document.querySelector("header").style.display = "";
    document.querySelector(".shell").style.display = "";
    startApp();
  } else {
    showUserPortal(sess);
  }
}

/* ── Logout ── */
async function doLogout() {
  try { await window.LpSync.logout(); } catch (e) {}
  clearSession();
  window.location.reload();
}

/* ════════════════════════════════════════════════════════════════
   USER PORTAL ENGINE
   Completely separate from pedigree app — users cannot access admin functions
   ════════════════════════════════════════════════════════════════ */

const MSG_KEY = "lp_messages_v1";

/* ── Message helpers ── */
function getMessages() {
  try {
    return JSON.parse(localStorage.getItem(MSG_KEY) || "[]");
  } catch (e) {
    return [];
  }
}
function saveMessages(msgs) {
  try {
    localStorage.setItem(MSG_KEY, JSON.stringify(msgs));
  } catch (e) {
    console.warn("saveMessages failed:", e);
  }
}
function addMessage(msg) {
  const msgs = getMessages();
  msg.id = "msg_" + Date.now() + "_" + Math.random().toString(36).slice(2, 6);
  msg.timestamp = new Date().toISOString();
  msgs.push(msg);
  saveMessages(msgs);
  return msg.id;
}
function markRead(id) {
  const msgs = getMessages();
  const m = msgs.find(x => x.id === id);
  if (m) {
    m.read = true;
    saveMessages(msgs);
  }
}
function deleteMessage(id) {
  saveMessages(getMessages().filter(m => m.id !== id));
}

/* ── Province cascade for user portal forms ── */
function upCascadeDistrict(provSel, distId) {
  const prov = typeof provSel === "string" ? provSel : provSel.value || "";
  const distEl = document.getElementById(distId);
  if (!distEl) return;
  const dists = typeof getDistrictsAF === "function" ? getDistrictsAF(prov) : [];
  distEl.innerHTML =
    '<option value="">— Select District —</option>' +
    dists.map(d => `<option>${d}</option>`).join("");
}

/* ── Current user portal session ── */
let UP_SESS = null;
let UP_VIEW = "dashboard";

/* ── Entry point: called after user login ── */
function showUserPortal(sess) {
  if (typeof lpHideLanding === "function") lpHideLanding();
  UP_SESS = sess;
  document.getElementById("userPortal").style.display = "block";
  const un = document.getElementById("up_userName");
  if (un) un.textContent = "👤 " + (sess.name || sess.username);
  upLocalize();
  upNav("dashboard");
  upUpdateBadges();
}

/* ── User-portal translations (Pashto / Dari / English) ──
   Reuses the shared i18n dictionary + language preference. */
function upT(key) {
  try {
    const i = lpGetI18n();
    const l = lpGetLang();
    return (i[l] && i[l][key]) || (i.en && i.en[key]) || key;
  } catch (e) {
    return key;
  }
}
function upBuildLangs() {
  const el = document.getElementById("up_langs");
  if (!el) return;
  const langs = window.LP_LANGS || [
    { code: "en", native: "English" }, { code: "ps", native: "پښتو" }, { code: "fa", native: "دری" },
  ];
  const cur = lpGetLang();
  el.innerHTML = langs
    .map(
      l => `<button onclick="upSetLang('${l.code}')" style="border:none;cursor:pointer;font:inherit;font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;white-space:nowrap;background:${l.code === cur ? "#fff" : "transparent"};color:${l.code === cur ? "#1a4a2e" : "rgba(255,255,255,.85)"}">${l.native}</button>`
    )
    .join("");
}
function upLocalize() {
  const root = document.getElementById("userPortal");
  if (!root) return;
  root.setAttribute("dir", ["ps", "fa"].includes(lpGetLang()) ? "rtl" : "ltr");
  root.querySelectorAll("[data-upt]").forEach(el => { el.textContent = upT(el.getAttribute("data-upt")); });
  const set = (id, key) => { const e = document.getElementById(id); if (e) e.textContent = upT(key); };
  set("up_portalName", "up_portal_name");
  set("up_navHeading", "up_nav");
  const so = document.getElementById("up_signoutBtn");
  if (so) so.textContent = "⬅ " + upT("up_signout");
  const ur = document.getElementById("up_userRole");
  if (ur && UP_SESS) ur.textContent = UP_SESS.ownerId ? upT("up_role_owner") : upT("up_role_user");
  upBuildLangs();
}
function upSetLang(code) {
  if (typeof lpSetLang === "function") lpSetLang(code);
  upLocalize();
  upNav(UP_VIEW || "dashboard");
}

/* ── Navigation ── */
function upNav(view) {
  UP_VIEW = view;
  document.querySelectorAll(".up-nav-btn").forEach(b => b.classList.remove("active"));
  const btn = document.getElementById("up-nav-" + view);
  if (btn) btn.classList.add("active");
  const main = document.getElementById("up_main");
  if (!main) return;
  switch (view) {
    case "dashboard":
      upRenderDashboard();
      break;
    case "request":
      upRenderRequestForm();
      break;
    case "inbox":
      upRenderInbox();
      break;
    case "sent":
      upRenderSent();
      break;
    case "password":
      upRenderAccount();
      break;
    default:
      upRenderDashboard();
  }
}

/* ── Update notification badges ── */
function upUpdateBadges() {
  const msgs = getMessages();
  const unread = msgs.filter(m => m.to === UP_SESS.id && !m.read).length;
  const badge = document.getElementById("up_inboxBadge");
  if (badge) {
    badge.style.display = unread > 0 ? "inline-flex" : "none";
    badge.textContent = unread;
  }
}

/* ════════════════════════════════════════════════════════════════
   DASHBOARD
════════════════════════════════════════════════════════════════ */
function upRenderDashboard() {
  const msgs = getMessages();
  const sent = msgs.filter(m => m.from === UP_SESS.id && m.type === "pedigree_request");
  const inbox = msgs.filter(m => m.to === UP_SESS.id);
  const unread = inbox.filter(m => !m.read).length;
  const pending = sent.filter(m => m.status === "pending").length;
  const replied = sent.filter(m => m.status === "replied").length;
  const done = sent.filter(m => m.status === "completed").length;

  document.getElementById("up_main").innerHTML = `
  <!-- Welcome -->
  <div style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);border-radius:10px;padding:18px 22px;color:#fff;margin-bottom:16px;-webkit-print-color-adjust:exact;print-color-adjust:exact">
    <div style="font-size:16px;font-weight:700">${upT("up_dash_welcome")}, ${UP_SESS.name || UP_SESS.username} 👋</div>
    <div style="font-size:11px;opacity:.8;margin-top:4px">${upT("up_dash_subtitle")}</div>
  </div>

  <!-- Stats -->
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:12px;margin-bottom:20px">
    <div class="up-stat2">
      <div class="n" style="color:${unread > 0 ? "#ef4444" : "#1a4a2e"}">${unread}</div>
      <div class="l">📥 ${upT("up_stat_unread")}</div>
    </div>
    <div class="up-stat2">
      <div class="n" style="color:#854d0e">${pending}</div>
      <div class="l">⏳ ${upT("up_stat_pending")}</div>
    </div>
    <div class="up-stat2">
      <div class="n" style="color:#1e40af">${replied}</div>
      <div class="l">💬 ${upT("up_stat_replied")}</div>
    </div>
    <div class="up-stat2">
      <div class="n" style="color:#065f46">${done}</div>
      <div class="l">✅ ${upT("up_stat_completed")}</div>
    </div>
  </div>

  <!-- Quick actions -->
  <div class="up-card2" style="margin-bottom:16px">
    <div class="up-card2-hdr"><h3>⚡ ${upT("up_dash_quick")}</h3></div>
    <div class="up-card2-body" style="display:flex;gap:10px;flex-wrap:wrap">
      <button class="up-btn up-btn-green" onclick="upNav('request')">📝 ${upT("up_req_title")}</button>
      <button class="up-btn up-btn-blue"  onclick="upNav('inbox')">📥 ${upT("up_inbox")} ${unread > 0 ? `<span style="background:#ef4444;color:#fff;border-radius:8px;padding:1px 6px;font-size:10px;margin-left:5px">${unread} ${upT("up_new")}</span>` : ""}</button>
      <button class="up-btn up-btn-gray"  onclick="upNav('sent')">📤 ${upT("up_sent")}</button>
    </div>
  </div>

  <!-- How it works -->
  <div class="up-card2">
    <div class="up-card2-hdr"><h3>ℹ️ ${upT("up_how_title")}</h3></div>
    <div class="up-card2-body">
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:12px">
        ${[
          ["1️⃣", upT("up_step1_t"), upT("up_step1_d")],
          ["2️⃣", upT("up_step2_t"), upT("up_step2_d")],
          ["3️⃣", upT("up_step3_t"), upT("up_step3_d")],
          ["4️⃣", upT("up_step4_t"), upT("up_step4_d")],
        ]
          .map(
            ([
              n,
              t,
              d,
            ]) => `<div style="display:flex;gap:10px;align-items:flex-start;padding:10px;background:#f8fafc;border-radius:8px;border:1px solid #e2e8f0">
          <span style="font-size:20px;flex-shrink:0">${n}</span>
          <div><div style="font-size:12px;font-weight:700;color:#1a2e1a;margin-bottom:3px">${t}</div>
          <div style="font-size:11px;color:#6b7280;line-height:1.5">${d}</div></div>
        </div>`
          )
          .join("")}
      </div>
    </div>
  </div>

  <!-- Recent inbox preview -->
  ${
    inbox.length
      ? `
  <div class="up-card2">
    <div class="up-card2-hdr">
      <h3>📥 ${upT("up_recent")}</h3>
      <button class="up-btn up-btn-gray" style="padding:5px 12px;font-size:11px" onclick="upNav('inbox')">${upT("up_viewall")}</button>
    </div>
    <div class="up-card2-body" style="padding:10px 14px">
      ${inbox
        .slice(-3)
        .reverse()
        .map(m => upMsgPreviewCard(m))
        .join("")}
    </div>
  </div>`
      : ""
  }
  `;
}

/* ════════════════════════════════════════════════════════════════
   PEDIGREE REQUEST FORM
════════════════════════════════════════════════════════════════ */
/* ════════════════════════════════════════════════════════════════
   IMPROVED PEDIGREE REQUEST FORM
   - Two clear sections: New Animal + Dam (mother) fully optional
   - Email optional, sex required, DOB/earTag/regNo on animal
   ════════════════════════════════════════════════════════════════ */
function upRenderRequestForm() {
  const provOpts =
    typeof getProvincesAF === "function"
      ? `<option value="">— Select Province —</option>${getProvincesAF()
          .map(p => `<option>${p}</option>`)
          .join("")}`
      : `<option value="">— Province —</option>`;

  document.getElementById("up_main").innerHTML = `
  <div class="up-card2">
    <div class="up-card2-hdr" style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h3 style="color:#fff">📝 ${upT("up_req_title")}</h3>
      <span style="color:rgba(255,255,255,.75);font-size:10px">${upT("up_req_sub")}</span>
    </div>
    <div class="up-card2-body">
      <form id="reqForm" onsubmit="submitPedigreeRequest(event)" autocomplete="off">

        <!-- ═══ SECTION 1: AI / SIRE ═══ -->
        <div style="background:#fffbeb;border:1px solid #fde68a;border-radius:8px;padding:14px 16px;margin-bottom:16px">
          <div style="font-size:12px;font-weight:700;color:#92400e;margin-bottom:12px">
            🐂 ${upT("up_sec_sire")}
          </div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:11px">
            <div class="up-field" style="grid-column:span 2">
              <label>${upT("up_f_sirename")} *</label>
              <div style="position:relative">
                <input id="req_sireName" name="sireName" required
                  placeholder="Type to search registered bulls, or enter AI code manually…"
                  oninput="upSireSearch(this)" onfocus="upSireSearch(this)" autocomplete="off"
                  style="width:100%;padding:9px 12px;border:1.5px solid #d1d5db;border-radius:7px;font-size:13px;background:#fff;outline:none">
                <div id="req_sireDD" style="display:none;position:absolute;top:100%;left:0;right:0;background:#fff;border:1.5px solid #fde68a;border-radius:0 0 8px 8px;box-shadow:0 4px 12px rgba(0,0,0,.15);z-index:200;max-height:220px;overflow-y:auto"></div>
              </div>
              <div id="req_sireSelected" style="display:none;margin-top:5px;background:#fffbeb;border:1px solid #fde68a;border-radius:6px;padding:6px 10px;font-size:11px;font-family:sans-serif;color:#92400e;display:flex;align-items:center;justify-content:space-between;gap:8px">
                <span id="req_sireSelectedLabel"></span>
                <button type="button" onclick="upSireClear()" style="background:none;border:none;cursor:pointer;color:#92400e;font-size:13px;font-weight:bold">✕</button>
              </div>
              <div style="font-size:10px;color:#9ca3af;margin-top:4px">
                💡 Search registered sires by name or AI code, or type any sire name/AI straw code manually
              </div>
            </div>
            <div class="up-field">
              <label>${upT("up_f_airecord")} *</label>
              <input name="aiRecord" required placeholder="e.g. STR-2024-00145">
            </div>
            <div class="up-field">
              <label>${upT("up_f_sirebreed")} *</label>
              <select name="sireBreed" required style="width:100%;padding:9px 12px;border:1.5px solid #d1d5db;border-radius:7px;font-size:13px;background:#fff">
                <option value="">${upT("up_sel_breed")}</option>
                <option>Holstein</option>
                <option>Montbéliarde</option>
                <option>Brown Swiss</option>
                <option>Jersey</option>
                <option>Other</option>
              </select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_aidate")} *</label>
              <input type="date" name="aiDate" required>
            </div>
            <div class="up-field">
              <label>${upT("up_f_aitech")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input name="aiTech" placeholder="Name of the AI technician">
            </div>
          </div>
        </div>

        <!-- ═══ SECTION 2: NEW ANIMAL / COW ═══ -->
        <div style="background:#f0fdf4;border:1px solid #a7f3d0;border-radius:8px;padding:14px 16px;margin-bottom:16px">
          <div style="font-size:12px;font-weight:700;color:#065f46;margin-bottom:12px">
            🐄 ${upT("up_sec_cow")}
          </div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:11px">
            <div class="up-field">
              <label>${upT("up_f_sex")} *</label>
              <select name="animalSex" required style="width:100%;padding:9px 12px;border:1.5px solid #d1d5db;border-radius:7px;font-size:13px;background:#fff">
                <option value="">${upT("up_sel_sex")}</option>
                <option value="Female">🐄 Female (Cow / Heifer)</option>
                <option value="Male">🐂 Male (Bull Calf)</option>
              </select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_animalname")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input name="animalName" placeholder="e.g. BELLA STAR 22 (if named)">
            </div>
            <div class="up-field">
              <label>${upT("up_f_dob")} *</label>
              <input type="date" name="animalDob" required>
            </div>
            <div class="up-field">
              <label>${upT("up_f_eartag")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input name="animalEarTag" placeholder="e.g. LK-2024-051">
            </div>
            <div class="up-field">
              <label>${upT("up_f_regno")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input name="animalRegId" placeholder="Existing reg. number if any">
            </div>
            <div class="up-field">
              <label>${upT("up_f_cowbreed")} *</label>
              <select name="animalBreed" required style="width:100%;padding:9px 12px;border:1.5px solid #d1d5db;border-radius:7px;font-size:13px;background:#fff">
                <option value="">${upT("up_sel_breed")}</option>
                <option>Holstein</option>
                <option>Montbéliarde</option>
                <option>F1 Crossbred (50% HO)</option>
                <option>F2 Crossbred (75% HO)</option>
                <option>F3 Crossbred (87.5% HO)</option>
                <option>Local / Indigenous</option>
              </select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_photo")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input type="file" id="req_photo" accept="image/*" onchange="upPreviewPhoto(this)" style="padding:6px">
              <canvas id="req_photoCanvas" style="display:none"></canvas>
              <div id="req_photoPreview" style="margin-top:6px"></div>
            </div>
          </div>
        </div>

        <!-- ═══ SECTION 3: DAM — ALL OPTIONAL ═══ -->
        <div style="background:#fff7ed;border:1px solid #fed7aa;border-radius:8px;padding:14px 16px;margin-bottom:16px">
          <div style="font-size:12px;font-weight:700;color:#c2410c;margin-bottom:4px">
            🐄 ${upT("up_sec_dam")}
            <span style="font-weight:400;color:#9ca3af;font-size:11px"> — ${upT("up_dam_optional")}</span>
          </div>
          <div style="font-size:10px;color:#9a3412;background:#fff;border-radius:5px;padding:6px 9px;margin-bottom:10px;border:1px solid #fed7aa">
            💡 ${upT("up_dam_hint")}
          </div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:11px">
            <div class="up-field">
              <label>${upT("up_f_damname")}</label>
              <input name="damName" placeholder="Mother's name (if known)">
            </div>
            <div class="up-field">
              <label>${upT("up_f_damdob")}</label>
              <input type="date" name="damDob">
            </div>
            <div class="up-field">
              <label>${upT("up_f_damreg")}</label>
              <input name="damRegId" placeholder="Registration number if available">
            </div>
            <div class="up-field">
              <label>${upT("up_f_dameartag")}</label>
              <input name="damEarTag" placeholder="Ear tag number if available">
            </div>
            <div class="up-field">
              <label>${upT("up_f_dambreed")}</label>
              <select name="damBreed" style="width:100%;padding:9px 12px;border:1.5px solid #d1d5db;border-radius:7px;font-size:13px;background:#fff">
                <option value="">— Unknown / Not recorded —</option>
                <option>Holstein</option>
                <option>Montbéliarde</option>
                <option>Local / Indigenous</option>
                <option>Crossbred</option>
              </select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_dammilk")}</label>
              <input type="number" name="damMilk" placeholder="e.g. 7500 (if known)">
            </div>
          </div>
        </div>

        <!-- ═══ SECTION 4: OWNER ═══ -->
        <div style="background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;padding:14px 16px;margin-bottom:16px">
          <div style="font-size:12px;font-weight:700;color:#1e40af;margin-bottom:12px">
            👤 ${upT("up_sec_owner")}
          </div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:11px">
            <div class="up-field">
              <label>${upT("up_f_ownername")} *</label>
              <input name="ownerName" required placeholder="Full name">
            </div>
            <div class="up-field">
              <label>${upT("up_f_phone")} *</label>
              <input name="ownerPhone" required placeholder="+93-700-000000">
            </div>
            <div class="up-field">
              <label>${upT("up_f_email")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input type="email" name="ownerEmail" placeholder="email@example.com">
            </div>
            <div class="up-field">
              <label>${upT("up_f_province")} *</label>
              <select name="ownerProvince" required onchange="upCascadeDistrict(this,'req_district')">${provOpts}</select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_district")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <select name="ownerDistrict" id="req_district">
                <option value="">— Select District —</option>
              </select>
            </div>
            <div class="up-field">
              <label>${upT("up_f_address")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
              <input name="ownerAddress" placeholder="Village or farm address">
            </div>
          </div>
        </div>

        <!-- Notes -->
        <div class="up-field">
          <label>${upT("up_f_notes")} <span style="font-weight:400;color:#9ca3af">(${upT("up_optional")})</span></label>
          <textarea name="notes" placeholder="Any additional information or special requests for the admin…" style="min-height:60px"></textarea>
        </div>

        <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-top:6px">
          <button type="submit" class="up-btn up-btn-green" style="padding:12px 28px;font-size:14px">
            📤 ${upT("up_req_submit")}
          </button>
          <button type="button" class="up-btn up-btn-gray"
            onclick="if(confirm('Clear all fields?')){document.getElementById('reqForm').reset();document.getElementById('req_photoPreview').innerHTML='';}">
            🗑 ${upT("up_clearform")}
          </button>
          <span style="font-size:10px;color:#9ca3af">* Required · Request sent directly to the admin</span>
        </div>
        <div id="req_result" style="margin-top:12px"></div>
      </form>
    </div>
  </div>`;
}

function submitPedigreeRequest(e) {
  e.preventDefault();
  const form = e.target;
  const g = n => (form.elements[n]?.value || "").trim();
  const canvas = document.getElementById("req_photoCanvas");
  const photo = canvas && canvas.width > 0 ? canvas.toDataURL("image/jpeg", 0.8) : "";

  const reqData = {
    // Animal
    animalName: g("animalName"),
    animalSex: g("animalSex"),
    animalDob: g("animalDob"),
    animalRegId: g("animalRegId"),
    animalEarTag: g("animalEarTag"),
    animalBreed: g("animalBreed"),
    photo,
    // AI / Sire
    sireName: g("sireName"),
    aiRecord: g("aiRecord"),
    sireBreed: g("sireBreed"),
    aiDate: g("aiDate"),
    aiTech: g("aiTech"),
    // Dam (all optional)
    damName: g("damName"),
    damDob: g("damDob"),
    damRegId: g("damRegId"),
    damEarTag: g("damEarTag"),
    damBreed: g("damBreed"),
    damMilk: g("damMilk"),
    // Owner
    ownerName: g("ownerName"),
    ownerPhone: g("ownerPhone"),
    ownerEmail: g("ownerEmail"),
    ownerProvince: g("ownerProvince"),
    ownerDistrict: g("ownerDistrict"),
    ownerAddress: g("ownerAddress"),
    notes: g("notes"),
  };

  // ── 1. Add the message/request ──
  const msgId = addMessage({
    type: "pedigree_request",
    from: UP_SESS.id,
    fromName: UP_SESS.name || UP_SESS.username,
    to: "admin",
    subject: `Pedigree Request — ${reqData.animalBreed} ${reqData.animalSex} | Sire: ${reqData.sireName}`,
    status: "pending",
    read: false,
    requestData: reqData,
  });

  // ── 2. Create a PENDING animal record in this user's herd/individual ──
  try {
    const savedAnimals = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
    const pendingAnimal = {
      id: "pending_" + msgId,
      name: reqData.animalName
        ? "[Pending] " + reqData.animalName
        : "[Pending] " + (reqData.animalBreed || "Animal") + " — " + reqData.sireName,
      sex: reqData.animalSex || "Female",
      breed: reqData.animalBreed || "",
      dob: reqData.animalDob || "",
      regId: reqData.animalRegId || "",
      earTag: reqData.animalEarTag || "",
      photo: reqData.photo || "",
      sireName: reqData.sireName || "",
      sireId: reqData.aiRecord || "",
      sireBreed: reqData.sireBreed || "",
      damName: reqData.damName || "",
      damId: reqData.damRegId || "",
      damDob: reqData.damDob || "",
      damBreed: reqData.damBreed || "",
      damMilk: reqData.damMilk || "",
      ownerId: UP_SESS.ownerId || "",
      province: reqData.ownerProvince || "",
      district: reqData.ownerDistrict || "",
      notes:
        "[PENDING PEDIGREE REQUEST] AI Record: " +
        reqData.aiRecord +
        " | AI Date: " +
        reqData.aiDate +
        " | Request ID: " +
        msgId,
      role: reqData.animalSex === "Male" ? "Son" : "Cow",
      pendingRequest: msgId,
      pendingStatus: "pending",
    };
    savedAnimals.push(pendingAnimal);
    localStorage.setItem("larspeenghar_v7", JSON.stringify(savedAnimals));
  } catch (err) {
    console.warn("Could not save pending animal:", err);
  }

  // ── 3. Show success ──
  const res = document.getElementById("req_result");
  if (res)
    res.innerHTML = `<div style="background:#d1fae5;border:1px solid #6ee7b7;border-radius:8px;padding:12px 16px;font-size:12px;color:#065f46">
    <b>✅ Request submitted successfully!</b><br>
    Ref: <code style="background:#f0fdf4;padding:1px 5px;border-radius:3px">${msgId}</code><br>
    A pending pedigree record has been created in your herd.<br>
    The admin will register the official pedigree and send it to your Inbox.
    <div style="margin-top:8px;display:flex;gap:8px;flex-wrap:wrap">
      <button class="up-btn up-btn-gray" style="padding:4px 10px;font-size:11px" onclick="upNav('sent')">📤 View in Sent</button>
      <button class="up-btn up-btn-gray" style="padding:4px 10px;font-size:11px" onclick="upNav('inbox')">📥 Check Inbox</button>
    </div>
  </div>`;
  form.reset();
  if (document.getElementById("req_photoPreview"))
    document.getElementById("req_photoPreview").innerHTML = "";
  upUpdateBadges();
}

/* ════════════════════════════════════════════════════════════════
   INBOX
════════════════════════════════════════════════════════════════ */
function upRenderInbox() {
  const msgs = getMessages()
    .filter(m => m.to === UP_SESS.id)
    .sort((a, b) => b.timestamp.localeCompare(a.timestamp));
  document.getElementById("up_main").innerHTML = `
  <div class="up-card2">
    <div class="up-card2-hdr">
      <h3>📥 ${upT("up_inbox_title")} <span style="font-size:11px;color:#9ca3af;font-weight:400">(${msgs.length})</span></h3>
    </div>
    <div class="up-card2-body" style="padding:10px 14px">
      ${
        !msgs.length
          ? `<div style="text-align:center;padding:30px;color:#9ca3af">
            <div style="font-size:32px;margin-bottom:8px">📭</div>
            <div style="font-size:13px">${upT("up_inbox_empty")}</div>
            <div style="font-size:11px;margin-top:4px">${upT("up_inbox_hint")}</div>
          </div>`
          : msgs.map(m => upMsgPreviewCard(m, true)).join("")
      }
    </div>
  </div>`;
}

/* ════════════════════════════════════════════════════════════════
   SENT
════════════════════════════════════════════════════════════════ */
function upRenderSent() {
  const msgs = getMessages()
    .filter(m => m.from === UP_SESS.id && m.type === "pedigree_request")
    .sort((a, b) => b.timestamp.localeCompare(a.timestamp));
  document.getElementById("up_main").innerHTML = `
  <div class="up-card2">
    <div class="up-card2-hdr">
      <h3>📤 ${upT("up_sent_title")} <span style="font-size:11px;color:#9ca3af;font-weight:400">(${msgs.length})</span></h3>
    </div>
    <div class="up-card2-body" style="padding:10px 14px">
      ${
        !msgs.length
          ? `<div style="text-align:center;padding:30px;color:#9ca3af">
            <div style="font-size:32px;margin-bottom:8px">📭</div>
            <div style="font-size:13px">${upT("up_sent_empty")}</div>
            <button class="up-btn up-btn-green" style="margin-top:10px" onclick="upNav('request')">📝 ${upT("up_sent_first")}</button>
          </div>`
          : msgs.map(m => upSentCard(m)).join("")
      }
    </div>
  </div>`;
}

function upSentCard(m) {
  const d = m.requestData || {};
  const statusMap = {
    pending: { bg: "#fef9c3", color: "#854d0e", icon: "⏳", label: upT("up_st_pending") },
    replied: { bg: "#dbeafe", color: "#1e40af", icon: "💬", label: upT("up_st_replied") },
    completed: { bg: "#d1fae5", color: "#065f46", icon: "✅", label: upT("up_st_completed") },
    rejected: { bg: "#fee2e2", color: "#991b1b", icon: "❌", label: upT("up_st_rejected") },
  };
  const st = statusMap[m.status] || statusMap.pending;
  const dt = m.timestamp
    ? new Date(m.timestamp).toLocaleString("en-GB", {
        day: "2-digit",
        month: "short",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "—";
  return `<div class="up-msg-card" onclick="upViewSent('${m.id}')">
    <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap">
      <div style="flex:1;min-width:0">
        <div style="font-size:13px;font-weight:700;color:#1a2e1a;margin-bottom:3px">🐄 ${d.cowName || "Unnamed Cow"}</div>
        <div style="font-size:11px;color:#6b7280;margin-bottom:5px">
          🐂 Sire: <b>${d.sireName || "—"}</b> · 📋 AI Record: ${d.aiRecord || "—"} · 📅 AI Date: ${d.aiDate || "—"}
        </div>
        <div style="font-size:10px;color:#9ca3af">👤 ${d.ownerName || "—"} · 📍 ${d.ownerProvince || "—"} · Sent: ${dt}</div>
      </div>
      <span class="up-badge" style="background:${st.bg};color:${st.color};white-space:nowrap;flex-shrink:0">${st.icon} ${st.label}</span>
    </div>
  </div>`;
}

function upViewSent(id) {
  const m = getMessages().find(x => x.id === id);
  if (!m) return;
  const d = m.requestData || {};
  const dt = m.timestamp
    ? new Date(m.timestamp).toLocaleString("en-GB", {
        day: "2-digit",
        month: "short",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "—";
  const statusMap = {
    pending: {
      bg: "#fef9c3",
      color: "#854d0e",
      icon: "⏳",
      label: "Pending — awaiting admin review",
    },
    replied: {
      bg: "#dbeafe",
      color: "#1e40af",
      icon: "💬",
      label: "Admin has replied — check your Inbox",
    },
    completed: { bg: "#d1fae5", color: "#065f46", icon: "✅", label: "Completed" },
    rejected: { bg: "#fee2e2", color: "#991b1b", icon: "❌", label: "Rejected" },
  };
  const st = statusMap[m.status] || statusMap.pending;

  document.getElementById("up_main").innerHTML = `
  <button class="up-btn up-btn-gray" style="margin-bottom:12px;padding:6px 14px;font-size:11px" onclick="upNav('sent')">← Back to Sent</button>
  <div class="up-card2">
    <div class="up-card2-hdr" style="background:#f8fafc">
      <div>
        <h3>📝 Pedigree Request Detail</h3>
        <div style="font-size:10px;color:#9ca3af;margin-top:2px">Submitted: ${dt} · Ref: <code>${m.id}</code></div>
      </div>
      <span class="up-badge" style="background:${st.bg};color:${st.color};font-size:11px;padding:4px 12px">${st.icon} ${st.label}</span>
    </div>
    <div class="up-card2-body">
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:16px">
        <!-- AI Info -->
        <div>
          <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#92400e;margin-bottom:8px;padding-bottom:5px;border-bottom:2px solid #fde68a">🐂 AI / Sire Record</div>
          ${[
            ["Sire Name", d.sireName],
            ["AI Straw No.", d.aiRecord],
            ["Sire Breed", d.sireBreed],
            ["AI Date", d.aiDate],
            ["AI Technician", d.aiTech],
          ]
            .map(([l, v]) =>
              v
                ? `<div style="font-size:11px;margin-bottom:5px"><span style="color:#9ca3af">${l}:</span> <b>${v}</b></div>`
                : ""
            )
            .join("")}
        </div>
        <!-- Cow Info -->
        <div>
          <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#065f46;margin-bottom:8px;padding-bottom:5px;border-bottom:2px solid #a7f3d0">🐄 Cow Information</div>
          ${[
            ["Name", d.cowName],
            ["Ear Tag", d.earTag],
            ["Reg. ID", d.cowRegId],
            ["DOB", d.cowDob],
            ["Breed", d.cowBreed],
            ["Dam", d.damName],
            ["Dam Breed", d.damBreed],
            ["Dam Milk", d.damMilk ? d.damMilk + " kg/305d" : ""],
          ]
            .map(([l, v]) =>
              v
                ? `<div style="font-size:11px;margin-bottom:5px"><span style="color:#9ca3af">${l}:</span> <b>${v}</b></div>`
                : ""
            )
            .join("")}
        </div>
        <!-- Owner Info -->
        <div>
          <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:#1e40af;margin-bottom:8px;padding-bottom:5px;border-bottom:2px solid #bfdbfe">👤 Owner</div>
          ${[
            ["Name", d.ownerName],
            ["Phone", d.ownerPhone],
            ["Email", d.ownerEmail],
            ["Province", d.ownerProvince],
            ["District", d.ownerDistrict],
            ["Address", d.ownerAddress],
          ]
            .map(([l, v]) =>
              v
                ? `<div style="font-size:11px;margin-bottom:5px"><span style="color:#9ca3af">${l}:</span> <b>${v}</b></div>`
                : ""
            )
            .join("")}
        </div>
      </div>
      ${d.photo ? `<div style="margin-top:14px"><div style="font-size:11px;font-weight:700;color:#7c3aed;margin-bottom:6px">📷 Animal Photo</div><img src="${d.photo}" style="max-width:160px;max-height:160px;border-radius:8px;border:2px solid #e9d5ff;object-fit:cover"></div>` : ""}
      ${d.notes ? `<div style="margin-top:12px;padding:10px 13px;background:#faf5ff;border-radius:7px;border:1px solid #e9d5ff"><div style="font-size:11px;font-weight:700;color:#7c3aed;margin-bottom:4px">📝 Notes</div><div style="font-size:12px;color:#374151">${d.notes}</div></div>` : ""}
    </div>
  </div>`;
}

function upMsgPreviewCard(m, clickable) {
  const dt = m.timestamp
    ? new Date(m.timestamp).toLocaleString("en-GB", {
        day: "2-digit",
        month: "short",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "—";
  const isUnread = !m.read;
  const fn = clickable ? `upViewInboxMsg('${m.id}')` : null;
  return `<div class="up-msg-card${isUnread ? " unread" : " admin-reply"}" ${fn ? `onclick="${fn}"` : ""}">
    <div style="display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap">
      <div style="display:flex;align-items:center;gap:8px;min-width:0">
        ${isUnread ? `<span style="width:8px;height:8px;background:#2d7a4f;border-radius:50%;flex-shrink:0"></span>` : "<span style='width:8px'></span>"}
        <div style="font-size:12px;font-weight:${isUnread ? 700 : 600};color:#1a2e1a;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${m.subject || "Message from Admin"}</div>
      </div>
      <span style="font-size:10px;color:#9ca3af;flex-shrink:0">${dt}</span>
    </div>
    ${m.preview ? `<div style="font-size:11px;color:#6b7280;margin-top:4px;padding-left:16px">${m.preview.substring(0, 80)}…</div>` : ""}
  </div>`;
}

function upViewInboxMsg(id) {
  markRead(id);
  upUpdateBadges();
  const m = getMessages().find(x => x.id === id);
  if (!m) return;
  const dt = m.timestamp
    ? new Date(m.timestamp).toLocaleString("en-GB", {
        day: "2-digit",
        month: "long",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "—";
  const isPedigreeDelivery = m.type === "pedigree_delivered";

  // Use embedded animal data first (works cross-device), fallback to localStorage
  let linkedAnimal = m.embeddedAnimal || null;
  if (!linkedAnimal && m.linkedAnimalId) {
    try {
      const allA = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
      linkedAnimal = allA.find(a => a.id === m.linkedAnimalId) || null;
    } catch (e) {}
  }

  document.getElementById("up_main").innerHTML = `
  <button class="up-btn up-btn-gray" style="margin-bottom:12px;padding:6px 14px;font-size:11px" onclick="upNav('inbox')">← Back to Inbox</button>
  <div class="up-card2">
    <div class="up-card2-hdr" style="background:${isPedigreeDelivery ? "#d1fae5" : "#eff6ff"}">
      <div>
        <h3 style="color:${isPedigreeDelivery ? "#065f46" : "#1e40af"}">${isPedigreeDelivery ? "✅ " : "📨 "}${m.subject || "Message from Admin"}</h3>
        <div style="font-size:10px;color:#9ca3af;margin-top:2px">From: Larspeenghar Admin · ${dt}</div>
      </div>
      <button class="up-btn up-btn-gray" style="padding:5px 11px;font-size:11px" onclick="if(confirm('Delete this message?')){deleteMessage('${id}');upNav('inbox');}">🗑 Delete</button>
    </div>
    <div class="up-card2-body">
      <!-- Message body -->
      <div style="font-size:13px;color:#1a2e1a;line-height:1.8;white-space:pre-wrap;margin-bottom:${isPedigreeDelivery ? "16px" : "0"}">${m.body || "(No message body)"}</div>

      ${
        isPedigreeDelivery
          ? `
      <!-- Pedigree certificate section -->
      <div style="background:#f0fdf4;border:1px solid #a7f3d0;border-radius:10px;padding:16px 18px;margin-top:8px">
        <div style="font-size:13px;font-weight:700;color:#065f46;margin-bottom:10px">📄 Your Official Pedigree Certificate</div>

        ${
          linkedAnimal
            ? `
        <!-- Animal summary card -->
        <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px;margin-bottom:14px;background:#fff;border-radius:8px;padding:12px;border:1px solid #a7f3d0">
          ${
            linkedAnimal.photo
              ? `<div style="grid-column:1/-1;display:flex;align-items:center;gap:10px;padding-bottom:10px;border-bottom:1px solid #e5e7eb">
            <img src="${linkedAnimal.photo}" style="width:50px;height:50px;border-radius:50%;object-fit:cover;border:2px solid #a7f3d0">
            <div><div style="font-weight:700;font-size:14px;color:#1a2e1a">${linkedAnimal.name}</div>
            <div style="font-size:10px;color:#9ca3af">${linkedAnimal.breed === "Montbeliard" ? "Montbéliarde" : linkedAnimal.breed} · ${linkedAnimal.sex}</div></div>
          </div>`
              : `<div style="grid-column:1/-1;padding-bottom:8px;border-bottom:1px solid #e5e7eb">
            <div style="font-weight:700;font-size:14px;color:#1a2e1a">${linkedAnimal.name || "(Unnamed)"}</div>
            <div style="font-size:11px;color:#9ca3af">${linkedAnimal.breed === "Montbeliard" ? "Montbéliarde" : linkedAnimal.breed} · ${linkedAnimal.sex}</div>
          </div>`
          }
          ${[
            ["📋 Reg. ID", linkedAnimal.regId || "—"],
            ["🏷 Ear Tag", linkedAnimal.earTag || "—"],
            [
              "🗓 Date of Birth",
              linkedAnimal.dob
                ? new Date(linkedAnimal.dob + "T12:00").toLocaleDateString("en-GB", {
                    day: "2-digit",
                    month: "short",
                    year: "numeric",
                  })
                : "—",
            ],
            ["🐂 Sire", linkedAnimal.sireName || "—"],
            ["🐄 Dam", linkedAnimal.damName || "—"],
            ["📍 Province", linkedAnimal.province || "—"],
          ]
            .map(
              ([l, v]) =>
                `<div><div style="font-size:10px;color:#9ca3af">${l}</div><div style="font-weight:600;font-size:12px;color:#1a2e1a">${v}</div></div>`
            )
            .join("")}
        </div>
        <button onclick="upPrintLinkedPedigree(null,'${id}')"
          style="width:100%;padding:13px;background:linear-gradient(135deg,#1a4a2e,#2d7a4f);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px">
          🖨 View &amp; Print Pedigree Certificate
        </button>
        <div style="text-align:center;font-size:10px;color:#9ca3af;margin-top:6px">Opens a printable A4 certificate · Use browser Print or Ctrl+P to save as PDF</div>
        `
            : `
        <div style="padding:14px;background:#fff;border-radius:8px;border:1px solid #e5e7eb;text-align:center">
          <div style="font-size:24px;margin-bottom:6px">⏳</div>
          <div style="font-size:12px;font-weight:600;color:#374151">Pedigree certificate is being processed</div>
          <div style="font-size:11px;color:#9ca3af;margin-top:4px">Please contact the admin if this takes longer than expected.</div>
        </div>
        `
        }
      </div>`
          : ""
      }
    </div>
  </div>`;
}

function upPrintLinkedPedigree(animalId, msgId) {
  // Get animal from embedded message first (works cross-device)
  let a = null;
  if (msgId) {
    try {
      const msg = getMessages().find(m => m.id === msgId);
      if (msg && msg.embeddedAnimal) a = msg.embeddedAnimal;
    } catch (e) {}
  }
  // Fallback: search localStorage
  if (!a && animalId) {
    try {
      const all = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
      a = all.find(x => x.id === animalId) || null;
    } catch (e) {}
  }
  if (!a) {
    alert("Pedigree not found. Please contact the admin.");
    return;
  }
  openPedigreeCertificate(a, 2);
}

/* ── Print pedigree by animal ID (used by user portal inbox) ── */
function printUserPedigree(id) {
  let a = null;
  try {
    const all = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
    a = all.find(x => x.id === id) || animals.find(x => x.id === id) || null;
  } catch (e) {
    a = animals.find(x => x.id === id) || null;
  }
  if (!a) {
    alert("Animal record not found. Please contact the admin.");
    return;
  }
  openPedigreeCertificate(a, 2);
}

function openPedigreeCertificate(a, genCount) {
  const gens = genCount || 2;
  const today = new Date().toLocaleDateString("en-GB", {
    day: "2-digit",
    month: "long",
    year: "numeric",
  });
  const dob = a.dob
    ? new Date(a.dob + "T12:00").toLocaleDateString("en-GB", {
        day: "2-digit",
        month: "short",
        year: "numeric",
      })
    : "—";
  const isMale = a.sex === "Male";
  const breedDisp = a.breed === "Montbeliard" ? "Montbéliarde" : a.breed || "—";
  const certType = isMale ? "Dairy Bull Pedigree" : "Dairy Cow Pedigree";
  const certNo =
    "LPM-" +
    (a.regId || a.id || "")
      .replace(/[^A-Z0-9]/gi, "")
      .toUpperCase()
      .slice(0, 8) +
    "-" +
    new Date().getFullYear();

  let genLabel = "—",
    pctLabel = "—",
    compEntries = [];
  try {
    const comp = calcBreedComp(a);
    const gi = getGenInfo(comp);
    genLabel = gi.gen;
    pctLabel = gi.pct.toFixed(1) + "%";
    compEntries = Object.entries(comp).sort((x, y) => y[1] - x[1]);
  } catch (e) {}

  const hasProd = !!a.milk;
  const fatKg =
    a.milk && a.fat ? +((parseFloat(a.milk) * parseFloat(a.fat)) / 100).toFixed(1) : null;
  const protKg =
    a.milk && a.prot ? +((parseFloat(a.milk) * parseFloat(a.prot)) / 100).toFixed(1) : null;

  // Dynamic logo
  const logoUrl = getLogoDataUrl();
  const certLogo = logoUrl
    ? `<img src="${logoUrl}" style="width:56px;height:56px;object-fit:contain;border-radius:50%;border:2px solid rgba(255,255,255,.4)">`
    : `<div style="width:56px;height:56px;background:rgba(255,255,255,.15);border-radius:50%;border:2px solid rgba(255,255,255,.4);display:flex;align-items:center;justify-content:center">
        <svg width="36" height="36" viewBox="0 0 64 64" fill="none">
          <ellipse cx="32" cy="34" rx="18" ry="16" fill="white" opacity=".95"/>
          <ellipse cx="32" cy="46" rx="10" ry="6" fill="white" opacity=".85"/>
          <ellipse cx="16" cy="26" rx="6" ry="9" fill="white" opacity=".9" transform="rotate(-15 16 26)"/>
          <ellipse cx="48" cy="26" rx="6" ry="9" fill="white" opacity=".9" transform="rotate(15 48 26)"/>
          <path d="M18 20 Q10 8 6 6 Q12 10 16 22" fill="white" opacity=".85"/>
          <path d="M46 20 Q54 8 58 6 Q52 10 48 22" fill="white" opacity=".85"/>
          <circle cx="26" cy="31" r="2.5" fill="#1a4a2e"/><circle cx="38" cy="31" r="2.5" fill="#1a4a2e"/>
          <ellipse cx="32" cy="42" rx="7" ry="4.5" fill="#2d7a4f" opacity=".6"/>
          <circle cx="29" cy="42" r="1.5" fill="#1a4a2e"/><circle cx="35" cy="42" r="1.5" fill="#1a4a2e"/>
        </svg>
      </div>`;

  /* ── Pedigree cell builder (NO photos) ── */
  function pc(lbl, name, id, dob2, breed, milk, fat, prot, cls) {
    const bd = breed === "Montbeliard" ? "Montbéliarde" : breed || "";
    const dotColor =
      breed === "Holstein"
        ? "#3a6bc4"
        : breed === "Montbeliard"
          ? "#e67e22"
          : breed === "Local"
            ? "#16a34a"
            : "#888";
    return `<div class="pc ${cls || ""}">
      <div class="pc-lbl">${lbl}</div>
      <div class="pc-nm">${name || `<span class="pc-empty">Not recorded</span>`}</div>
      ${id ? `<div class="pc-meta">ID: ${id}</div>` : ""}
      ${dob2 ? `<div class="pc-meta">${dob2}</div>` : ""}
      ${bd ? `<div class="pc-meta"><span class="pc-dot" style="background:${dotColor}"></span>${bd}</div>` : ""}
      ${milk ? `<div class="pc-prod">🥛 ${Number(milk).toLocaleString()}kg${fat ? " F" + fat + "%" : ""}${prot ? " P" + prot + "%" : ""}</div>` : ""}
    </div>`;
  }

  /* ── 2-Generation layout (fits perfectly on A4) ── */
  const ped2 = `
  <table class="ped-tbl">
    <colgroup><col style="width:24%"><col style="width:28%"><col style="width:24%"><col style="width:24%"></colgroup>
    <tbody>
      <tr>
        <td rowspan="4" class="subj-cell">
          <div class="pc-lbl">${isMale ? "🐂 Subject Bull" : "🐄 Subject Cow"}</div>
          <div class="pc-nm" style="font-size:11pt;color:#1a4a2e">${a.name || "(Unnamed)"}</div>
          ${a.regId ? `<div class="pc-meta">ID: ${a.regId}</div>` : ""}
          <div class="pc-meta">${dob}</div>
          <div class="pc-meta"><span class="pc-dot" style="background:${a.breed === "Holstein" ? "#3a6bc4" : a.breed === "Montbeliard" ? "#e67e22" : "#16a34a"}"></span>${breedDisp}</div>
          ${genLabel !== "—" ? `<div class="pc-gen">${genLabel} · ${pctLabel}</div>` : ""}
          ${a.milk ? `<div class="pc-prod">🥛 ${Number(a.milk).toLocaleString()}kg${a.fat ? " F" + a.fat + "%" : ""}${a.prot ? " P" + a.prot + "%" : ""}</div>` : ""}
          ${a.earTag ? `<div class="pc-meta">🏷 ${a.earTag}</div>` : ""}
        </td>
        <td rowspan="2" class="sire-cell">${pc("🐂 Sire (Father)", a.sireName, a.sireId, a.sireDob, a.sireBreed, "", "", "", "")}</td>
        <td colspan="2">${pc("Paternal Grandsire (PGS)", a.ssName, a.ssId, "", a.ssBreed, "", "", "", "gp-cell")}</td>
      </tr>
      <tr>
        <td colspan="2">${pc("Paternal Granddam (PGD)", a.sdName, a.sdId, "", a.sdBreed, a.sdMilk, a.sdFat, a.sdProt, "gp-cell")}</td>
      </tr>
      <tr>
        <td rowspan="2" class="dam-cell">${pc("🐄 Dam (Mother)", a.damName, a.damId, a.damDob, a.damBreed, a.damMilk, a.damFat, a.damProt, "")}</td>
        <td colspan="2">${pc("Maternal Grandsire (MGS)", a.dsName, a.dsId, "", a.dsBreed, "", "", "", "gp-cell")}</td>
      </tr>
      <tr>
        <td colspan="2">${pc("Maternal Granddam (MGD)", a.ddName, a.ddId, "", a.ddBreed, a.ddMilk, a.ddFat, a.ddProt, "gp-cell")}</td>
      </tr>
    </tbody>
  </table>`;

  /* ── 3-Generation layout (landscape recommended) ── */
  const ped3 = `
  <table class="ped-tbl">
    <colgroup><col style="width:18%"><col style="width:22%"><col style="width:22%"><col style="width:19%"><col style="width:19%"></colgroup>
    <tbody>
      <tr>
        <td rowspan="8" class="subj-cell">
          <div class="pc-lbl">${isMale ? "🐂 Subject" : "🐄 Subject"}</div>
          <div class="pc-nm" style="font-size:10pt;color:#1a4a2e">${a.name || "(Unnamed)"}</div>
          ${a.regId ? `<div class="pc-meta">ID: ${a.regId}</div>` : ""}
          <div class="pc-meta">${dob}</div>
          <div class="pc-meta">${breedDisp}</div>
          ${genLabel !== "—" ? `<div class="pc-gen">${genLabel}</div>` : ""}
        </td>
        <td rowspan="4" class="sire-cell">${pc("🐂 Sire", a.sireName, a.sireId, a.sireDob, a.sireBreed, "", "", "", "")}</td>
        <td rowspan="2">${pc("PGS", a.ssName, a.ssId, "", a.ssBreed, "", "", "", "gp-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td rowspan="2">${pc("PGD", a.sdName, a.sdId, "", a.sdBreed, a.sdMilk, a.sdFat, a.sdProt, "gp-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td rowspan="4" class="dam-cell">${pc("🐄 Dam", a.damName, a.damId, a.damDob, a.damBreed, a.damMilk, a.damFat, a.damProt, "")}</td>
        <td rowspan="2">${pc("MGS", a.dsName, a.dsId, "", a.dsBreed, "", "", "", "gp-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td rowspan="2">${pc("MGD", a.ddName, a.ddId, "", a.ddBreed, a.ddMilk, a.ddFat, a.ddProt, "gp-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GS", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
      <tr>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
        <td>${pc("G-GD", "", "", "", "", "", "", "", "gg-cell")}</td>
      </tr>
    </tbody>
  </table>`;

  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${certType} — ${a.name || "Animal"}</title>
<style>
/* ── PAGE SETUP ── */
@page { size:A4 portrait; margin:8mm 10mm }
@page.landscape { size:A4 landscape; margin:8mm 10mm }
*  { box-sizing:border-box; margin:0; padding:0; -webkit-print-color-adjust:exact; print-color-adjust:exact }
body { font-family:'Segoe UI',Arial,sans-serif; font-size:8.5pt; color:#111; background:#fff; max-width:196mm; margin:0 auto }

/* ── HEADER ── */
.hdr  { background:#1a4a2e; color:#fff; padding:10px 14px; display:flex; align-items:center; gap:12px; border-radius:4px 4px 0 0 }
.hdr-txt h1 { font-size:10pt; font-weight:700; letter-spacing:.2px }
.hdr-txt h2 { font-size:13pt; font-weight:900; margin-top:1px }
.hdr-txt p  { font-size:7pt; opacity:.75; margin-top:2px }
.hdr-no { text-align:right; font-size:7pt; opacity:.75; margin-left:auto; flex-shrink:0 }

/* ── ANIMAL NAME STRIP ── */
.strip { background:#2d7a4f; color:#fff; padding:6px 14px; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:6px }
.strip-name { font-size:14pt; font-weight:900 }
.strip-badges { display:flex; gap:6px; flex-wrap:wrap }
.badge { background:rgba(255,255,255,.22); border:1px solid rgba(255,255,255,.4); border-radius:3px; padding:2px 8px; font-size:7.5pt; font-weight:700 }

/* ── IDENTITY GRID ── */
.id-grid { display:grid; grid-template-columns:repeat(5,1fr); border:1px solid #c8dcc8; border-top:none }
.id-cell { padding:4px 8px; border-right:1px solid #d4e4d4; border-bottom:1px solid #d4e4d4 }
.id-cell:last-child,:nth-child(5n) { border-right:none }
.id-cell.w2 { grid-column:span 2 } .id-cell.w5 { grid-column:span 5 }
.id-cell .lb { font-size:6pt; text-transform:uppercase; letter-spacing:.4px; color:#5a7a5a; font-weight:600 }
.id-cell .vl { font-size:8.5pt; font-weight:700; color:#1a2e1a; margin-top:1px }

/* ── SECTION HEADER ── */
.sh { background:#ecf5ec; border:1px solid #c8dcc8; border-top:none; padding:3px 10px; font-size:7.5pt; font-weight:700; color:#1a4a2e; display:flex; align-items:center; gap:5px }
.sh.bl { background:#eef4ff; border-color:#bfdbfe; color:#1e40af }
.sh.am { background:#fffbeb; border-color:#fde68a; color:#92400e }

/* ── PEDIGREE TABLE ── */
.ped-tbl { width:100%; border-collapse:collapse; border:1px solid #c8dcc8; border-top:none; table-layout:fixed }
.ped-tbl td { border:1px solid #c8dcc8; vertical-align:top; padding:0 }

/* ── PEDIGREE CELL ── */
.pc { padding:5px 7px; height:100%; display:flex; flex-direction:column; justify-content:center; min-height:38px }
.pc-lbl   { font-size:6pt; text-transform:uppercase; letter-spacing:.4px; color:#888; margin-bottom:2px; font-weight:600 }
.pc-nm    { font-size:8.5pt; font-weight:700; color:#1a2e1a; line-height:1.3 }
.pc-empty { color:#bbb; font-style:italic; font-size:7.5pt; font-weight:400 }
.pc-meta  { font-size:6.5pt; color:#5a7a5a; margin-top:1px; display:flex; align-items:center; gap:3px }
.pc-prod  { font-size:6.5pt; color:#6d28d9; font-weight:600; margin-top:2px }
.pc-gen   { font-size:7pt; font-weight:700; color:#fff; background:#2d7a4f; display:inline-block; padding:1px 6px; border-radius:3px; margin-top:3px }
.pc-dot   { width:7px; height:7px; border-radius:50%; display:inline-block; flex-shrink:0 }

.subj-cell { background:#eaf6ea }
.sire-cell { background:#eff6ff }
.dam-cell  { background:#fff0fb }
.gp-cell   { background:#fafafa }
.gg-cell   { background:#f5f5f5 }

/* ── COMPOSITION ROW ── */
.comp-row { display:grid; grid-template-columns:repeat(auto-fit,minmax(60mm,1fr)); gap:8px; padding:6px 10px; border:1px solid #c8dcc8; border-top:none; background:#fafffe }
.comp-item .cl { font-size:6.5pt; font-weight:700; display:flex; justify-content:space-between; margin-bottom:2px; color:#1a2e1a }
.comp-item .bg { background:#e5e7eb; height:5px; border-radius:3px; overflow:hidden }
.comp-item .fg { height:100%; border-radius:3px }
.gen-box { text-align:center; display:flex; flex-direction:column; align-items:center; justify-content:center }
.gen-box .gv { font-size:13pt; font-weight:900; color:#1a4a2e }
.gen-box .gl { font-size:7pt; color:#888 }

/* ── PRODUCTION ── */
.prod-row { display:grid; grid-template-columns:repeat(6,1fr); border:1px solid #c8dcc8; border-top:none }
.pc2 { padding:5px 8px; border-right:1px solid #e0e8e0; text-align:center }
.pc2:last-child { border-right:none }
.pc2 .lb { font-size:6pt; text-transform:uppercase; color:#5a7a5a; letter-spacing:.3px }
.pc2 .vl { font-size:10pt; font-weight:800; color:#1a4a2e; margin-top:1px }

/* ── SIGNATURES ── */
.sig-row { display:grid; grid-template-columns:1fr 1fr 1fr; gap:14px; padding:12px 14px; border:1px solid #c8dcc8; border-top:none }
.sig-box { text-align:center }
.sig-line { border-top:1.5px solid #1a4a2e; margin-top:22px; margin-bottom:3px }
.sig-role { font-size:7.5pt; color:#1a4a2e; font-weight:700 }
.sig-org  { font-size:6.5pt; color:#888 }

/* ── FOOTER ── */
.ftr { background:#ecf5ec; border:1px solid #c8dcc8; border-top:none; padding:4px 12px; display:flex; justify-content:space-between; border-radius:0 0 4px 4px }
.ftr p { font-size:6.5pt; color:#5a7a5a }

/* ── PRINT BUTTONS (screen only) ── */
.no-print { position:fixed; top:12px; right:12px; z-index:999; display:flex; gap:7px }
.btn-p { padding:7px 14px; background:#1a4a2e; color:#fff; border:none; border-radius:6px; font-size:10.5pt; font-weight:700; cursor:pointer }
.btn-3 { padding:7px 12px; background:#7c3aed; color:#fff; border:none; border-radius:6px; font-size:10.5pt; font-weight:700; cursor:pointer }
.btn-2 { padding:7px 12px; background:#3a6bc4; color:#fff; border:none; border-radius:6px; font-size:10.5pt; font-weight:700; cursor:pointer }
.btn-c { padding:7px 10px; background:#f3f4f6; color:#374151; border:1px solid #d1d5db; border-radius:6px; font-size:10.5pt; cursor:pointer }
@media print { .no-print { display:none !important } }
</style>
</head>
<body>

<div class="no-print">
  <button class="btn-2" onclick="setPed(2)">2-Gen</button>
  <button class="btn-3" onclick="setPed(3)">3-Gen</button>
  <button class="btn-p" onclick="window.print()">🖨 Print / Save PDF</button>
  <button class="btn-c" onclick="window.close()">✕</button>
</div>

<!-- HEADER -->
<div class="hdr">
  ${certLogo}
  <div class="hdr-txt" style="flex:1">
    <h1>${getCertSettings().companyName}</h1>
    <h2>Larspeenghar ${certType}</h2>
    <p>${getCertSettings().certSubtitle}</p>
  </div>
  <div class="hdr-no"><div style="font-weight:700">No: ${certNo}</div><div style="margin-top:2px">${today}</div></div>
</div>

<!-- ANIMAL STRIP -->
<div class="strip">
  <div>
    <div style="font-size:7pt;opacity:.8">PEDIGREE RECORD — ${isMale ? "DAIRY BULL" : "DAIRY COW/HEIFER"}</div>
    <div class="strip-name">${a.name || "(Unnamed)"}</div>
  </div>
  <div class="strip-badges">
    <span class="badge">${breedDisp}</span>
    <span class="badge">${isMale ? "🐂 Bull" : "🐄 Cow"}</span>
    ${genLabel !== "—" ? `<span class="badge">${genLabel} · ${pctLabel}</span>` : ""}
    ${a.earTag ? `<span class="badge">🏷 ${a.earTag}</span>` : ""}
  </div>
</div>

<!-- IDENTITY -->
<div class="id-grid">
  <div class="id-cell"><div class="lb">Ear Tag No.</div><div class="vl">${a.earTag || "—"}</div></div>
  <div class="id-cell"><div class="lb">Registration ID</div><div class="vl">${a.regId || "—"}</div></div>
  <div class="id-cell"><div class="lb">Date of Birth</div><div class="vl">${dob}</div></div>
  <div class="id-cell"><div class="lb">Sex / Lactation</div><div class="vl">${a.sex || "—"}${a.lact ? " · Lact. " + a.lact : ""}</div></div>
  <div class="id-cell"><div class="lb">Color / Markings</div><div class="vl">${a.color || "—"}</div></div>
  <div class="id-cell w2"><div class="lb">Owner Name</div><div class="vl">${a.ownerName || "—"}</div></div>
  <div class="id-cell"><div class="lb">Phone</div><div class="vl">${a.ownerPhone || "—"}</div></div>
  <div class="id-cell"><div class="lb">Province</div><div class="vl">${a.province || "—"}</div></div>
  <div class="id-cell"><div class="lb">District</div><div class="vl">${a.district || "—"}</div></div>
</div>

<!-- PEDIGREE -->
<div class="sh">🌳 Three-Generation Pedigree (No. of Generations shown: <span id="ped-gen-label">${gens}</span>)</div>
<div id="ped-area">${gens >= 3 ? ped3 : ped2}</div>

<!-- BREED COMPOSITION -->
<div class="sh">🔀 Breed Composition &amp; Generation</div>
<div class="comp-row">
  <div class="gen-box">
    <div class="gv">${genLabel}</div>
    <div class="gl">${pctLabel} ${compEntries[0] ? (compEntries[0][0] === "Montbeliard" ? "Montbéliarde" : compEntries[0][0]) : ""}</div>
  </div>
  ${compEntries
    .map(([br, pct]) => {
      const col = br === "Holstein" ? "#3a6bc4" : br === "Montbeliard" ? "#e67e22" : "#16a34a";
      return `<div class="comp-item">
      <div class="cl"><span>${br === "Montbeliard" ? "Montbéliarde" : br}</span><span>${pct.toFixed(1)}%</span></div>
      <div class="bg"><div class="fg" style="width:${Math.min(100, pct).toFixed(0)}%;background:${col}"></div></div>
    </div>`;
    })
    .join("")}
</div>

${
  hasProd
    ? `
<!-- PRODUCTION -->
<div class="sh bl">🥛 305-Day Production Record — Lactation ${a.lact || "1"}</div>
<div class="prod-row">
  <div class="pc2"><div class="lb">Milk Yield</div><div class="vl">${Number(a.milk).toLocaleString()} kg</div></div>
  <div class="pc2"><div class="lb">Fat %</div><div class="vl">${a.fat || "—"}</div></div>
  <div class="pc2"><div class="lb">Protein %</div><div class="vl">${a.prot || "—"}</div></div>
  <div class="pc2"><div class="lb">Fat kg</div><div class="vl">${fatKg !== null ? fatKg + " kg" : "—"}</div></div>
  <div class="pc2"><div class="lb">Protein kg</div><div class="vl">${protKg !== null ? protKg + " kg" : "—"}</div></div>
  <div class="pc2"><div class="lb">Peak / DIM</div><div class="vl">${a.peak ? a.peak + " kg" : "—"}${a.dim ? " / " + a.dim + "d" : ""}</div></div>
</div>`
    : ""
}

<!-- SIGNATURES — read from certificate settings -->
<div class="sh am" style="margin-top:8px">✍️ Certification &amp; Authorisation</div>
${(() => {
  const cs = getCertSettings();
  const sigs = [];
  const num = cs.numSigs || 3;
  if (num >= 1 && cs.sig1Role) sigs.push({ role: cs.sig1Role, org: cs.sig1Org || "" });
  if (num >= 2 && cs.sig2Role) sigs.push({ role: cs.sig2Role, org: cs.sig2Org || "" });
  if (num >= 3 && cs.sig3Role) sigs.push({ role: cs.sig3Role, org: cs.sig3Org || "" });
  if (!sigs.length) return "";
  const cols = sigs.length === 1 ? "1fr" : sigs.length === 2 ? "1fr 1fr" : "1fr 1fr 1fr";
  return `<div class="sig-row" style="grid-template-columns:${cols}">
    ${sigs
      .map(
        s => `<div class="sig-box"><div class="sig-line"></div>
      <div class="sig-role">${s.role}</div>
      ${s.org ? `<div class="sig-org">${s.org}</div>` : ""}
    </div>`
      )
      .join("")}
  </div>`;
})()}

<div class="ftr">
  <p>${getCertSettings().footerText} · ${certType} · ${certNo}</p>
  <p>Printed: ${today}</p>
</div>

<script>
  var _ped2=${JSON.stringify(ped2)};
  var _ped3=${JSON.stringify(ped3)};
  function setPed(n){
    document.getElementById("ped-area").innerHTML=n>=3?_ped3:_ped2;
    document.getElementById("ped-gen-label").textContent=n;
  }
<\/script>
</body>
</html>`;

  const win = window.open("", "_blank", "width=900,height=820");
  if (!win) {
    alert("Please allow popups to open the pedigree certificate.");
    return;
  }
  win.document.write(html);
  win.document.close();
  setTimeout(() => win.focus(), 200);
}

function upRenderAccount() {
  document.getElementById("up_main").innerHTML = `
  <div class="up-card2" style="max-width:520px">
    <div class="up-card2-hdr"><h3>🔑 ${upT("up_acc_title")}</h3></div>
    <div class="up-card2-body">
      <div style="margin-bottom:16px;padding:10px 13px;background:#f0fdf4;border-radius:7px;border:1px solid #a7f3d0">
        <div style="font-size:11px;color:#6b7280">${upT("up_logged_as")}</div>
        <div style="font-size:14px;font-weight:700;color:#1a2e1a">${UP_SESS.name || UP_SESS.username}</div>
        <div style="font-size:11px;color:#6b7280">${upT("up_username_lbl")}: ${UP_SESS.username}</div>
      </div>
      <div class="up-field"><label>${upT("up_curpass")} *</label><input type="password" id="up_oldPass" placeholder="Enter current password"></div>
      <div class="up-field"><label>${upT("up_newpass")} *</label><input type="password" id="up_newPass" placeholder="Minimum 6 characters"></div>
      <div class="up-field"><label>${upT("up_confpass")} *</label><input type="password" id="up_confPass" placeholder="Repeat new password"></div>
      <button class="up-btn up-btn-green" onclick="changeMyPassword('${UP_SESS.username}')">💾 ${upT("up_updatepass")}</button>
      <div id="up_passMsg" style="font-size:12px;margin-top:8px"></div>
    </div>
  </div>`;
}

/* ════════════════════════════════════════════════════════════════
   ADMIN INBOX — inside pedigree app (Requests section)
════════════════════════════════════════════════════════════════ */
function getAdminUnreadCount() {
  return getMessages().filter(m => m.to === "admin" && !m.read).length;
}

/* ════════════════════════════════════════════════════════════════
   ADMIN REQUESTS PAGE — grouped by sire/bull
════════════════════════════════════════════════════════════════ */
function renderAdminRequests() {
  const el = document.getElementById("pg-requests");
  if (!el) return;
  const msgs = getMessages()
    .filter(m => m.to === "admin" && m.type === "pedigree_request")
    .sort((a, b) => b.timestamp.localeCompare(a.timestamp));
  const unread = msgs.filter(m => !m.read).length;
  const pending = msgs.filter(m => m.status === "pending").length;

  // ── Group by sire name ──
  const bySire = {};
  msgs.forEach(m => {
    const sireName = (m.requestData?.sireName || "Unknown Sire").toUpperCase().trim();
    if (!bySire[sireName]) bySire[sireName] = [];
    bySire[sireName].push(m);
  });
  const sireGroups = Object.entries(bySire).sort((a, b) => b[1].length - a[1].length);

  el.innerHTML = `
  <!-- HEADER -->
  <div style="background:linear-gradient(135deg,#1e3a5f,#3a6bc4);border-radius:var(--rl);padding:14px 18px;color:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact;margin-bottom:12px">
    <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px">
      <div>
        <div style="font-size:clamp(14px,2vw,17px);font-weight:bold">📬 Pedigree Requests from Users</div>
        <div style="font-size:11px;opacity:.8;margin-top:3px">
          ${msgs.length} total request${msgs.length !== 1 ? "s" : ""} · ${pending} pending · ${unread} unread · ${sireGroups.length} sire${sireGroups.length !== 1 ? "s" : ""}
        </div>
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <button class="btn bg bsm" onclick="openAdminCompose()" style="font-size:clamp(10px,1vw,12px)">📨 Compose Message</button>
      <select id="req_filter_status" onchange="filterAdminRequests()" style="font-size:11px;padding:5px 9px;border:1px solid rgba(255,255,255,.3);border-radius:5px;background:rgba(255,255,255,.1);color:#fff">
          <option value="">All Statuses</option>
          <option value="pending">⏳ Pending</option>
          <option value="replied">💬 Replied</option>
          <option value="completed">✅ Completed</option>
          <option value="rejected">❌ Rejected</option>
        </select>
        <button class="btn bw bsm" onclick="openAdminCompose()" style="font-size:clamp(10px,1vw,12px);border:1px solid rgba(255,255,255,.4);color:#fff;background:rgba(255,255,255,.1)">📨 New Message</button>
      <input id="req_filter_search" placeholder="🔍 Search requests…" oninput="filterAdminRequests()"
          style="font-size:11px;padding:5px 9px;border:1px solid rgba(255,255,255,.3);border-radius:5px;background:rgba(255,255,255,.1);color:#fff;width:160px;outline:none"
          placeholder="Search owner, province, sire…">
      </div>
    </div>
  </div>

  ${
    !msgs.length
      ? `<div class="card"><div class="cb"><div class="empty">
    <div class="ei">📭</div><h3>No requests yet</h3>
    <p>Pedigree requests from users will appear here, grouped by sire/bull.</p>
  </div></div></div>`
      : ""
  }

  <!-- GROUPED BY SIRE -->
  <div id="reqGroupsContainer">
  ${sireGroups
    .map(([sireName, sireReqs]) => {
      const pending = sireReqs.filter(m => m.status === "pending").length;
      const unreadG = sireReqs.filter(m => !m.read).length;
      const breed = sireReqs[0]?.requestData?.sireBreed || "";
      const bcolor =
        breed === "Holstein" ? "#1e40af" : breed === "Montbéliarde" ? "#b45309" : "#1a4a2e";
      const bbg =
        breed === "Holstein" ? "#dbeafe" : breed === "Montbéliarde" ? "#fef3c7" : "#dcfce7";
      return `
    <div class="card req-group" data-sire="${sireName.toLowerCase()}" style="border-top:3px solid ${bcolor};margin-bottom:10px">
      <!-- Sire group header -->
      <div class="ch" style="cursor:pointer;flex-wrap:wrap;gap:8px" onclick="toggleSireGroup('sg_${sireName.replace(/[^a-z0-9]/gi, "_")}')">
        <div style="display:flex;align-items:center;gap:10px;flex:1;min-width:0">
          <span style="font-size:22px">🐂</span>
          <div>
            <h2 style="font-size:clamp(13px,1.4vw,15px);color:${bcolor}">${sireName}</h2>
            <div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-top:2px">
              ${breed ? `<span style="background:${bbg};color:${bcolor};padding:1px 6px;border-radius:5px;font-weight:700;margin-right:6px">${breed}</span>` : ""}
              AI Record · ${sireReqs[0]?.requestData?.aiRecord || "—"}
            </div>
          </div>
        </div>
        <div style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;flex-shrink:0">
          <span style="background:${bcolor};color:#fff;padding:3px 10px;border-radius:10px;font-family:sans-serif;font-size:11px;font-weight:700">${sireReqs.length} request${sireReqs.length !== 1 ? "s" : ""}</span>
          ${pending > 0 ? `<span style="background:#fef9c3;color:#854d0e;padding:3px 8px;border-radius:8px;font-size:10px;font-weight:700">⏳ ${pending} pending</span>` : ""}
          ${unreadG > 0 ? `<span style="background:#fee2e2;color:#991b1b;padding:3px 8px;border-radius:8px;font-size:10px;font-weight:700">● ${unreadG} new</span>` : ""}
          <span style="font-size:16px;color:var(--gm)" id="ico_sg_${sireName.replace(/[^a-z0-9]/gi, "_")}">▲</span>
        </div>
      </div>
      <!-- Individual requests under this sire -->
      <div id="sg_${sireName.replace(/[^a-z0-9]/gi, "_")}" class="cb" style="padding:0">
        ${sireReqs.map((m, idx) => buildAdminRequestCard(m, idx)).join("")}
      </div>
    </div>`;
    })
    .join("")}
  </div>

  <!-- Reply modal -->
  <div id="adminReplyModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center;overflow-y:auto">
    <div style="background:#fff;border-radius:var(--rl);padding:20px;width:min(540px,96vw);box-shadow:var(--shl);margin:20px auto">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px">
        <h3 style="color:var(--gd)">💬 Reply to User</h3>
        <button class="ib" onclick="document.getElementById('adminReplyModal').style.display='none'">✕</button>
      </div>
      <input type="hidden" id="adminReplyMsgId">
      <div class="fr full" style="margin-bottom:10px">
        <label>Subject</label><input id="adminReplySubj" placeholder="Re: Pedigree Request">
      </div>
      <div class="fr full">
        <label>Message *</label>
        <textarea id="adminReplyBody" style="min-height:120px" placeholder="Write your reply — include pedigree details, status updates, or requests for more information…"></textarea>
      </div>
      <input type="hidden" id="adminReplyAnimalId">
      <div id="ac_pedigree_preview" style="display:none;margin-top:8px;padding:8px 12px;background:#f0fdf4;border:1px solid #a7f3d0;border-radius:6px;font-size:11px;font-family:sans-serif;color:#065f46"></div>
      <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:12px;flex-wrap:wrap">
        <button class="btn bd" onclick="document.getElementById('adminReplyModal').style.display='none'">Cancel</button>
        <button class="btn bg" onclick="sendAdminReply()">📤 Send Reply</button>
        <button class="btn bg" style="background:#7c3aed" onclick="adminSendPedigree()">📄 Send with Pedigree PDF</button>
      </div>
    </div>
  </div>
  `;

  // Mark all as read and update badge
  msgs.forEach(m => {
    if (!m.read) {
      markRead(m.id);
    }
  });
  updateAdminBadge();
}

/* ── Single request card ── */
function buildAdminRequestCard(m, idx) {
  const d = m.requestData || {};
  const dt = m.timestamp
    ? new Date(m.timestamp).toLocaleString("en-GB", {
        day: "2-digit",
        month: "short",
        year: "numeric",
        hour: "2-digit",
        minute: "2-digit",
      })
    : "—";
  const statusMap = {
    pending: { bg: "#fef9c3", color: "#854d0e", icon: "⏳", label: "Pending" },
    replied: { bg: "#dbeafe", color: "#1e40af", icon: "💬", label: "Replied" },
    completed: { bg: "#d1fae5", color: "#065f46", icon: "✅", label: "Completed" },
    rejected: { bg: "#fee2e2", color: "#991b1b", icon: "❌", label: "Rejected" },
  };
  const st = statusMap[m.status] || statusMap.pending;
  const fromUser = getUsers().find(u => u.id === m.from) || { name: m.fromName || m.from };

  return `<div class="req-card" data-msgid="${m.id}" data-owner="${(d.ownerName || "").toLowerCase()}" data-prov="${(d.ownerProvince || "").toLowerCase()}" data-status="${m.status || "pending"}"
    style="border-bottom:1px solid var(--bdr);padding:14px 16px;background:${idx % 2 === 0 ? "#fff" : "#fafafa"}">
    <div style="display:flex;align-items:flex-start;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-bottom:10px">
      <div style="flex:1;min-width:200px">
        <!-- Animal info -->
        <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:6px">
          <span style="font-size:16px">${d.animalSex === "Male" ? "🐂" : "🐄"}</span>
          <div>
            <span style="font-family:sans-serif;font-size:13px;font-weight:700;color:var(--td)">${d.animalBreed || "Animal"} ${d.animalSex || ""}</span>
            <span style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-left:8px">DOB: ${d.animalDob || "—"}</span>
            ${d.animalRegId ? `<span style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-left:6px">Reg: ${d.animalRegId}</span>` : ""}
            ${d.animalEarTag ? `<span style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-left:6px">Tag: ${d.animalEarTag}</span>` : ""}
          </div>
        </div>
        <!-- Owner + province -->
        <div style="font-family:sans-serif;font-size:11px;color:var(--tl)">
          👤 <b>${d.ownerName || "—"}</b> · 📞 ${d.ownerPhone || "—"} · 📍 ${d.ownerProvince || "—"}${d.ownerDistrict ? ", " + d.ownerDistrict : ""}
          <span style="margin-left:8px;color:#3a6bc4">From: ${fromUser.name}</span>
        </div>
        <!-- Dam info (if provided) -->
        ${
          d.damName || d.damBreed
            ? `<div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-top:3px">
          🐄 Dam: ${d.damName || "—"} ${d.damBreed ? "(" + d.damBreed + ")" : ""} ${d.damMilk ? "· " + d.damMilk + " kg" : ""} ${d.damDob ? "· DOB: " + d.damDob : ""}
        </div>`
            : ""
        }
        <!-- AI Date -->
        <div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-top:2px">
          📅 AI Date: ${d.aiDate || "—"} ${d.aiTech ? "· Tech: " + d.aiTech : ""} · Received: ${dt}
        </div>
        ${d.notes ? `<div style="font-family:sans-serif;font-size:10px;color:#7c3aed;margin-top:3px;font-style:italic">📝 "${d.notes}"</div>` : ""}
      </div>
      <div style="display:flex;flex-direction:column;align-items:flex-end;gap:6px;flex-shrink:0">
        <span style="background:${st.bg};color:${st.color};padding:3px 10px;border-radius:10px;font-size:11px;font-weight:700">${st.icon} ${st.label}</span>
        ${d.photo ? `<img src="${d.photo}" style="width:50px;height:50px;border-radius:6px;object-fit:cover;border:2px solid var(--bdr)">` : ""}
      </div>
    </div>
    <!-- Actions -->
    <div style="display:flex;gap:6px;flex-wrap:wrap">
      <button class="btn bg bsm" style="font-size:clamp(10px,1vw,12px)" onclick="adminRegisterFromRequest('${m.id}')">
        📋 Register Pedigree
      </button>
      <button class="btn bw bsm" style="font-size:clamp(10px,1vw,12px);border:1px solid var(--gm)" onclick="adminReplyModal('${m.id}')">
        💬 Reply
      </button>
      <button class="btn bw bsm" style="font-size:clamp(10px,1vw,12px);border:1px solid #7c3aed;color:#7c3aed" onclick="adminOpenSendPedigree('${m.id}')">
        📄 Send PDF
      </button>
      <select onchange="adminUpdateStatus('${m.id}',this.value);this.value=''" style="font-size:11px;padding:4px 8px;border:1px solid var(--bdr);border-radius:5px">
        <option value="">📊 Update Status…</option>
        <option value="pending">⏳ Pending</option>
        <option value="replied">💬 Replied</option>
        <option value="completed">✅ Completed</option>
        <option value="rejected">❌ Rejected</option>
      </select>
      <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px);color:#c0392b" onclick="if(confirm('Delete this request?')){deleteMessage('${m.id}');renderAdminRequests();}">🗑</button>
    </div>
  </div>`;
}

/* ── Toggle sire group ── */
function toggleSireGroup(id) {
  const el = document.getElementById(id);
  const ico = document.getElementById("ico_" + id);
  if (!el) return;
  const open = el.style.display !== "none";
  el.style.display = open ? "none" : "block";
  if (ico) ico.textContent = open ? "▼" : "▲";
}

/* ── Filter requests ── */
function filterAdminRequests() {
  const q = (document.getElementById("req_filter_search")?.value || "").toLowerCase();
  const st = document.getElementById("req_filter_status")?.value || "";
  document.querySelectorAll(".req-card").forEach(card => {
    const owner = card.getAttribute("data-owner") || "";
    const prov = card.getAttribute("data-prov") || "";
    const stat = card.getAttribute("data-status") || "";
    const sire = card.closest(".req-group")?.getAttribute("data-sire") || "";
    const show =
      (!q || owner.includes(q) || prov.includes(q) || sire.includes(q)) && (!st || stat === st);
    card.style.display = show ? "" : "none";
  });
  // Hide sire groups with all cards hidden
  document.querySelectorAll(".req-group").forEach(grp => {
    const visible = [...grp.querySelectorAll(".req-card")].some(c => c.style.display !== "none");
    grp.style.display = visible ? "" : "none";
  });
}

/* ════════════════════════════════════════════════════════════════
   AUTO-FILL REGISTRATION FORM FROM REQUEST (all fields)
════════════════════════════════════════════════════════════════ */
function adminRegisterFromRequest(msgId) {
  const m = getMessages().find(x => x.id === msgId);
  if (!m) return;
  const d = m.requestData || {};

  // Open the correct form (male vs female)
  const sex = d.animalSex || "Female";
  openNew("", sex);

  setTimeout(() => {
    const s = (id, v) => {
      const e = document.getElementById(id);
      if (e && v !== undefined && v !== "") e.value = v;
    };

    // ── Animal identity ──
    s("f_name", d.animalName);
    s("f_dob", d.animalDob);
    s("f_regid", d.animalRegId);
    s("f_earTag", d.animalEarTag);
    s(
      "f_notes",
      `[From Request] AI Record: ${d.aiRecord} | AI Date: ${d.aiDate}${d.aiTech ? " | Tech: " + d.aiTech : ""} | Request: ${msgId}`
    );

    // Set breed tab
    const breedMap = {
      Holstein: "Holstein",
      Montbéliarde: "Montbeliard",
      "Local / Indigenous": "Local",
    };
    const normBrd = Object.entries(breedMap).find(
      ([k]) => d.animalBreed && d.animalBreed.includes(k)
    );
    if (normBrd) selBreed(normBrd[1]);
    else if (d.animalBreed && d.animalBreed.includes("Crossbred")) selBreed("Crossbred");

    // Bull role
    if (sex === "Male") {
      const roleRow = document.getElementById("roleFieldRow");
      if (roleRow) roleRow.style.display = "block";
      s("f_role", "Son");
    }

    // ── Sire information ──
    s("f_sireName", d.sireName);
    s("f_sireId", d.aiRecord); // AI straw = sire ID link
    const sireBrMap = { Holstein: "Holstein", Montbéliarde: "Montbeliard" };
    const sb = Object.entries(sireBrMap).find(([k]) => d.sireBreed && d.sireBreed.includes(k));
    s("f_sireBreed", sb ? sb[1] : "Holstein");

    // ── Dam information (all optional) ──
    s("f_damName", d.damName);
    s("f_damId", d.damRegId);
    s("f_damDob", d.damDob);
    const dbMap = { Holstein: "Holstein", Montbéliarde: "Montbeliard", Local: "Local" };
    const db = Object.entries(dbMap).find(([k]) => d.damBreed && d.damBreed.includes(k));
    s("f_damBreed", db ? db[1] : "Local");
    s("f_damMilk", d.damMilk);

    // ── Province / District ──
    const provEl = document.getElementById("f_province");
    if (provEl && d.ownerProvince) {
      // Populate province options first
      if (provEl.options.length <= 1 && typeof getProvincesAF === "function") {
        provEl.innerHTML =
          '<option value="">— Select Province —</option>' +
          getProvincesAF()
            .map(p => `<option>${p}</option>`)
            .join("");
      }
      provEl.value = d.ownerProvince;
      cascadeDistrict("f_province", "f_district");
      setTimeout(() => {
        s("f_district", d.ownerDistrict);
      }, 150);
    }

    // ── Assign to owner if user has a herd ──
    const fromUser = getUsers().find(u => u.id === m.from);
    if (fromUser && fromUser.ownerId) {
      populateOwnerSelect(fromUser.ownerId);
      s("f_ownerId", fromUser.ownerId);
    } else {
      populateOwnerSelect("");
    }

    // Stay on current page — modal is visible on top
    showToast("✅ Form pre-filled — review all fields and click Save");

    // Store the request ID so we can link the saved animal back
    window._pendingRequestLink = { msgId, pendingId: "pending_" + msgId };
  }, 200);
}

/* ════════════════════════════════════════════════════════════════
   ADMIN SENDS PEDIGREE PDF TO USER INBOX
════════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════
   ADMIN MESSAGING & PDF DELIVERY
════════════════════════════════════════════════════════════════ */

/* ── Open "Send PDF" overlay for a specific request ── */
function adminOpenSendPedigree(msgId) {
  const allAnimals = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
  const linked = allAnimals.filter(
    a =>
      a.pendingRequest === msgId ||
      (a.notes && a.notes.includes(msgId)) ||
      (a.notes && a.notes.includes("Request: " + msgId))
  );
  const m = getMessages().find(x => x.id === msgId);
  const d = m?.requestData || {};
  const animal = linked.length ? linked[0] : null;

  document.getElementById("adminReplyMsgId").value = msgId;
  document.getElementById("adminReplyAnimalId").value = animal ? animal.id : "";

  // Show which animal will be sent
  const prev = document.getElementById("ac_pedigree_preview");
  if (prev) {
    if (animal) {
      prev.style.display = "block";
      prev.innerHTML = `✅ Pedigree found: <b>${animal.name}</b> · ${animal.regId || "—"} · ${animal.breed} · DOB: ${animal.dob || "—"}`;
    } else {
      prev.style.display = "block";
      prev.style.cssText =
        "display:block;margin-top:8px;padding:8px 12px;background:#fee2e2;border:1px solid #fca5a5;border-radius:6px;font-size:11px;color:#991b1b";
      prev.innerHTML =
        "⚠️ No registered pedigree found for this request yet. Register the pedigree first via '📋 Register Pedigree'.";
    }
  }

  document.getElementById("adminReplySubj").value =
    "✅ Pedigree Certificate Ready — " + (d.sireName || "");
  document.getElementById("adminReplyBody").value =
    `Dear ${m?.fromName || "Owner"},\n\nYour pedigree certificate has been registered in the Larspeenghar system.\n\n` +
    `Animal: ${animal ? animal.name : (d.animalBreed || "") + " " + (d.animalSex || "")}\n` +
    `Sire: ${d.sireName || "—"} · AI Record: ${d.aiRecord || "—"}\n` +
    `Owner: ${d.ownerName || "—"} · Province: ${d.ownerProvince || "—"}\n\n` +
    (animal
      ? "Click the button in your inbox to view and print your official pedigree certificate.\n"
      : "Please contact us for your certificate details.\n") +
    "\nLarspeenghar Livestock & Agriculture Company";
  document.getElementById("adminReplyModal").style.display = "flex";
}

function adminReplyModal(msgId) {
  const m = getMessages().find(x => x.id === msgId);
  const d = m?.requestData || {};
  document.getElementById("adminReplyMsgId").value = msgId;
  document.getElementById("adminReplyAnimalId").value = "";
  document.getElementById("adminReplySubj").value =
    "Re: Pedigree Request — " + (d.sireName || m?.subject || "");
  document.getElementById("adminReplyBody").value = "";
  document.getElementById("adminReplyModal").style.display = "flex";
}

/* ── Send reply or pedigree PDF to user inbox ── */
function sendAdminReply(deliveryType) {
  const msgId = document.getElementById("adminReplyMsgId").value;
  const animalId = document.getElementById("adminReplyAnimalId")?.value || "";
  const subj = document.getElementById("adminReplySubj").value.trim();
  const body = document.getElementById("adminReplyBody").value.trim();
  if (!body) {
    alert("Please write a message.");
    return;
  }
  const orig = getMessages().find(x => x.id === msgId);
  if (!orig) {
    alert("Original request not found.");
    return;
  }

  const isPdf = deliveryType === "pedigree_delivered";

  // Embed the full animal data so user can open it from any device
  let embeddedAnimal = null;
  if (isPdf && animalId) {
    try {
      const allA = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
      embeddedAnimal = allA.find(a => a.id === animalId) || null;
    } catch (e) {}
  }

  addMessage({
    type: isPdf ? "pedigree_delivered" : "admin_reply",
    from: "admin",
    fromName: "Larspeenghar Admin",
    to: orig.from,
    subject: subj || "Message from Admin",
    body,
    preview: body.substring(0, 100),
    read: false,
    linkedAnimalId: animalId || "",
    embeddedAnimal: embeddedAnimal, // full pedigree data embedded in message
  });

  if (isPdf) {
    adminUpdateStatus(msgId, "completed");
    // Also update the linked animal's pendingStatus
    if (animalId) {
      try {
        const all = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
        const a = all.find(x => x.id === animalId);
        if (a) {
          a.pendingStatus = "completed";
          localStorage.setItem("larspeenghar_v7", JSON.stringify(all));
        }
      } catch (e) {}
    }
  } else {
    adminUpdateStatus(msgId, "replied");
  }

  document.getElementById("adminReplyModal").style.display = "none";
  renderAdminRequests();
  showToast((isPdf ? "📄 Pedigree PDF sent" : "💬 Reply sent") + " to " + orig.fromName + " ✓");
}

function adminSendPedigree() {
  sendAdminReply("pedigree_delivered");
}
function adminUpdateStatus(msgId, status) {
  if (!status) return;
  const msgs = getMessages();
  const m = msgs.find(x => x.id === msgId);
  if (m) {
    m.status = status;
    saveMessages(msgs);
  }
  renderAdminRequests();
}

/* ════════════════════════════════════════════════════════════════
   ADMIN COMPOSE — Send message to any user's inbox
════════════════════════════════════════════════════════════════ */
function openAdminCompose(preselectedUserId) {
  const users = getUsers().filter(u => u.role !== "admin");
  const overlay = document.getElementById("adminComposeModal");
  if (overlay) {
    const sel = document.getElementById("ac_recipient");
    if (sel) {
      sel.innerHTML =
        '<option value="">— Select user —</option>' +
        users
          .map(
            u =>
              `<option value="${u.id}"${u.id === preselectedUserId ? " selected" : ""}>${u.name || u.username} (${u.username})</option>`
          )
          .join("");
    }
    document.getElementById("ac_subject").value = "";
    document.getElementById("ac_body").value = "";
    overlay.style.display = "flex";
    return;
  }
  // Build modal if not present yet
  const div = document.createElement("div");
  div.id = "adminComposeModal";
  div.style.cssText =
    "position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:400;display:flex;align-items:center;justify-content:center;overflow-y:auto";
  div.innerHTML = `
  <div style="background:#fff;border-radius:var(--rl);padding:20px;width:min(560px,96vw);max-height:90vh;overflow-y:auto;box-shadow:var(--shl);margin:20px auto">
    <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px">
      <h3 style="color:var(--gd);font-size:16px">📨 Compose Message to User</h3>
      <button class="ib" onclick="document.getElementById('adminComposeModal').style.display='none'" style="font-size:18px">✕</button>
    </div>
    <div class="fr full" style="margin-bottom:10px">
      <label>Recipient *</label>
      <select id="ac_recipient" style="font-size:13px">
        <option value="">— Select user —</option>
        ${users.map(u => `<option value="${u.id}"${u.id === preselectedUserId ? " selected" : ""}>${u.name || u.username} (${u.username})</option>`).join("")}
      </select>
    </div>
    <div class="fr full" style="margin-bottom:10px">
      <label>Subject *</label>
      <input id="ac_subject" placeholder="Message subject" style="font-size:13px">
    </div>
    <div class="fr full" style="margin-bottom:14px">
      <label>Message *</label>
      <textarea id="ac_body" style="min-height:120px;font-size:13px" placeholder="Write your message to the user…"></textarea>
    </div>
    <div style="display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap">
      <button class="btn bd" onclick="document.getElementById('adminComposeModal').style.display='none'">Cancel</button>
      <button class="btn bg" onclick="sendAdminCompose()">📤 Send Message</button>
    </div>
    <div id="ac_msg" style="font-size:11px;margin-top:8px"></div>
  </div>`;
  document.body.appendChild(div);
}

function sendAdminCompose() {
  const recipId = (document.getElementById("ac_recipient")?.value || "").trim();
  const subj = (document.getElementById("ac_subject")?.value || "").trim();
  const body = (document.getElementById("ac_body")?.value || "").trim();
  const msg = document.getElementById("ac_msg");
  if (!recipId) {
    if (msg) {
      msg.textContent = "❌ Please select a recipient.";
      msg.style.color = "#c0392b";
    }
    return;
  }
  if (!subj) {
    if (msg) {
      msg.textContent = "❌ Please enter a subject.";
      msg.style.color = "#c0392b";
    }
    return;
  }
  if (!body) {
    if (msg) {
      msg.textContent = "❌ Please write a message.";
      msg.style.color = "#c0392b";
    }
    return;
  }
  const recip = getUsers().find(u => u.id === recipId);
  addMessage({
    type: "admin_reply",
    from: "admin",
    fromName: "Larspeenghar Admin",
    to: recipId,
    subject: subj,
    body,
    preview: body.substring(0, 100),
    read: false,
    linkedAnimalId: "",
  });
  if (msg) {
    msg.textContent = "✅ Message sent to " + (recip?.name || recip?.username || "user") + "!";
    msg.style.color = "#16a34a";
  }
  document.getElementById("ac_subject").value = "";
  document.getElementById("ac_body").value = "";
  setTimeout(() => {
    document.getElementById("adminComposeModal").style.display = "none";
  }, 1200);
  showToast("Message sent ✓");
  updateAdminBadge();
}

/* ══════════════════════════════════════════════════════════════════
   INBREEDING ENGINE — Wright's Path Coefficient Method
   Reference: Wright (1922), Van Raden (1992), CDCB methodology
   ══════════════════════════════════════════════════════════════════ */

/* ── Breed population averages (editable in the UI) ── */

/* ════════════════════════════════════════════════════════════════
   BREED COMPOSITION & GENERATION TRACKING ENGINE
   Tracks Holstein%, Montbéliarde%, Local% through pedigree
   Supports F1→F6, Purebred, High Grade, Grading-Up programs
   ════════════════════════════════════════════════════════════════ */

const BREEDS_CFG = {
  Holstein: { emoji: "🖤", color: "#3a6bc4", bg: "#dbeafe", short: "HO" },
  Montbeliard: { emoji: "🟠", color: "#e67e22", bg: "#fef3c7", short: "MO", label: "Montbéliarde" },
  Local: { emoji: "🌿", color: "#16a34a", bg: "#dcfce7", short: "LC" },
  Crossbred: { emoji: "🔀", color: "#7c3aed", bg: "#f3e8ff", short: "XB" },
};

function normBreed(b) {
  if (!b) return "Local";
  const l = b.toLowerCase().trim();
  if (l.includes("holstein") || l === "ho") return "Holstein";
  if (l.includes("montb") || l === "mo" || l.includes("montbéliard") || l.includes("month"))
    return "Montbeliard";
  if (
    l.includes("local") ||
    l.includes("indig") ||
    l.includes("native") ||
    l.includes("desi") ||
    l === "lc" ||
    l === "indigenous"
  )
    return "Local";
  return b;
}

/* Build breed composition {breed: %} from pedigree (recursive, max depth 6) */
function calcBreedComp(a, depth) {
  depth = depth || 0;
  if (!a || depth > 6) return {};

  // Manual override takes priority
  if (
    a.breedCompOverride &&
    typeof a.breedCompOverride === "object" &&
    Object.keys(a.breedCompOverride).length > 0
  ) {
    return a.breedCompOverride;
  }

  const hasSire = a.sireLinkedId || (a.sireName && a.sireName.trim());
  const hasDam = a.damLinkedId || (a.damName && a.damName.trim());

  // No parents known → pure breed from breed field
  if (!hasSire && !hasDam) {
    const k = normBreed(a.breed || "Local");
    return { [k]: 100 };
  }

  // --- Sire composition ---
  let sc = {};
  if (a.sireLinkedId) {
    const sr = byId(a.sireLinkedId);
    sc = sr
      ? calcBreedComp(sr, depth + 1)
      : { [normBreed(a.sireBreed || a.breed || "Holstein")]: 100 };
  } else if (a.sireName) {
    sc = { [normBreed(a.sireBreed || a.breed || "Holstein")]: 100 };
  }

  // --- Dam composition ---
  let dc = {};
  if (a.damLinkedId) {
    const dm = byId(a.damLinkedId);
    dc = dm ? calcBreedComp(dm, depth + 1) : { [normBreed(a.damBreed || "Local")]: 100 };
  } else if (a.damName) {
    dc = { [normBreed(a.damBreed || "Local")]: 100 };
  }

  // Average sire + dam
  const comp = {};
  new Set([...Object.keys(sc), ...Object.keys(dc)]).forEach(b => {
    const v = ((sc[b] || 0) + (dc[b] || 0)) / 2;
    if (v > 0.05) comp[b] = v;
  });
  return comp;
}

/* Determine generation label from composition */
function getGenInfo(comp) {
  const entries = Object.entries(comp).filter(([, v]) => v > 0.1);
  if (!entries.length)
    return {
      gen: "Unknown",
      desc: "No pedigree data",
      dominant: "",
      pct: 0,
      isPure: false,
      color: "#888",
      bg: "#f3f4f6",
    };
  entries.sort((a, b) => b[1] - a[1]);
  const [dominant, pct] = entries[0];

  if (pct >= 99.5) {
    const bc = BREEDS_CFG[dominant] || { color: "#555", bg: "#f3f4f6" };
    return {
      gen: "Purebred",
      desc: "100% " + dominant,
      dominant,
      pct: 100,
      isPure: true,
      color: bc.color,
      bg: bc.bg,
    };
  }

  const t = 0.8; // tolerance %
  const genMap = [
    [50, "F1", "First Cross (50/50)"],
    [75, "F2", "Grade-Up 2nd Gen · 75%"],
    [87.5, "F3", "Grade-Up 3rd Gen · 87.5%"],
    [93.75, "F4", "Grade-Up 4th Gen · 93.75%"],
    [96.875, "F5", "Grade-Up 5th Gen · 96.9%"],
    [98.4375, "F6", "Grade-Up 6th Gen · 98.4%"],
  ];

  for (const [p, g, d] of genMap) {
    if (Math.abs(pct - p) < t) {
      const colors = {
        F1: { color: "#92400e", bg: "#fef3c7" },
        F2: { color: "#9a3412", bg: "#ffedd5" },
        F3: { color: "#881337", bg: "#ffe4e6" },
        F4: { color: "#6b21a8", bg: "#f3e8ff" },
        F5: { color: "#1e40af", bg: "#dbeafe" },
        F6: { color: "#065f46", bg: "#d1fae5" },
      };
      const c = colors[g] || { color: "#374151", bg: "#f3f4f6" };
      return { gen: g, desc: d + " " + dominant, dominant, pct, isPure: false, ...c };
    }
  }

  if (pct >= 93.75) {
    const bc = BREEDS_CFG[dominant] || { color: "#14532d", bg: "#dcfce7" };
    return {
      gen: "High Grade",
      desc: pct.toFixed(1) + "% " + dominant + " (High Grade)",
      dominant,
      pct,
      isPure: false,
      color: bc.color,
      bg: bc.bg,
    };
  }

  return {
    gen: "Cross",
    desc: pct.toFixed(1) + "% " + dominant,
    dominant,
    pct,
    isPure: false,
    color: "#374151",
    bg: "#f3f4f6",
  };
}

/* Compact generation badge */
function genBadge(comp) {
  const g = getGenInfo(comp);
  return (
    `<span style="display:inline-flex;align-items:center;gap:5px;background:${g.bg};color:${g.color};padding:3px 10px;border-radius:14px;font-family:sans-serif;font-size:12px;font-weight:700;border:1.5px solid ${g.color}44;white-space:nowrap">` +
    `<span style="font-size:13px">${g.gen}</span>` +
    `<span style="font-weight:400;font-size:10px">${g.desc.replace("Montbeliard", "Montbéliarde")}</span>` +
    `</span>`
  );
}

/* Breed composition horizontal bar */
function compBar(comp, height) {
  height = height || 20;
  const entries = Object.entries(comp)
    .filter(([, v]) => v > 0.1)
    .sort((a, b) => b[1] - a[1]);
  if (!entries.length) return "";
  const segs = entries
    .map(([b, v]) => {
      const bc = BREEDS_CFG[b] || { color: "#888", emoji: "🐄" };
      const lbl = v >= 12 ? bc.emoji + " " + v.toFixed(0) + "%" : v >= 6 ? v.toFixed(0) + "%" : "";
      return (
        `<div title="${b}: ${v.toFixed(2)}%" style="background:${bc.color};height:100%;width:${v.toFixed(2)}%;display:flex;align-items:center;justify-content:center;overflow:hidden;transition:width .3s;flex-shrink:0">` +
        `<span style="color:#fff;font-size:9px;font-weight:bold;white-space:nowrap;padding:0 2px">${lbl}</span>` +
        `</div>`
      );
    })
    .join("");
  return `<div style="width:100%;height:${height}px;border-radius:${height / 2}px;overflow:hidden;background:#e5e7eb;display:flex;border:1px solid rgba(0,0,0,.1)">${segs}</div>`;
}

/* Full breed composition detail row */
function compDetail(comp) {
  const entries = Object.entries(comp)
    .filter(([, v]) => v > 0.1)
    .sort((a, b) => b[1] - a[1]);
  return entries
    .map(([b, v]) => {
      const bc = BREEDS_CFG[b] || { emoji: "🐄", color: "#555" };
      return `<span style="font-family:sans-serif;font-size:11px;color:${bc.color};white-space:nowrap">${bc.emoji} <b>${b}</b> ${v.toFixed(2)}%</span>`;
    })
    .join(' <span style="color:#ccc">|</span> ');
}

/* ── BC preview in form ── */
function updateBCPreview() {
  const ho = parseFloat(document.getElementById("f_bc_ho")?.value) || 0;
  const mo = parseFloat(document.getElementById("f_bc_mo")?.value) || 0;
  const lc = parseFloat(document.getElementById("f_bc_lc")?.value) || 0;
  const on = document.getElementById("f_bc_other_name")?.value.trim() || "";
  const op = parseFloat(document.getElementById("f_bc_other_pct")?.value) || 0;
  const tot = ho + mo + lc + op;
  const el = document.getElementById("bc_prev");
  if (!el) return;
  if (tot === 0) {
    el.innerHTML = "Auto-calculated from pedigree";
    return;
  }
  const comp = {};
  if (ho > 0) comp["Holstein"] = ho;
  if (mo > 0) comp["Montbeliard"] = mo;
  if (lc > 0) comp["Local"] = lc;
  if (on && op > 0) comp[on] = op;
  const g = getGenInfo(comp);
  const warn =
    Math.abs(tot - 100) > 0.1
      ? `<span style="color:#c0392b"> ⚠ Total = ${tot.toFixed(1)}% (must be 100%)</span>`
      : ``;
  el.innerHTML = `${genBadge(comp)}${warn}`;
}

/* ════════════════════════════════════════════════════════════════
   AFGHANISTAN — 34 PROVINCES & DISTRICTS
   Used for cascading province → district dropdowns
   ════════════════════════════════════════════════════════════════ */
const AF_PROVINCES = {
  Badakhshan: [
    "Fayzabad",
    "Arghanjkhwa",
    "Baharak",
    "Darayim",
    "Eshkashem",
    "Jurm",
    "Keran wa Munjan",
    "Khash",
    "Kishim",
    "Kohistan",
    "Kuran wa Munjan",
    "Raghistan",
    "Shighnan",
    "Shuhada",
    "Tagab",
    "Tishkan",
    "Wakhan",
    "Warduj",
    "Wurduj",
    "Zebak",
  ],
  Badghis: ["Qala i Naw", "Ab Kamari", "Bala Murghab", "Ghormach", "Jawand", "Muqur", "Qadis"],
  Baghlan: [
    "Pul-i-Khumri",
    "Andarab",
    "Baghlan-e-Jadid",
    "Burkah",
    "Dahana-i-Ghuri",
    "Doshi",
    "Guzargah-e-Nur",
    "Khwaja Hijran",
    "Khost wa Fereng",
    "Nahrin",
    "Tala wa Barfak",
  ],
  Balkh: [
    "Mazar-i-Sharif",
    "Balkh",
    "Char Bolak",
    "Char Kint",
    "Chimtal",
    "Dawlatabad",
    "Dehdadi",
    "Dihdadi",
    "Kaldar",
    "Khulm",
    "Kishindeh",
    "Marmal",
    "Nahr-i-Shahi",
    "Sholgarah",
    "Shortepa",
    "Zari",
  ],
  Bamyan: ["Bamyan City", "Kahmard", "Panjab", "Sayghan", "Shibar", "Waras", "Yakawlang"],
  Daykundi: [
    "Nili",
    "Ashtarlai",
    "Gizab",
    "Irakht",
    "Kajran",
    "Khidir",
    "Miramor",
    "Sang-i-Takht",
    "Shahristan",
  ],
  Farah: [
    "Farah City",
    "Anar Dara",
    "Bakwa",
    "Balabuluk",
    "Bala Buluk",
    "Gulistan",
    "Khaki Safed",
    "Lash wa Juwayn",
    "Pur Chaman",
    "Shib Koh",
  ],
  Faryab: [
    "Maymana",
    "Almar",
    "Andkhoy",
    "Belcharagh",
    "Dawlatabad",
    "Gurziwan",
    "Khan Chahar Bagh",
    "Khwaja Sabz Posh",
    "Kohistan",
    "Pashtunkot",
    "Qaysar",
    "Shirin Tagab",
  ],
  Ghazni: [
    "Ghazni City",
    "Ab Band",
    "Ajristan",
    "Andar",
    "De Merdah",
    "Dih Yak",
    "Gelan",
    "Giro",
    "Jaghatu",
    "Jaghori",
    "Khwaja Omari",
    "Malistan",
    "Moqor",
    "Nawa",
    "Nawur",
    "Qarabagh",
    "Rashidan",
    "Waghaz",
    "Wali Muhammad Shahid",
    "Zanakhan",
    "Zana Khan",
  ],
  Ghor: [
    "Chaghcharan",
    "Chahar Sada",
    "Dawlatyar",
    "Dulaina",
    "Lal wa Sar Jangal",
    "Pasaband",
    "Saghar",
    "Shahrak",
    "Taywara",
    "Tolak",
  ],
  Helmand: [
    "Lashkar Gah",
    "Baghran",
    "Dishu",
    "Garmser",
    "Kajaki",
    "Marjah",
    "Musa Qala",
    "Nad Ali",
    "Nawa-i-Barakzayi",
    "Nawzad",
    "Nahr-e-Saraj",
    "Reg",
    "Sangin",
    "Washer",
  ],
  Herat: [
    "Herat City",
    "Adraskan",
    "Chishti Sharif",
    "Farsi",
    "Ghoryan",
    "Gulran",
    "Guzara",
    "Injil",
    "Karrukh",
    "Kohsan",
    "Kushk",
    "Kushk-i-Kuhna",
    "Obeh",
    "Pashtun Zarghun",
    "Robat Sangi",
    "Shindand",
    "Tir Pul",
    "Zinda Jan",
  ],
  Jawzjan: [
    "Sheberghan",
    "Aqcha",
    "Darzab",
    "Fayzabad",
    "Khanaqa",
    "Khamyab",
    "Mingajik",
    "Mardyan",
    "Qos Tepa",
    "Qarqin",
  ],
  Kabul: [
    "Kabul City",
    "Bagrami",
    "Chahar Asyab",
    "Dehsabz",
    "Estalef",
    "Farza",
    "Guldara",
    "Istalef",
    "Kalakan",
    "Khaki Jabbar",
    "Mir Bacha Kot",
    "Musahi",
    "Paghman",
    "Qarabagh",
    "Shakardara",
    "Surobi",
  ],
  Kandahar: [
    "Kandahar City",
    "Arghistan",
    "Arghandab",
    "Daman",
    "Dand",
    "Ghorak",
    "Khakrez",
    "Maiwand",
    "Maruf",
    "Nesh",
    "Panjwai",
    "Reg",
    "Shah Wali Kot",
    "Spin Boldak",
    "Zhari",
  ],
  Kapisa: [
    "Mahmud Raqi",
    "Alasay",
    "Hisa-i-Awal Kohistan",
    "Hisa-i-Duwum Kohistan",
    "Koh Band",
    "Kunar",
    "Nijrab",
    "Tagab",
  ],
  Khost: [
    "Khost City",
    "Bak",
    "Gurbuz",
    "Ismail Khel",
    "Mandozai",
    "Musa Khel",
    "Nadir Shah Kot",
    "Qalandar",
    "Sabari",
    "Shamal",
    "Spera",
    "Tani",
    "Terezayi",
  ],
  Kunar: [
    "Asadabad",
    "Asmar",
    "Bar Kunar",
    "Chawkay",
    "Dangam",
    "Dara-i-Pech",
    "Ghaziabad",
    "Khas Kunar",
    "Marawara",
    "Nari",
    "Nurgal",
    "Shaygal",
    "Wata Pur",
  ],
  Kunduz: [
    "Kunduz City",
    "Ali Abad",
    "Chahar Dara",
    "Dasht-i-Archi",
    "Imam Sahib",
    "Khanabad",
    "Khan Abad",
    "Qala-i-Zal",
  ],
  Laghman: ["Mihtarlam", "Alingar", "Alishang", "Dawlatshah", "Qarghayi"],
  Logar: ["Pul-i-Alam", "Azra", "Baraki Barak", "Charkh", "Kharwar", "Khoshi", "Mohammad Agha"],
  Nangarhar: [
    "Jalalabad",
    "Achin",
    "Batikot",
    "Bati Kot",
    "Chaparhar",
    "Dara-i-Noor",
    "Dehbala",
    "Dur Baba",
    "Goshta",
    "Hesarak",
    "Kama",
    "Khogyani",
    "Kot",
    "Kuz Kunar",
    "Lalpora",
    "Mohmand Dara",
    "Muhmand Dara",
    "Nazyan",
    "Pachir wa Agam",
    "Rodat",
    "Sherzad",
    "Shinwar",
    "Sorkhrod",
  ],
  Nimroz: ["Zaranj", "Chakhansur", "Chahar Burjak", "Delaram", "Khash Rod"],
  Nuristan: ["Parun", "Bargi Matal", "Kamdesh", "Mandogal", "Nurgaram", "Waygal", "Wama"],
  Paktia: [
    "Gardez",
    "Ahmad Khel",
    "Chamkani",
    "Dand wa Patan",
    "Dzadran",
    "Gerda Seray",
    "Jadran",
    "Jani Khel",
    "Laja Ahmad Khel",
    "Laja Mangal",
    "Mirzaka",
    "Sayyid Karam",
    "Shwak",
    "Wuza Zadran",
    "Zurmat",
  ],
  Paktika: [
    "Sharan",
    "Barmal",
    "Dila wa Khushamond",
    "Gayan",
    "Gomal",
    "Janikhel",
    "Mata Khan",
    "Naka",
    "Nika",
    "Omna",
    "Orgun",
    "Sar Hawza",
    "Terwa",
    "Turwo",
    "Urgun",
    "Waza Khwa",
    "Yahya Khel",
    "Zerok",
  ],
  Parwan: [
    "Charikar",
    "Bagram",
    "Ghorband",
    "Jabal us Saraj",
    "Koh-i-Safi",
    "Salang",
    "Sayed Khel",
    "Shinwari",
    "Shikh Ali",
    "Surkhi Parsa",
  ],
  Samangan: [
    "Aybak",
    "Dara-i-Suf Bala",
    "Dara-i-Suf Payin",
    "Feroz Nakhchir",
    "Hazrat Sultan",
    "Khuram wa Sarbagh",
    "Ruyi Du Ab",
  ],
  "Sar-e Pol": [
    "Sar-e Pol City",
    "Balkhab",
    "Gosfandi",
    "Kohestanat",
    "Sayyad",
    "Sanchaarak",
    "Sozma Qala",
  ],
  Takhar: [
    "Taloqan",
    "Baharak",
    "Bangi",
    "Chal",
    "Darqad",
    "Farkhar",
    "Hazar Sumuch",
    "Ishkamesh",
    "Kalafgan",
    "Khwaja Bahauddin",
    "Khwaja Ghar",
    "Namak Ab",
    "Rostaq",
    "Warsaj",
    "Yangi Qala",
  ],
  Uruzgan: [
    "Tarin Kowt",
    "Charchino",
    "Chinarto",
    "Dehrawood",
    "Gizab",
    "Khas Uruzgan",
    "Shah Wali Kot",
  ],
  Wardak: [
    "Maidan Shahr",
    "Chak",
    "Daimirdad",
    "Day Mirdad",
    "Hesa-i-Awal Behsud",
    "Jalrez",
    "Markazi Behsud",
    "Nirkh",
    "Saydabad",
    "Salar",
  ],
  Zabul: [
    "Qalat",
    "Arghandab",
    "Atghar",
    "Daychopan",
    "Khak Afghan",
    "Mizan",
    "Nawbahar",
    "Naw Bahar",
    "Shahjoy",
    "Shamulzayi",
    "Shinkay",
    "Tarnak wa Jaldak",
  ],
};

function getProvincesAF() {
  return Object.keys(AF_PROVINCES).sort();
}
function getDistrictsAF(province) {
  return AF_PROVINCES[province] || [];
}

/* Build a province <select> + district <select> pair, cascading */
function buildProvinceSelect(provId, distId, selProv, selDist) {
  const provs = getProvincesAF();
  let ph = `<select id="${provId}" style="font-size:13px" onchange="cascadeDistrict('${provId}','${distId}')">
    <option value="">— Select Province —</option>
    ${provs.map(p => `<option${p === selProv ? " selected" : ""}>${p}</option>`).join("")}
  </select>`;
  const dists = selProv ? getDistrictsAF(selProv) : [];
  let dh = `<select id="${distId}" style="font-size:13px">
    <option value="">— Select District —</option>
    ${dists.map(d => `<option${d === selDist ? " selected" : ""}>${d}</option>`).join("")}
  </select>`;
  return { provHtml: ph, distHtml: dh };
}

function cascadeDistrict(provId, distId) {
  const prov = document.getElementById(provId)?.value || "";
  const sel = document.getElementById(distId);
  if (!sel) return;
  const dists = getDistrictsAF(prov);
  sel.innerHTML =
    `<option value="">— Select District —</option>` +
    dists.map(d => `<option>${d}</option>`).join("");
}

let BREED_AVG = (function () {
  try {
    return (
      JSON.parse(localStorage.getItem("lp_breed_avg") || "null") || {
        "Holstein (USA)": { avg: 9.99, trend: "+0.20% / year", source: "CDCB 2024" },
        "Holstein (Europe)": { avg: 8.5, trend: "+0.18% / year", source: "Interbull 2023" },
        Montbéliarde: { avg: 4.2, trend: "+0.12% / year", source: "UNCEIA 2023" },
        "Brown Swiss": { avg: 5.8, trend: "+0.15% / year", source: "ASA 2023" },
        Jersey: { avg: 10.5, trend: "+0.22% / year", source: "CDCB 2024" },
      }
    );
  } catch (e) {
    return {};
  }
})();
function saveBreedAvg() {
  try {
    localStorage.setItem("lp_breed_avg", JSON.stringify(BREED_AVG));
  } catch (e) {}
}

/* ── Build full ancestor map from an animal data object ──
   Returns Map<ancestorKey, Array<number>>  where number = generation distance (1=parent,2=grandparent,…)
   ancestorKey: "ID:b01" for linked animals, "NM:SANDYVALLEYCHROME" for name-matched */
function buildAncMap(aData, depth, maxDepth, map) {
  if (!aData || depth >= maxDepth) return;
  map = map || new Map();

  function addAnc(key, gen) {
    if (!key) return;
    if (!map.has(key)) map.set(key, []);
    map.get(key).push(gen);
  }
  function normName(n) {
    return n ? n.trim().toUpperCase().replace(/\s+/g, " ") : "";
  }

  // ── Sire branch ──
  const sireKey = aData.sireLinkedId
    ? "ID:" + aData.sireLinkedId
    : aData.sireName
      ? "NM:" + normName(aData.sireName)
      : null;
  if (sireKey) {
    addAnc(sireKey, depth + 1);
  }
  const sireRec = aData.sireLinkedId ? byId(aData.sireLinkedId) : null;
  if (sireRec) {
    buildAncMap(sireRec, depth + 1, maxDepth, map);
  } else {
    // Use manually entered grandparents
    if (aData.ssName) {
      addAnc("NM:" + normName(aData.ssName), depth + 2);
    }
    if (aData.sdName) {
      addAnc("NM:" + normName(aData.sdName), depth + 2);
    }
    // Great-grandparents if sire record has them
    if (depth + 2 < maxDepth) {
      if (aData.ssName && aData.ssBreed) {
        /* only name-keyed, already added */
      }
    }
  }

  // ── Dam branch ──
  const damKey = aData.damLinkedId
    ? "ID:" + aData.damLinkedId
    : aData.damName
      ? "NM:" + normName(aData.damName)
      : null;
  if (damKey) {
    addAnc(damKey, depth + 1);
  }
  const damRec = aData.damLinkedId ? byId(aData.damLinkedId) : null;
  if (damRec) {
    buildAncMap(damRec, depth + 1, maxDepth, map);
  } else {
    if (aData.dsName) {
      addAnc("NM:" + normName(aData.dsName), depth + 2);
    }
    if (aData.ddName) {
      addAnc("NM:" + normName(aData.ddName), depth + 2);
    }
  }
  return map;
}

/* ── Ancestor label lookup ── */
function ancLabel(key) {
  if (key.startsWith("ID:")) {
    const a = byId(key.slice(3));
    return a ? a.name + " (" + a.regId + ")" : key.slice(3);
  }
  return key.slice(3); // strip "NM:"
}

/* ── Wright's inbreeding coefficient ──
   Calculates expected F of offspring from sireData × damData cross.
   Returns { F: percent, common: [{key,label,contrib,sireGens,damGens}] } */
function wrightF(sireData, damData, maxDepth) {
  maxDepth = maxDepth || 4;
  if (!sireData || !damData) return { F: 0, common: [] };

  const sm = buildAncMap(sireData, 0, maxDepth, new Map());
  const dm = buildAncMap(damData, 0, maxDepth, new Map());

  let F = 0;
  const common = [];
  sm.forEach((sGens, key) => {
    if (!dm.has(key)) return;
    const dGens = dm.get(key);
    let contrib = 0;
    sGens.forEach(L1 => {
      dGens.forEach(L2 => {
        contrib += Math.pow(0.5, L1 + L2 + 1);
      });
    });
    F += contrib;
    common.push({
      key,
      label: ancLabel(key),
      contrib: contrib * 100,
      sireGens: sGens,
      damGens: dGens,
    });
  });
  common.sort((a, b) => b.contrib - a.contrib);
  return { F: Math.round(F * 10000) / 100, common };
}

/* ── Individual animal's own inbreeding ──
   Needs its sire and dam data, both could be linked or manual */
function animalF(a) {
  if (!a) return null;
  // Build a synthetic "sire record" and "dam record" from the animal's own pedigree fields
  const sireRec = a.sireLinkedId
    ? byId(a.sireLinkedId)
    : {
        name: a.sireName || "",
        regId: a.sireId || "",
        sireName: a.ssName || "",
        sireId: a.ssId || "",
        sireLinkedId: "",
        damName: a.sdName || "",
        damId: a.sdId || "",
        damLinkedId: "",
        ssName: "",
        sdName: "",
        dsName: "",
        ddName: "",
      };
  const damRec = a.damLinkedId
    ? byId(a.damLinkedId)
    : {
        name: a.damName || "",
        regId: a.damId || "",
        sireName: a.dsName || "",
        sireId: a.dsId || "",
        sireLinkedId: "",
        damName: a.ddName || "",
        damId: a.ddId || "",
        damLinkedId: "",
        ssName: "",
        sdName: "",
        dsName: "",
        ddName: "",
      };
  // If neither parent has any identifying info, no F is computable.
  const hasSire = !!(
    sireRec &&
    (sireRec.name || sireRec.regId || sireRec.sireName || sireRec.damName)
  );
  const hasDam = !!(damRec && (damRec.name || damRec.regId || damRec.sireName || damRec.damName));
  if (!hasSire && !hasDam) return null;
  return wrightF(sireRec, damRec, 4);
}

/* ── Risk colour ── */
function fColor(f) {
  if (f < 3.125) return { bg: "#dcfce7", color: "#14532d", label: "Very Low" };
  if (f < 6.25) return { bg: "#d1fae5", color: "#065f46", label: "Low" };
  if (f < 12.5) return { bg: "#fef9c3", color: "#713f12", label: "Moderate" };
  if (f < 18.75) return { bg: "#fed7aa", color: "#7c2d12", label: "High" };
  return { bg: "#fee2e2", color: "#7f1d1d", label: "Very High" };
}
function fBadge(f) {
  const c = fColor(f);
  return `<span style="background:${c.bg};color:${c.color};padding:3px 9px;border-radius:12px;font-family:sans-serif;font-size:11px;font-weight:700">${f.toFixed(2)}% — ${c.label}</span>`;
}

/* ── Subject's own inbreeding coefficient at a chosen pedigree depth ──
   Like animalF() but the depth is configurable so the Deep Pedigree page can
   compute F over the full tree the user is viewing (up to 11 generations).
   Returns { F, common } or null when no ancestry is recorded. */
function subjectF(a, maxDepth) {
  if (!a) return null;
  const depth = Math.max(2, Math.min(23, maxDepth || 6));
  const sireRec = a.sireLinkedId
    ? byId(a.sireLinkedId)
    : {
        name: a.sireName || "",
        regId: a.sireId || "",
        sireName: a.ssName || "",
        sireId: a.ssId || "",
        sireLinkedId: "",
        damName: a.sdName || "",
        damId: a.sdId || "",
        damLinkedId: "",
        ssName: "",
        sdName: "",
        dsName: "",
        ddName: "",
      };
  const damRec = a.damLinkedId
    ? byId(a.damLinkedId)
    : {
        name: a.damName || "",
        regId: a.damId || "",
        sireName: a.dsName || "",
        sireId: a.dsId || "",
        sireLinkedId: "",
        damName: a.ddName || "",
        damId: a.ddId || "",
        damLinkedId: "",
        ssName: "",
        sdName: "",
        dsName: "",
        ddName: "",
      };
  const hasSire = !!(
    sireRec &&
    (sireRec.name || sireRec.regId || sireRec.sireName || sireRec.damName)
  );
  const hasDam = !!(damRec && (damRec.name || damRec.regId || damRec.sireName || damRec.damName));
  if (!hasSire && !hasDam) return null;
  return wrightF(sireRec, damRec, depth);
}

/* ── Inbreeding-depression estimates ──
   Published per-1%-inbreeding effects for dairy cattle (US Holstein,
   Smith, Cassell & Pearson, 1998, J. Dairy Sci. 81:2729-2737). These are
   POPULATION-AVERAGE estimates shown for guidance — an individual animal's
   actual performance also depends on its genetic merit and environment.
   Returns an array of { label, perF, total, unit, dir } scaled to this F. */
const INBREEDING_DEPRESSION_PER_1PCT = [
  { key: "milk", label: "305-day Milk yield", perF: -29.6, unit: "kg", dir: "down" },
  { key: "fat", label: "305-day Fat", perF: -1.08, unit: "kg", dir: "down" },
  { key: "protein", label: "305-day Protein", perF: -0.97, unit: "kg", dir: "down" },
  { key: "pl", label: "Productive life", perF: -13.07, unit: "days", dir: "down" },
  { key: "fertility", label: "Days open (longer = worse fertility)", perF: 0.26, unit: "days", dir: "up" },
];

function inbreedingDepression(F) {
  const f = Math.max(0, F || 0);
  return INBREEDING_DEPRESSION_PER_1PCT.map(m => ({
    label: m.label,
    unit: m.unit,
    dir: m.dir,
    perF: m.perF,
    total: Math.round(m.perF * f * 100) / 100,
  }));
}

/* Default depth (generations) used by inbreeding analysis across the app.
   Deeper = more thorough but slower. 6 is a good balance for cattle. */
let IB_DEPTH = 6;

/* ════════════════════════════════════════════════════════════════
   ADVANCED INBREEDING PARAMETERS
   ════════════════════════════════════════════════════════════════ */

/* ── Walk an animal's ancestors, collecting every ancestor node by key ──
   Returns Map<key, {label, gens:Set, count, complete}> plus the set of
   "slots" actually traced. Used for diversity metrics below.
   key is "ID:<id>" for linked records, "NM:<NAME>" for manual entries. */
function collectAncestors(a, maxDepth) {
  const depth = Math.max(1, Math.min(23, maxDepth || IB_DEPTH));
  const map = new Map();
  let tracedSlots = 0; // how many ancestor positions had a known animal

  function norm(n) {
    return n ? n.trim().toUpperCase().replace(/\s+/g, " ") : "";
  }
  function add(key, label, gen) {
    if (!key) return;
    if (!map.has(key)) map.set(key, { label, gens: new Set(), count: 0 });
    const e = map.get(key);
    e.gens.add(gen);
    e.count++;
  }

  // walk(node, gen) where node is a record-like object
  function walk(node, gen) {
    if (!node || gen > depth) return;

    // Sire side
    const sLinked = node.sireLinkedId ? byId(node.sireLinkedId) : null;
    const sKey = node.sireLinkedId
      ? "ID:" + node.sireLinkedId
      : node.sireName
        ? "NM:" + norm(node.sireName)
        : null;
    if (sKey) {
      add(sKey, sLinked ? sLinked.name + (sLinked.regId ? " (" + sLinked.regId + ")" : "") : node.sireName, gen);
      tracedSlots++;
      if (sLinked && gen < depth) walk(sLinked, gen + 1);
      else if (!sLinked && gen + 1 <= depth) {
        if (node.ssName) { add("NM:" + norm(node.ssName), node.ssName, gen + 1); tracedSlots++; }
        if (node.sdName) { add("NM:" + norm(node.sdName), node.sdName, gen + 1); tracedSlots++; }
      }
    }

    // Dam side
    const dLinked = node.damLinkedId ? byId(node.damLinkedId) : null;
    const dKey = node.damLinkedId
      ? "ID:" + node.damLinkedId
      : node.damName
        ? "NM:" + norm(node.damName)
        : null;
    if (dKey) {
      add(dKey, dLinked ? dLinked.name + (dLinked.regId ? " (" + dLinked.regId + ")" : "") : node.damName, gen);
      tracedSlots++;
      if (dLinked && gen < depth) walk(dLinked, gen + 1);
      else if (!dLinked && gen + 1 <= depth) {
        if (node.dsName) { add("NM:" + norm(node.dsName), node.dsName, gen + 1); tracedSlots++; }
        if (node.ddName) { add("NM:" + norm(node.ddName), node.ddName, gen + 1); tracedSlots++; }
      }
    }
  }

  walk(a, 1);
  return { map, tracedSlots };
}

/* ── Pedigree diversity metrics for one animal ──
   Returns:
     distinctAnc  — number of unique ancestors found
     tracedSlots  — total ancestor positions traced (with repeats)
     avk          — Ancestor Loss Coefficient = distinct / traced (1.0 = no loss)
     completeness — traced / theoretically-possible positions (pedigree fill %)
     genEquiv     — complete generation equivalents (sum of 1/2^(gen) over ancestors)
     repeated     — ancestors appearing more than once (inbreeding drivers) */
function pedigreeDiversity(a, maxDepth) {
  const depth = Math.max(1, Math.min(23, maxDepth || IB_DEPTH));
  const { map, tracedSlots } = collectAncestors(a, depth);
  const distinctAnc = map.size;

  // Possible ancestor positions over `depth` generations = 2^1 + ... + 2^depth
  const possible = Math.pow(2, depth + 1) - 2;
  const completeness = possible ? tracedSlots / possible : 0;

  // Complete Generation Equivalents: sum over all ancestors of (1/2)^(gen-1)...
  // standard CGE = sum over known ancestors of (1/2)^g where g is generation number.
  let cge = 0;
  map.forEach(e => {
    e.gens.forEach(g => {
      cge += Math.pow(0.5, g);
    });
  });

  // AVK (Ahnenverlust / Ancestor Loss): distinct unique ancestors divided by
  // the number of ancestor positions traced. 100% = every ancestor distinct.
  const avk = tracedSlots ? distinctAnc / tracedSlots : 0;

  const repeated = [];
  map.forEach((e, key) => {
    if (e.count > 1) repeated.push({ key, label: e.label, count: e.count });
  });
  repeated.sort((x, y) => y.count - x.count);

  return {
    depth,
    distinctAnc,
    tracedSlots,
    possible,
    completeness: Math.round(completeness * 1000) / 10, // %
    avk: Math.round(avk * 1000) / 10, // %
    genEquiv: Math.round(cge * 100) / 100,
    repeated,
  };
}

/* ── Ancestral inbreeding (Ballou's Fa, approximate) ──
   Fraction of an animal's genome that has been exposed to inbreeding at least
   once in its ancestral path. We approximate it as the share of repeated
   ancestors weighted by their pathways — a practical proxy, clearly labelled
   as an estimate. Returns a percentage. */
function ancestralInbreeding(a, maxDepth) {
  const depth = Math.max(1, Math.min(23, maxDepth || IB_DEPTH));
  const own = subjectF(a, depth);
  if (!own) return null;
  // Approximate Fa from the proportion of inbreeding pathways that pass
  // through ancestors who are themselves inbred. Use the animal's sire/dam F.
  const sireRec = a.sireLinkedId ? byId(a.sireLinkedId) : null;
  const damRec = a.damLinkedId ? byId(a.damLinkedId) : null;
  const sF = sireRec ? (subjectF(sireRec, depth) || { F: 0 }).F : 0;
  const dF = damRec ? (subjectF(damRec, depth) || { F: 0 }).F : 0;
  // Ballou recursion (simplified): Fa ≈ 0.5*(Fa_sire + F_sire) + 0.5*(Fa_dam + F_dam)
  // We don't recurse Fa fully (cost); use parents' F as the ancestral signal.
  const faApprox = 0.5 * sF + 0.5 * dF;
  return Math.round(faApprox * 100) / 100;
}

/* ── Herd-wide founder & common-ancestor analysis ──
   Founders = animals (or named ancestors) that appear in pedigrees but have
   no recorded parents themselves. Top ancestors = those appearing across the
   most distinct animals' pedigrees (biggest inbreeding-risk drivers). */
function herdFounderAnalysis(maxDepth) {
  const depth = Math.max(1, Math.min(23, maxDepth || IB_DEPTH));
  const ancestorInPedigrees = new Map(); // key -> {label, animals:Set}

  animals.forEach(animal => {
    const { map } = collectAncestors(animal, depth);
    map.forEach((e, key) => {
      if (!ancestorInPedigrees.has(key))
        ancestorInPedigrees.set(key, { label: e.label, animals: new Set() });
      ancestorInPedigrees.get(key).animals.add(animal.id);
    });
  });

  // Top recurring ancestors across the herd
  const top = [];
  ancestorInPedigrees.forEach((e, key) => {
    top.push({ key, label: e.label, descendants: e.animals.size });
  });
  top.sort((a, b) => b.descendants - a.descendants);

  // Founders: linked animals with no recorded sire AND no recorded dam.
  const founders = animals
    .filter(a => {
      const noSire = !a.sireLinkedId && !a.sireName;
      const noDam = !a.damLinkedId && !a.damName;
      return noSire && noDam;
    })
    .map(a => ({ id: a.id, name: a.name, regId: a.regId, breed: a.breed }));

  return { top: top.slice(0, 15), founders, totalAncestors: ancestorInPedigrees.size };
}

/* ── Herd inbreeding trend by birth year ──
   Groups animals by birth year and averages each year's F so the breeder can
   see whether the herd is trending more or less inbred over time. */
function herdInbreedingTrend() {
  const byYear = {};
  animals.forEach(a => {
    const res = animalF(a);
    if (!res || res.F <= 0) return;
    const y = (a.dob || "").slice(0, 4);
    if (!/^\d{4}$/.test(y)) return;
    if (!byYear[y]) byYear[y] = { sum: 0, n: 0 };
    byYear[y].sum += res.F;
    byYear[y].n++;
  });
  return Object.keys(byYear)
    .sort()
    .map(y => ({ year: y, avgF: byYear[y].sum / byYear[y].n, n: byYear[y].n }));
}

/* ═════════════════════════════════════════════════════════════════
   BREEDING PAGE RENDERER
   ═════════════════════════════════════════════════════════════════ */
let bpState = { cowId: "", sireId: "", manualSire: null, result: null };

function renderBreedPage() {
  const el = document.getElementById("pg-breed");
  if (!el) return;

  const cows = animals.filter(a => a.sex === "Female");
  const fVals = cows.map(a => ({ a, res: animalF(a) })).filter(x => x.res && x.res.F > 0);
  const avgF = fVals.length ? fVals.reduce((s, x) => s + x.res.F, 0) / fVals.length : null;

  el.innerHTML = `
  <!-- HEADER BANNER -->
  <div style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);border-radius:var(--rl);padding:14px 18px;color:#fff;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px;-webkit-print-color-adjust:exact;print-color-adjust:exact">
    <div style="min-width:0">
      <div style="font-size:clamp(13px,2vw,17px);font-weight:bold">🧬 Breeding &amp; Inbreeding Analysis</div>
      <div style="font-size:clamp(9px,1.1vw,11px);opacity:.8;margin-top:3px">Wright's Path Coefficient Method · Up to 4-generation depth</div>
    </div>
    <div style="flex-shrink:0;display:flex;gap:8px;align-items:center;flex-wrap:wrap">
      <button class="btn bw bsm" onclick="showInbreedingExample()" title="A simple visual walkthrough for farmers">📚 How is it calculated?</button>
      ${avgF !== null ? fBadge(avgF).replace("— ", "Herd avg — ") : ""}
    </div>
  </div>

  <!-- ══ SECTION 1: PRE-INSEMINATION PREDICTOR ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('bp_sec1')">
      <h2>💉 Pre-Insemination Inbreeding Predictor</h2>
      <span id="bp_sec1_ico" style="font-size:18px;color:var(--gm)">▲</span>
    </div>
    <div id="bp_sec1">
      <div style="padding:14px;border-bottom:1px solid var(--bdr);background:#fafffe">
        <p style="font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);color:var(--tm);margin-bottom:14px">
          Select a cow and a sire to calculate the <b>expected inbreeding % of their offspring</b> before insemination.
          Helps choose sires that minimise inbreeding and maximise outcrossing.
        </p>

        <!-- ROW 1: Cow search -->
        <div style="margin-bottom:12px">
          <label style="font-size:clamp(10px,1vw,11px);font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--tm);display:block;margin-bottom:4px">🐄 Select Cow (search by name, ID, or ear tag)</label>
          <div class="ppick" style="position:relative">
            <div style="display:flex;gap:6px">
              <input id="bp_cowQ" placeholder="Type cow name, registration ID, or ear tag number…"
                style="flex:1;font-size:clamp(12px,1.2vw,14px);padding:9px 12px"
                oninput="bpCowSearch()" onfocus="bpCowSearch()" autocomplete="off">
              <button class="btn bd" style="flex-shrink:0;font-size:clamp(11px,1vw,12px)" onclick="bpCowClear()">✕ Clear</button>
            </div>
            <div id="bp_cowDD" style="display:none;position:absolute;top:100%;left:0;right:0;background:#fff;border:1px solid var(--gm);border-radius:var(--r);box-shadow:var(--shl);z-index:300;max-height:220px;overflow-y:auto"></div>
            <div id="bp_cowSel" style="display:none;background:var(--gp);border:1px solid var(--gm);border-radius:var(--r);padding:6px 10px;margin-top:4px;font-family:sans-serif;font-size:clamp(11px,1.1vw,13px);align-items:center;gap:8px;justify-content:space-between;flex-wrap:wrap"></div>
          </div>
        </div>

        <!-- ROW 2: Sire search -->
        <div style="margin-bottom:12px">
          <label style="font-size:clamp(10px,1vw,11px);font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--tm);display:block;margin-bottom:4px">🐂 Select Sire from Register (search by name or ID)</label>
          <div class="ppick" style="position:relative">
            <div style="display:flex;gap:6px">
              <input id="bp_sireQ" placeholder="Type sire name or registration ID…"
                style="flex:1;font-size:clamp(12px,1.2vw,14px);padding:9px 12px"
                oninput="bpSireSearch()" onfocus="bpSireSearch()" autocomplete="off">
              <button class="btn bd" style="flex-shrink:0;font-size:clamp(11px,1vw,12px)" onclick="bpSireClear()">✕ Clear</button>
            </div>
            <div id="bp_sireDD" style="display:none;position:absolute;top:100%;left:0;right:0;background:#fff;border:1px solid var(--gm);border-radius:var(--r);box-shadow:var(--shl);z-index:300;max-height:220px;overflow-y:auto"></div>
            <div id="bp_sireSel" style="display:none;background:var(--gp);border:1px solid var(--gm);border-radius:var(--r);padding:6px 10px;margin-top:4px;font-family:sans-serif;font-size:clamp(11px,1.1vw,13px);align-items:center;gap:8px;justify-content:space-between;flex-wrap:wrap"></div>
          </div>
        </div>

        <!-- ROW 3: External sire -->
        <div style="margin-bottom:12px">
          <label style="font-size:clamp(10px,1vw,11px);font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--tm);display:block;margin-bottom:4px">— or — Enter External / AI Sire</label>
          <input id="bp_extSire" placeholder="AI straw code or sire name (not in register)"
            style="width:100%;font-size:clamp(12px,1.2vw,14px);padding:9px 12px"
            oninput="bpManualSireChange()" value="${bpState.manualSire ? bpState.manualSire.name : ""}">
        </div>

        <!-- Manual sire pedigree -->
        <div id="bp_manualPed" style="display:${!bpState.sireId && bpState.manualSire ? "block" : "none"};background:#f0fdf4;border:1px solid #a7f3d0;border-radius:var(--r);padding:12px;margin-bottom:12px">
          <div style="font-family:sans-serif;font-size:clamp(11px,1.1vw,12px);font-weight:700;color:var(--gd);margin-bottom:9px">🐂 External Sire Pedigree (increases calculation accuracy)</div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:9px">
            <div class="fr"><label>Sire's Sire (PGS) Name</label><input id="bp_pgs" placeholder="e.g. SANDY-VALLEY CHROME" oninput="bpManualPedChange()"></div>
            <div class="fr"><label>PGS ID</label><input id="bp_pgsid" placeholder="e.g. HOL-US-65963857" oninput="bpManualPedChange()"></div>
            <div class="fr"><label>Sire's Dam (PGD) Name</label><input id="bp_pgd" placeholder="e.g. WESTCOAST ALICIA" oninput="bpManualPedChange()"></div>
            <div class="fr"><label>PGD ID</label><input id="bp_pgdid" placeholder="e.g. HOL-CA-106661" oninput="bpManualPedChange()"></div>
          </div>
        </div>
      </div>

      <!-- RESULT PANEL -->
      <div id="bp_result" style="padding:14px">${renderPredictorResult()}</div>
    </div>
  </div>

  <!-- ══ SECTION 2: SIRE RANKING ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('bp_sec2')">
      <h2>🏆 Sire Ranking — Lowest Inbreeding First</h2>
      <span id="bp_sec2_ico" style="font-size:18px;color:var(--gm)">▲</span>
    </div>
    <div id="bp_sec2">
      <div style="padding:14px">
        <p style="font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);color:var(--tm);margin-bottom:12px">All registered sires ranked by expected offspring inbreeding for the selected cow. ✅ = best outcross.</p>
        <div id="bp_ranking">${renderSireRanking()}</div>
      </div>
    </div>
  </div>

  <!-- ══ SECTION 3: HERD INBREEDING OVERVIEW ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('bp_sec3')">
      <h2>📊 Herd Inbreeding Overview</h2>
      <span id="bp_sec3_ico" style="font-size:18px;color:var(--gm)">▲</span>
    </div>
    <div id="bp_sec3">
      <div style="padding:14px">${renderHerdInbreedingTable()}</div>
    </div>
  </div>


  <!-- ══ SECTION: CROSSBREEDING PROGRAM SUMMARY ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer;background:linear-gradient(135deg,#065f46,#16a34a);-webkit-print-color-adjust:exact;print-color-adjust:exact" onclick="bpToggle('bp_secX')">
      <h2 style="color:#fff">🔀 Crossbreeding Program — Generation &amp; Purity Tracker</h2>
      <span id="bp_secX_ico" style="font-size:18px;color:#fff">▲</span>
    </div>
    <div id="bp_secX">
      <div style="padding:14px">${renderCrossbreedSection()}</div>
    </div>
  </div>

  <!-- ══ SECTION 4: BREED AVERAGES ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('bp_sec4')">
      <h2>🌍 Breed Population Inbreeding Rates</h2>
      <div style="display:flex;gap:7px;align-items:center">
        <button class="btn bg bsm" onclick="event.stopPropagation();showAddBreedAvg()" style="font-size:clamp(11px,1vw,12px)">＋ Add / Edit</button>
        <span id="bp_sec4_ico" style="font-size:18px;color:var(--gm)">▲</span>
      </div>
    </div>
    <div id="bp_sec4">
      <div style="padding:14px">
        <p style="font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);color:var(--tl);margin-bottom:12px">
          Compare your herd against global breed inbreeding averages. Data is editable.
        </p>
        <div id="bp_breedTable">${renderBreedAvgTable()}</div>
        <div id="bp_breedForm" style="display:none;margin-top:14px;background:var(--gp);border:1px solid var(--bdr);border-radius:var(--r);padding:14px">
          <div style="font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);font-weight:700;color:var(--gd);margin-bottom:10px">Add / Edit Breed Average</div>
          <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:9px">
            <div class="fr"><label>Breed Name</label><input id="ba_breed" placeholder="e.g. Holstein (USA)"></div>
            <div class="fr"><label>Avg Inbreeding %</label><input type="number" step="0.01" id="ba_avg" placeholder="e.g. 9.99"></div>
            <div class="fr"><label>Trend (optional)</label><input id="ba_trend" placeholder="e.g. +0.20% / year"></div>
            <div class="fr"><label>Source / Year</label><input id="ba_source" placeholder="e.g. CDCB 2024"></div>
          </div>
          <div style="display:flex;gap:7px;margin-top:10px;flex-wrap:wrap">
            <button class="btn bg" style="font-size:clamp(11px,1vw,13px)" onclick="saveBreedAvgEntry()">💾 Save</button>
            <button class="btn bd" style="font-size:clamp(11px,1vw,13px)" onclick="document.getElementById('bp_breedForm').style.display='none'">Cancel</button>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- ══ SECTION 5: METHODOLOGY ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('bp_sec5')">
      <h2>📖 Methodology &amp; Risk Levels</h2>
      <span id="bp_sec5_ico" style="font-size:18px;color:var(--gm)">▼</span>
    </div>
    <div id="bp_sec5" style="display:none">
      <div style="padding:14px;font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);color:var(--tm);line-height:1.7">
        <p><b>Wright's Path Coefficient Formula:</b> F(I) = Σ [(½)<sup>L₁+L₂+1</sup> × (1 + F<sub>A</sub>)]</p>
        <p style="margin-top:6px">Where <b>L₁</b> = steps from sire to common ancestor, <b>L₂</b> = steps from dam to common ancestor.</p>
        <div style="display:flex;flex-wrap:wrap;gap:8px;margin-top:12px">
          ${[
            ["< 3.1%", "Very Low", "#dcfce7", "#14532d", "3rd cousin"],
            ["3.1–6.25%", "Low", "#d1fae5", "#065f46", "2nd cousin"],
            ["6.25–12.5%", "Moderate", "#fef9c3", "#713f12", "1st cousin"],
            ["12.5–25%", "High", "#fed7aa", "#7c2d12", "Half-sibling"],
            [">25%", "Very High", "#fee2e2", "#7f1d1d", "Full-sibling"],
          ]
            .map(
              ([
                r,
                l,
                bg,
                c,
                ex,
              ]) => `<div style="background:${bg};border-radius:6px;padding:8px 12px;border-left:3px solid ${c};min-width:120px;flex:1">
              <div style="font-weight:700;color:${c};font-size:clamp(11px,1.2vw,13px)">${r} — ${l}</div>
              <div style="font-size:clamp(9px,1vw,11px);color:${c};opacity:.8">${ex} equivalent</div>
            </div>`
            )
            .join("")}
        </div>
        <p style="margin-top:10px;font-size:clamp(9px,1vw,11px);color:var(--tl)">
          Reference: Wright S. (1922) Am.Nat. 56:330–338. Van Raden P.M. (1992) J.Dairy Sci. 75:3136–3144.
        </p>
      </div>
    </div>
  </div>
  `;

  // Restore search display after re-render
  setTimeout(() => {
    if (bpState.cowId) {
      const a = byId(bpState.cowId);
      if (a) bpShowCowSel(a);
    }
    if (bpState.sireId) {
      const a = byId(bpState.sireId);
      if (a) bpShowSireSel(a);
    }
    if (bpState.manualSire) {
      const ei = document.getElementById("bp_extSire");
      if (ei) ei.value = bpState.manualSire.name || "";
      const mp = document.getElementById("bp_manualPed");
      if (mp) mp.style.display = "block";
    }
  }, 0);

  // Close dropdowns on outside click
  document.addEventListener("click", function bpOutside(e) {
    const dd1 = document.getElementById("bp_cowDD");
    const dd2 = document.getElementById("bp_sireDD");
    if (dd1 && !e.target.closest("#bp_cowDD") && e.target.id !== "bp_cowQ")
      dd1.style.display = "none";
    if (dd2 && !e.target.closest("#bp_sireDD") && e.target.id !== "bp_sireQ")
      dd2.style.display = "none";
  });
}

/* Collapsible sections */
function bpToggle(id) {
  const sec = document.getElementById(id);
  const ico = document.getElementById(id + "_ico");
  if (!sec) return;
  const open = sec.style.display !== "none";
  sec.style.display = open ? "none" : "block";
  if (ico) ico.textContent = open ? "▼" : "▲";
}

/* ── Searchable Cow Picker ── */
function bpCowSearch() {
  const inp = document.getElementById("bp_cowQ");
  const dd = document.getElementById("bp_cowDD");
  if (!inp || !dd) return;
  const q = inp.value.toLowerCase().trim();
  const cows = animals
    .filter(a => a.sex === "Female")
    .filter(
      a =>
        !q ||
        (a.name || "").toLowerCase().includes(q) ||
        (a.regId || "").toLowerCase().includes(q) ||
        (a.color || "").toLowerCase().includes(q) ||
        (a.dob || "").includes(q)
    );
  if (!cows.length) {
    dd.innerHTML =
      '<div style="padding:9px 12px;font-family:sans-serif;font-size:12px;color:var(--tl);font-style:italic">No cows found matching "' +
      inp.value +
      '"</div>';
    dd.style.display = "block";
    return;
  }
  dd.innerHTML =
    cows
      .slice(0, 20)
      .map(
        a => `
    <div style="padding:9px 12px;cursor:pointer;font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);border-bottom:1px solid #f0f0f0;display:flex;align-items:center;gap:8px"
      onmouseover="this.style.background='var(--gp)'" onmouseout="this.style.background='#fff'"
      onclick="bpSelectCow('${a.id}')">
      <div style="width:28px;height:28px;border-radius:50%;background:var(--hbg);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0">${a.photo ? `<img src="${a.photo}" style="width:100%;height:100%;object-fit:cover;border-radius:50%">` : "🐄"}</div>
      <div style="min-width:0">
        <div style="font-weight:bold;color:var(--td)">${a.name}</div>
        <div style="font-size:clamp(9px,1vw,11px);color:var(--tl)">${a.regId || "—"} · Born ${fmtD(a.dob)} · ${a.breed} · Lact.${a.lact || "—"}</div>
        ${a.milk ? `<div style="font-size:clamp(9px,1vw,11px);color:#7a3aad">🥛${fmtN(a.milk)}kg · F${a.fat || "—"}% · P${a.prot || "—"}%</div>` : ""}
      </div>
    </div>`
      )
      .join("") +
    (cows.length > 20
      ? `<div style="padding:7px 12px;font-family:sans-serif;font-size:11px;color:var(--tl);background:#f9f9f9">${cows.length - 20} more — type to narrow search</div>`
      : "");
  dd.style.display = "block";
}
function bpSelectCow(id) {
  const a = byId(id);
  if (!a) return;
  bpState.cowId = id;
  const inp = document.getElementById("bp_cowQ");
  const dd = document.getElementById("bp_cowDD");
  if (inp) inp.value = a.name + " [" + a.regId + "]";
  if (dd) dd.style.display = "none";
  bpShowCowSel(a);
  bpUpdateResults();
}
function bpShowCowSel(a) {
  const sel = document.getElementById("bp_cowSel");
  if (!sel) return;
  const res = animalF(a);
  sel.innerHTML = `<div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap">
    <div style="width:30px;height:30px;border-radius:50%;overflow:hidden;background:var(--hbg);display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0">${a.photo ? `<img src="${a.photo}" style="width:100%;height:100%;object-fit:cover">` : "🐄"}</div>
    <div><b style="font-size:clamp(12px,1.2vw,14px)">${a.name}</b> <span style="font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:var(--tl)">${a.regId || "—"} · ${a.breed} · Lact.${a.lact || "—"}</span></div>
    ${res && res.F > 0 ? `<span>${fBadge(res.F)} <span style="font-family:sans-serif;font-size:10px;color:var(--tl)">own F</span></span>` : ""}
  </div>`;
  sel.style.display = "flex";
}
function bpCowClear() {
  bpState.cowId = "";
  const inp = document.getElementById("bp_cowQ");
  if (inp) inp.value = "";
  const dd = document.getElementById("bp_cowDD");
  if (dd) dd.style.display = "none";
  const sel = document.getElementById("bp_cowSel");
  if (sel) sel.style.display = "none";
  bpUpdateResults();
}

/* ── Searchable Sire Picker ── */
function bpSireSearch() {
  const inp = document.getElementById("bp_sireQ");
  const dd = document.getElementById("bp_sireDD");
  if (!inp || !dd) return;
  const q = inp.value.toLowerCase().trim();
  const sires = animals
    .filter(a => a.sex === "Male")
    .filter(
      a =>
        !q || (a.name || "").toLowerCase().includes(q) || (a.regId || "").toLowerCase().includes(q)
    );
  if (!sires.length) {
    dd.innerHTML =
      '<div style="padding:9px 12px;font-family:sans-serif;font-size:12px;color:var(--tl);font-style:italic">No sires found</div>';
    dd.style.display = "block";
    return;
  }
  dd.innerHTML = sires
    .slice(0, 15)
    .map(a => {
      const off = offspring(a.id);
      return `<div style="padding:9px 12px;cursor:pointer;font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);border-bottom:1px solid #f0f0f0;display:flex;align-items:center;gap:8px"
      onmouseover="this.style.background='var(--gp)'" onmouseout="this.style.background='#fff'"
      onclick="bpSelectSire('${a.id}')">
      <div style="width:28px;height:28px;border-radius:50%;background:var(--hbg);display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0">${a.photo ? `<img src="${a.photo}" style="width:100%;height:100%;object-fit:cover;border-radius:50%">` : "🐂"}</div>
      <div style="min-width:0">
        <div style="font-weight:bold;color:var(--td)">${a.name}</div>
        <div style="font-size:clamp(9px,1vw,11px);color:var(--tl)">${a.regId || "—"} · ${a.breed} · ${off.length} offspring</div>
      </div>
    </div>`;
    })
    .join("");
  dd.style.display = "block";
}
function bpSelectSire(id) {
  const a = byId(id);
  if (!a) return;
  bpState.sireId = id;
  bpState.manualSire = null;
  const inp = document.getElementById("bp_sireQ");
  if (inp) inp.value = a.name + " [" + a.regId + "]";
  const dd = document.getElementById("bp_sireDD");
  if (dd) dd.style.display = "none";
  const ei = document.getElementById("bp_extSire");
  if (ei) ei.value = "";
  const mp = document.getElementById("bp_manualPed");
  if (mp) mp.style.display = "none";
  bpShowSireSel(a);
  bpUpdateResults();
}
function bpShowSireSel(a) {
  const sel = document.getElementById("bp_sireSel");
  if (!sel) return;
  sel.innerHTML = `<div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap">
    <div style="width:30px;height:30px;border-radius:50%;overflow:hidden;background:var(--hbg);display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0">${a.photo ? `<img src="${a.photo}" style="width:100%;height:100%;object-fit:cover">` : "🐂"}</div>
    <div><b style="font-size:clamp(12px,1.2vw,14px)">${a.name}</b> <span style="font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:var(--tl)">${a.regId || "—"} · ${a.breed} · ${offspring(a.id).length} offspring</span></div>
    <span style="font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:var(--gm)">🔗 Linked from register</span>
  </div>`;
  sel.style.display = "flex";
}
function bpSireClear() {
  bpState.sireId = "";
  const inp = document.getElementById("bp_sireQ");
  if (inp) inp.value = "";
  const dd = document.getElementById("bp_sireDD");
  if (dd) dd.style.display = "none";
  const sel = document.getElementById("bp_sireSel");
  if (sel) sel.style.display = "none";
  bpUpdateResults();
}

/* ── Central update ── */
function bpUpdateResults() {
  const res = document.getElementById("bp_result");
  if (res) res.innerHTML = renderPredictorResult();
  const rank = document.getElementById("bp_ranking");
  if (rank) rank.innerHTML = renderSireRanking();
}

function renderPredictorResult() {
  const cowId = bpState.cowId;
  const sireId = bpState.sireId;

  const cow = byId(cowId);
  const sire = sireId ? byId(sireId) : bpState.manualSire;
  if (!cow || !sire)
    return `<div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:16px;text-align:center;font-family:sans-serif;font-size:12px;color:var(--tl)">👆 Select a cow and sire above to calculate expected offspring inbreeding.</div>`;

  const res = wrightF(sire, cow, IB_DEPTH);
  const fc = fColor(res.F);
  const cowF = animalF(cow);

  // Breed avg comparison
  const breedKey = Object.keys(BREED_AVG).find(k =>
    k.toLowerCase().includes(cow.breed.toLowerCase())
  );
  const breedAvg = breedKey ? BREED_AVG[breedKey].avg : null;

  return `
  ${defectWarningHTML(sire, cow)}
  <div style="border:2px solid ${fc.color}33;border-radius:var(--rl);overflow:hidden">
    <div style="background:${fc.bg};padding:clamp(8px,2vw,14px) clamp(10px,2vw,18px);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px">
      <div>
        <div style="font-size:11px;font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:${fc.color};opacity:.7">Expected Offspring Inbreeding</div>
        <div style="font-size:clamp(22px,4vw,36px);font-weight:bold;color:${fc.color};line-height:1.1">${res.F.toFixed(2)}%</div>
        <div style="font-family:sans-serif;font-size:12px;color:${fc.color};margin-top:3px">${fc.label} · ${sire.name || sire.regId || "External sire"} × ${cow.name}</div>
      </div>
      <div style="text-align:right;font-family:sans-serif;font-size:11px">
        <div style="color:var(--tl)">Cow's own inbreeding: <b style="color:var(--td)">${cowF && cowF.F > 0 ? cowF.F.toFixed(2) + "%" : "not calculable"}</b></div>
        ${breedAvg !== null ? `<div style="color:var(--tl);margin-top:3px">${breedKey} avg: <b style="color:var(--td)">${breedAvg}%</b> &nbsp;<b style="color:${res.F > breedAvg ? "#c0392b" : "#16a34a"}">${res.F > breedAvg ? "▲ above" : "▼ below"} average</b></div>` : ""}
      </div>
    </div>
    ${
      res.common.length
        ? `
    <div style="padding:12px 18px;background:#fff">
      <div style="font-family:sans-serif;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:8px">
        Common Ancestors Found (${res.common.length}) — causing inbreeding
      </div>
      <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
        <thead><tr style="background:var(--gp)">
          <th style="text-align:left;padding:5px 8px;font-size:10px">Ancestor</th>
          <th style="text-align:center;padding:5px 8px;font-size:10px">Sire side<br>generation</th>
          <th style="text-align:center;padding:5px 8px;font-size:10px">Dam side<br>generation</th>
          <th style="text-align:right;padding:5px 8px;font-size:10px">F contribution</th>
        </tr></thead>
        <tbody>
          ${res.common
            .map(
              (
                c,
                i
              ) => `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
            <td style="padding:5px 8px;font-weight:bold">${c.label}</td>
            <td style="text-align:center;padding:5px 8px">${c.sireGens.map(g => "Gen " + g).join(", ")}</td>
            <td style="text-align:center;padding:5px 8px">${c.damGens.map(g => "Gen " + g).join(", ")}</td>
            <td style="text-align:right;padding:5px 8px;font-weight:bold;color:${fColor(c.contrib).color}">+${c.contrib.toFixed(3)}%</td>
          </tr>`
            )
            .join("")}
          <tr style="border-top:2px solid var(--bdr);background:${fc.bg}">
            <td colspan="3" style="padding:6px 8px;font-weight:700;font-family:sans-serif">Total expected inbreeding (F)</td>
            <td style="text-align:right;padding:6px 8px;font-weight:bold;font-size:14px;color:${fc.color}">${res.F.toFixed(2)}%</td>
          </tr>
        </tbody>
      </table>
    </div>`
        : `
    <div style="padding:14px 18px;background:#fff;font-family:sans-serif;font-size:12px;color:#16a34a">
      ✅ No common ancestors found in available pedigree records — this cross has <b>zero detectable inbreeding</b>. Excellent outcrossing candidate.
    </div>`
    }
    <div style="padding:8px 18px;background:#f9f9f9;border-top:1px solid var(--bdr);font-family:sans-serif;font-size:10px;color:var(--tl)">
      Calculated using ${res.common.length} common ancestor(s) found across both pedigrees up to generation ${IB_DEPTH}.
      ${res.F > 6.25 ? `<b style="color:#c0392b"> ⚠ Consider choosing a less related sire to reduce inbreeding.</b>` : `<b style="color:#16a34a"> ✅ Inbreeding level is within acceptable limits.</b>`}
    </div>
  </div>`;
}

/* ── Sire ranking for selected cow ── */
function renderSireRanking() {
  const cowId = bpState.cowId;
  const cow = byId(cowId);
  const sires = animals.filter(a => a.sex === "Male");
  if (!cow || !sires.length) return "";

  const ranked = sires
    .map(s => {
      const res = wrightF(s, cow, IB_DEPTH);
      return { s, res };
    })
    .sort((a, b) => a.res.F - b.res.F);

  return `
  <div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
    <div style="padding:10px 15px;background:var(--gp);border-bottom:1px solid var(--bdr)">
      <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">
        🏆 Sire Ranking for ${cow.name} — by expected offspring inbreeding (lowest = best outcross)
      </div>
    </div>
    <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
      <thead><tr style="background:#f9f9f9">
        <th style="text-align:left;padding:7px 10px;font-size:10px">Rank</th>
        <th style="text-align:left;padding:7px 10px;font-size:10px">Sire</th>
        <th style="text-align:center;padding:7px 10px;font-size:10px">Expected F%</th>
        <th style="text-align:center;padding:7px 10px;font-size:10px">Common Ancestors</th>
        <th style="text-align:center;padding:7px 10px;font-size:10px">Recommendation</th>
        <th style="text-align:center;padding:7px 10px;font-size:10px">Action</th>
      </tr></thead>
      <tbody>
      ${ranked
        .map((r, i) => {
          const fc = fColor(r.res.F);
          const medal = i === 0 ? "🥇 " : i === 1 ? "🥈 " : i === 2 ? "🥉 " : "";
          const rec =
            r.res.F < 3.125
              ? "✅ Best outcross"
              : r.res.F < 6.25
                ? "✅ Good choice"
                : r.res.F < 12.5
                  ? "⚠️ Acceptable"
                  : r.res.F < 18.75
                    ? "❌ Avoid if possible"
                    : "🚫 Do not use";
          const defRisk = matingDefectRisk(r.s, cow).filter(x => x.level !== "caution");
          const defFlag = defRisk.length
            ? `<div style="color:#991b1b;font-size:10px;font-weight:700;margin-top:3px">⛔ ${defRisk.map(d => d.code).join(", ")} defect risk</div>`
            : "";
          return `<tr style="border-top:1px solid var(--bdr);background:${defRisk.length ? "#fef2f2" : i === 0 ? fc.bg : "#fff"}">
          <td style="padding:7px 10px;font-weight:bold;color:${fc.color}">${medal}${i + 1}</td>
          <td style="padding:7px 10px"><b>${r.s.name}</b><br><span style="color:var(--tl);font-size:10px">${r.s.regId || "—"} · ${r.s.breed}</span></td>
          <td style="text-align:center;padding:7px 10px">${fBadge(r.res.F)}</td>
          <td style="text-align:center;padding:7px 10px">${
            r.res.common.length > 0
              ? r.res.common
                  .map(c => c.label)
                  .slice(0, 2)
                  .join(", ") +
                (r.res.common.length > 2 ? " +" + (r.res.common.length - 2) + " more" : "")
              : "None found"
          }</td>
          <td style="text-align:center;padding:7px 10px;font-weight:600;color:${fc.color}">${rec}${defFlag}</td>
          <td style="text-align:center;padding:7px 10px">
            <button class="btn bg bsm" onclick="bpSelectSire('${r.s.id}');document.getElementById('bp_sireQ').value='${r.s.name} [${r.s.regId}]'">Select</button>
          </td>
        </tr>`;
        })
        .join("")}
      </tbody>
    </table>
  </div>`;
}

/* ── Herd inbreeding table ── */
/* DB mode: build the herd inbreeding overview from server endpoints. */
async function loadHerdReportDB() {
  const host = document.getElementById("herdReportDB");
  if (!host) return;
  try {
    const [rep, founders, top] = await Promise.all([
      window.LpDB.herdReport(1, 50),
      window.LpDB.founders(1, 30).catch(() => ({ rows: [], total: 0 })),
      window.LpDB.topAncestors(10).catch(() => []),
    ]);
    const stat = (label, val, col) =>
      `<div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:10px 12px">
        <div style="font-size:9px;font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:3px">${label}</div>
        <div style="font-size:20px;font-weight:bold;color:${col}">${val}</div></div>`;
    const stats = `<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:14px">
      ${stat("Herd Average F", rep.avg_f != null ? rep.avg_f + "%" : "N/A", "var(--gd)")}
      ${stat("Highest F", rep.max_f != null ? Number(rep.max_f).toFixed(2) + "%" : "N/A", "#c0392b")}
      ${stat("Animals with F", (rep.count || 0).toLocaleString(), "var(--gd)")}
      ${stat("Founders", (founders.total || 0).toLocaleString(), "#16a34a")}
    </div>`;
    const note = !rep.count
      ? `<div style="font-family:sans-serif;font-size:11px;color:var(--tl);background:#fffbeb;border:1px solid #fde68a;border-radius:var(--r);padding:9px 12px;margin-bottom:12px">
          No precomputed inbreeding values yet. Open animals in <b>Deep Pedigree</b> to compute &amp; store their F, or run a batch recompute. The report sorts instantly once values are stored.</div>`
      : "";
    const repRows = (rep.rows || [])
      .map((a, i) => {
        const f = a.inbreeding_f != null ? Number(a.inbreeding_f) : (a.F != null ? a.F : null);
        const fc = f != null ? fColor(f) : { bg: "#f9f9f9", color: "#aaa", label: "—" };
        return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
          <td style="padding:6px 9px;font-weight:bold"><span style="cursor:pointer;color:var(--gm)" onclick="selAnimal('${a.id}')">${dpEscape(a.name || "—")}</span><div style="font-size:9px;color:var(--tl)">${dpEscape(a.regId || "—")}</div></td>
          <td style="text-align:center;padding:6px 9px">${a.breed === "Holstein" ? "HO" : a.breed === "Montbeliard" ? "MO" : (a.breed || "")}</td>
          <td style="text-align:center;padding:6px 9px">${f != null ? fBadge(f) : "—"}</td>
        </tr>`;
      })
      .join("");
    const topRows = (top || [])
      .map((t, i) => `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:5px 9px">${i + 1}</td><td style="padding:5px 9px;font-weight:bold">${dpEscape(t.name || "—")}</td>
        <td style="text-align:center;padding:5px 9px;font-weight:700;color:${t.descendants >= 4 ? "#c0392b" : "var(--gd)"}">${t.descendants}</td></tr>`)
      .join("");
    host.innerHTML = `${stats}${note}
      <div style="overflow-x:auto;margin-bottom:16px">
        <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
          <thead><tr style="background:var(--gp)"><th style="text-align:left;padding:6px 9px">Animal (most inbred first)</th><th style="padding:6px 9px">Breed</th><th style="padding:6px 9px">Inbreeding F%</th></tr></thead>
          <tbody>${repRows || '<tr><td colspan="3" style="padding:12px;text-align:center;color:var(--tl)">—</td></tr>'}</tbody>
        </table>
      </div>
      <div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
        <div style="padding:9px 14px;background:var(--gp);font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">🧬 Most Influential Ancestors (by offspring count)</div>
        <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:11px">
          <thead><tr style="background:#f9f9f9"><th style="text-align:left;padding:5px 9px">#</th><th style="text-align:left;padding:5px 9px">Ancestor</th><th style="text-align:center;padding:5px 9px">Offspring</th></tr></thead>
          <tbody>${topRows || '<tr><td colspan="3" style="padding:12px;text-align:center;color:var(--tl)">—</td></tr>'}</tbody>
        </table>
      </div>`;
  } catch (err) {
    host.innerHTML = '<div style="padding:12px;color:#c0392b;font-size:11px">Could not load herd report: ' + (err.message || err) + "</div>";
  }
}

function renderHerdInbreedingTable() {
  // DB mode: render from the server (precomputed F report + founders + top
  // ancestors) instead of iterating the whole 3M-row herd in the browser.
  if (DB_MODE) {
    setTimeout(loadHerdReportDB, 0);
    return `<div id="herdReportDB"><div style="padding:14px;color:var(--tl);font-family:sans-serif;font-size:12px">Loading herd report…</div></div>`;
  }
  const allAnim = animals;
  if (!allAnim.length)
    return `<div style="font-family:sans-serif;font-size:12px;color:var(--tl)">No animals registered yet.</div>`;

  const rows = allAnim.map(a => {
    const res = animalF(a);
    const breedKey = Object.keys(BREED_AVG).find(k =>
      k.toLowerCase().includes(a.breed.toLowerCase())
    );
    const bAvg = breedKey ? BREED_AVG[breedKey].avg : null;
    return { a, F: res ? res.F : null, common: res ? res.common : [], bAvg };
  });

  const hasF = rows.filter(r => r.F !== null && r.F > 0);
  const avgF = hasF.length ? hasF.reduce((s, r) => s + r.F, 0) / hasF.length : null;
  const maxF = hasF.length ? Math.max(...hasF.map(r => r.F)) : null;
  const minF = hasF.length ? Math.min(...hasF.map(r => r.F)) : null;

  return `
  <!-- Summary stats -->
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:14px">
    ${[
      ["Herd Average F", avgF !== null ? avgF.toFixed(2) + "%" : "N/A", "var(--gd)"],
      ["Highest F", maxF !== null ? maxF.toFixed(2) + "%" : "N/A", "#c0392b"],
      ["Lowest F", minF !== null ? minF.toFixed(2) + "%" : "N/A", "#16a34a"],
      ["Animals Calculated", hasF.length + " / " + allAnim.length, "var(--gd)"],
    ]
      .map(
        ([
          l,
          v,
          c,
        ]) => `<div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:10px 12px">
        <div style="font-size:9px;font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:3px">${l}</div>
        <div style="font-size:20px;font-weight:bold;color:${c}">${v}</div>
      </div>`
      )
      .join("")}
  </div>
  <!-- Per-animal table -->
  <div style="overflow-x:auto">
  <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
    <thead><tr style="background:var(--gp)">
      <th style="text-align:left;padding:6px 9px">Animal</th>
      <th style="padding:6px 9px">Breed</th>
      <th style="padding:6px 9px">Sex</th>
      <th style="padding:6px 9px">Inbreeding F%</th>
      <th style="padding:6px 9px">vs Breed Avg</th>
      <th style="text-align:left;padding:6px 9px">Common Ancestors</th>
    </tr></thead>
    <tbody>
    ${rows
      .map((r, i) => {
        const fc =
          r.F !== null && r.F > 0
            ? fColor(r.F)
            : { bg: "#f9f9f9", color: "#aaa", label: "Unknown" };
        const vs =
          r.F !== null && r.bAvg !== null
            ? `<span style="color:${r.F > r.bAvg ? "#c0392b" : "#16a34a"};font-weight:600">${r.F > r.bAvg ? "▲" : r.F < r.bAvg ? "▼" : "="} ${r.bAvg}%</span>`
            : '<span style="color:#aaa">—</span>';
        return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:6px 9px;font-weight:bold">
          <span style="cursor:pointer;color:var(--gm)" onclick="selAnimal('${r.a.id}');goPage('herd')">${r.a.name}</span>
          <div style="font-size:9px;color:var(--tl)">${r.a.regId || "—"}</div>
        </td>
        <td style="text-align:center;padding:6px 9px">${r.a.breed === "Holstein" ? '<span class="tag tgh">HO</span>' : '<span class="tag tgm">MO</span>'}</td>
        <td style="text-align:center;padding:6px 9px">${r.a.sex === "Male" ? "🐂" : "🐄"}</td>
        <td style="text-align:center;padding:6px 9px">${r.F !== null && r.F > 0 ? fBadge(r.F) : '<span style="color:#aaa;font-size:10px">Not enough pedigree data</span>'}</td>
        <td style="text-align:center;padding:6px 9px">${vs}</td>
        <td style="padding:6px 9px;color:var(--tm);font-size:10px">${
          r.common.length
            ? r.common
                .slice(0, 3)
                .map(c => c.label)
                .join(", ") + (r.common.length > 3 ? " …" : "")
            : "—"
        }</td>
      </tr>`;
      })
      .join("")}
    </tbody>
  </table>
  </div>
  ${herdTrendChartHTML()}
  ${herdFounderAnalysisHTML()}`;
}

/* ── Inbreeding trend chart (SVG line) over birth years ── */
function herdTrendChartHTML() {
  const trend = herdInbreedingTrend();
  if (trend.length < 2)
    return `<div style="margin-top:14px;font-family:sans-serif;font-size:11px;color:var(--tl)">
      📈 Inbreeding trend needs animals with birth years across at least two years to plot.
    </div>`;

  const W = 620, H = 180, pad = 34;
  const maxF = Math.max(...trend.map(t => t.avgF), 1);
  const xs = (i) => pad + (i * (W - pad * 2)) / Math.max(1, trend.length - 1);
  const ys = (f) => H - pad - (f / maxF) * (H - pad * 2);

  const linePts = trend.map((t, i) => `${xs(i).toFixed(1)},${ys(t.avgF).toFixed(1)}`).join(" ");
  const dots = trend
    .map((t, i) => {
      const fc = fColor(t.avgF);
      return `<circle cx="${xs(i).toFixed(1)}" cy="${ys(t.avgF).toFixed(1)}" r="4" fill="${fc.color}">
        <title>${t.year}: avg F ${t.avgF.toFixed(2)}% (${t.n} animal${t.n > 1 ? "s" : ""})</title>
      </circle>`;
    })
    .join("");
  const xlabels = trend
    .map((t, i) => `<text x="${xs(i).toFixed(1)}" y="${H - 8}" font-size="9" fill="#888" text-anchor="middle">${t.year}</text>`)
    .join("");
  // y gridlines at 0, 6.25, 12.5
  const grid = [0, 6.25, 12.5]
    .filter(g => g <= maxF * 1.05)
    .map(
      g => `<line x1="${pad}" y1="${ys(g).toFixed(1)}" x2="${W - pad}" y2="${ys(g).toFixed(1)}" stroke="#eee"/>
        <text x="4" y="${(ys(g) + 3).toFixed(1)}" font-size="8" fill="#aaa">${g}%</text>`
    )
    .join("");

  const first = trend[0].avgF, last = trend[trend.length - 1].avgF;
  const dir = last > first ? "▲ rising" : last < first ? "▼ falling" : "→ stable";
  const dirCol = last > first ? "#c0392b" : last < first ? "#16a34a" : "#888";

  return `
  <div style="margin-top:16px;border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
    <div style="padding:9px 14px;background:var(--gp);border-bottom:1px solid var(--bdr);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:6px">
      <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">📈 Herd Inbreeding Trend by Birth Year</div>
      <div style="font-family:sans-serif;font-size:11px;color:${dirCol};font-weight:700">${dir} (${first.toFixed(2)}% → ${last.toFixed(2)}%)</div>
    </div>
    <div style="padding:12px;overflow-x:auto">
      <svg viewBox="0 0 ${W} ${H}" style="width:100%;min-width:420px;max-width:680px;height:auto;font-family:sans-serif">
        ${grid}
        <polyline points="${linePts}" fill="none" stroke="var(--gm)" stroke-width="2"/>
        ${dots}
        ${xlabels}
      </svg>
      <div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-top:4px">
        A rising trend means the herd is becoming more inbred over time — introduce unrelated genetics to reverse it.
      </div>
    </div>
  </div>`;
}

/* ── Founder & common-ancestor analysis ── */
function herdFounderAnalysisHTML() {
  const fa = herdFounderAnalysis(IB_DEPTH);
  const topRows = fa.top
    .filter(t => t.descendants > 1)
    .slice(0, 10)
    .map(
      (t, i) => `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:5px 9px;font-weight:bold">${i + 1}</td>
        <td style="padding:5px 9px">${dpEscape(t.label)}</td>
        <td style="text-align:center;padding:5px 9px;font-weight:700;color:${t.descendants >= 4 ? "#c0392b" : "var(--gd)"}">${t.descendants}</td>
      </tr>`
    )
    .join("");

  const founderChips = fa.founders.length
    ? fa.founders
        .map(
          f => `<span style="display:inline-block;background:#f0fdf4;border:1px solid #a7f3d0;border-radius:12px;padding:3px 10px;font-size:11px;margin:2px;cursor:pointer" onclick="selAnimal('${f.id}');goPage('herd')">
            🌱 ${dpEscape(f.name)}${f.regId ? " · " + dpEscape(f.regId) : ""}
          </span>`
        )
        .join("")
    : `<span style="font-size:11px;color:var(--tl)">No founders identified — every animal has at least one recorded parent.</span>`;

  return `
  <div style="margin-top:16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px">
    <!-- Top recurring ancestors -->
    <div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
      <div style="padding:9px 14px;background:var(--gp);border-bottom:1px solid var(--bdr);font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">
        🧬 Most Influential Ancestors
      </div>
      <div style="padding:0">
        ${
          topRows
            ? `<table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:11px">
                 <thead><tr style="background:#f9f9f9">
                   <th style="text-align:left;padding:5px 9px;font-size:10px">#</th>
                   <th style="text-align:left;padding:5px 9px;font-size:10px">Ancestor</th>
                   <th style="text-align:center;padding:5px 9px;font-size:10px">In how many<br>pedigrees</th>
                 </tr></thead>
                 <tbody>${topRows}</tbody>
               </table>
               <div style="padding:7px 12px;font-size:10px;color:var(--tl)">Ancestors appearing in 4+ pedigrees (red) are the biggest inbreeding-risk drivers when mating within the herd.</div>`
            : `<div style="padding:14px;font-size:11px;color:var(--tl)">No ancestor appears in more than one animal's pedigree yet.</div>`
        }
      </div>
    </div>
    <!-- Founders -->
    <div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
      <div style="padding:9px 14px;background:var(--gp);border-bottom:1px solid var(--bdr);font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">
        🌱 Herd Founders <span style="font-weight:400;color:var(--tl)">(${fa.founders.length})</span>
      </div>
      <div style="padding:12px">
        <div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-bottom:8px">
          Animals with no recorded parents — the genetic foundation of your herd. Fewer founders = narrower gene pool.
        </div>
        ${founderChips}
      </div>
    </div>
  </div>`;
}

/* ── Breed average table ── */

function renderCrossbreedSection() {
  const rows = animals.map(a => {
    const comp = calcBreedComp(a);
    const gi = getGenInfo(comp);
    return { a, comp, gi };
  });

  // Group by generation
  const groups = {};
  rows.forEach(r => {
    const k = r.gi.gen;
    if (!groups[k]) groups[k] = [];
    groups[k].push(r);
  });

  // Grading-up progress chart per sire
  const sireSummary = animals
    .filter(a => a.sex === "Male")
    .map(s => {
      const off = offspring(s.id);
      const offWithComp = off.map(o => ({
        o,
        comp: calcBreedComp(o),
        gi: getGenInfo(calcBreedComp(o)),
      }));
      return { s, offWithComp };
    })
    .filter(x => x.offWithComp.length > 0);

  const genOrder = [
    "Purebred",
    "F1",
    "F2",
    "F3",
    "F4",
    "F5",
    "F6",
    "High Grade",
    "Cross",
    "Unknown",
  ];
  const sortedGroups = genOrder.filter(g => groups[g]);

  return `
  <!-- Summary cards -->
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:16px">
    ${sortedGroups
      .map(g => {
        const cnt = groups[g].length;
        const gi = groups[g][0].gi;
        return `<div style="background:${gi.bg};border:1.5px solid ${gi.color}44;border-radius:var(--rl);padding:11px 12px;text-align:center">
        <div style="font-size:clamp(20px,3vw,28px);font-weight:bold;color:${gi.color}">${cnt}</div>
        <div style="font-family:sans-serif;font-size:clamp(11px,1.2vw,13px);font-weight:700;color:${gi.color}">${g}</div>
        <div style="font-size:clamp(9px,1vw,10px);font-family:sans-serif;color:${gi.color};opacity:.75;margin-top:2px">${gi.desc.replace(gi.dominant, "").trim()}</div>
      </div>`;
      })
      .join("")}
  </div>

  <!-- Per-animal generation table -->
  <div style="overflow-x:auto;margin-bottom:18px">
  <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1vw,12px);min-width:500px">
    <thead><tr style="background:var(--gp)">
      <th style="text-align:left;padding:7px 9px">Animal</th>
      <th style="padding:7px 9px">Sex</th>
      <th style="padding:7px 9px">Generation</th>
      <th style="padding:7px 9px;min-width:160px">Breed Composition Bar</th>
      <th style="text-align:left;padding:7px 9px">Breakdown</th>
      <th style="padding:7px 9px">Purity %</th>
    </tr></thead>
    <tbody>
    ${rows
      .sort((a, b) => {
        const go = [
          "Purebred",
          "High Grade",
          "F6",
          "F5",
          "F4",
          "F3",
          "F2",
          "F1",
          "Cross",
          "Unknown",
        ];
        return go.indexOf(a.gi.gen) - go.indexOf(b.gi.gen) || b.gi.pct - a.gi.pct;
      })
      .map((r, i) => {
        const { a, comp, gi } = r;
        return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 === 0 ? "#fff" : "#fafafa"}">
        <td style="padding:6px 9px">
          <span style="cursor:pointer;font-weight:bold;color:var(--gm)" onclick="selAnimal('${a.id}');goPage('herd')">${a.name}</span>
          <div style="font-size:9px;color:var(--tl)">${a.regId || "—"}</div>
        </td>
        <td style="text-align:center;padding:6px 9px">${a.sex === "Male" ? "🐂" : "🐄"}</td>
        <td style="text-align:center;padding:6px 9px">
          <span style="background:${gi.bg};color:${gi.color};padding:3px 8px;border-radius:10px;font-weight:700;font-size:clamp(10px,1vw,12px);white-space:nowrap;border:1px solid ${gi.color}44">${gi.gen}</span>
        </td>
        <td style="padding:6px 9px">${compBar(comp, 16)}</td>
        <td style="padding:6px 9px;font-size:clamp(9px,1vw,11px)">${compDetail(comp)}</td>
        <td style="text-align:center;padding:6px 9px;font-weight:bold;color:${gi.color}">${gi.pct.toFixed(1)}%</td>
      </tr>`;
      })
      .join("")}
    </tbody>
  </table>
  </div>

  <!-- Sire grading-up trees -->
  ${
    sireSummary.length
      ? `
  <div style="font-family:sans-serif;font-size:clamp(12px,1.3vw,14px);font-weight:700;color:var(--gd);margin-bottom:10px">
    📈 Grading-Up Progress by Sire
  </div>
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:12px">
    ${sireSummary
      .map(({ s, offWithComp }) => {
        const sComp = calcBreedComp(s);
        const sGi = getGenInfo(sComp);
        return `<div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden">
        <div style="background:var(--gp);padding:9px 12px;display:flex;align-items:center;gap:8px">
          ${s.photo ? `<img src="${s.photo}" style="width:28px;height:28px;border-radius:50%;object-fit:cover">` : "<span style='font-size:18px'>🐂</span>"}
          <div>
            <div style="font-weight:bold;font-size:clamp(11px,1.2vw,13px)">${s.name}</div>
            <div style="font-size:9px;color:var(--tl)">${s.regId || "—"} · ${s.breed}</div>
          </div>
        </div>
        <div style="padding:9px 12px">
          ${offWithComp
            .map(
              ({ o, comp, gi }) => `
          <div style="display:flex;align-items:center;gap:8px;padding:4px 0;border-bottom:1px solid #f0f0f0;flex-wrap:wrap">
            <span style="font-size:10px;font-family:sans-serif;font-weight:700;color:${gi.color};background:${gi.bg};padding:2px 7px;border-radius:8px;flex-shrink:0">${gi.gen}</span>
            <span style="font-family:sans-serif;font-size:clamp(10px,1vw,12px);font-weight:bold;flex:1;min-width:80px;cursor:pointer;color:var(--gm)" onclick="selAnimal('${o.id}');goPage('herd')">${o.name}</span>
            <div style="flex:1;min-width:80px">${compBar(comp, 12)}</div>
            <span style="font-family:sans-serif;font-size:9px;color:var(--tl);flex-shrink:0">${gi.pct.toFixed(1)}%</span>
          </div>`
            )
            .join("")}
        </div>
      </div>`;
      })
      .join("")}
  </div>`
      : ""
  }

  <!-- Methodology note -->
  <div style="margin-top:14px;background:#fef9c3;border:1px solid #fde047;border-radius:var(--r);padding:10px 13px;font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:#854d0e;line-height:1.6">
    <b>How generation is calculated:</b> Each offspring inherits 50% from sire + 50% from dam.
    F1 = Pure Sire × Local Dam (50/50). F2 = F1 × Pure Sire (75/25). F3 = F2 × Pure Sire (87.5/12.5), etc.
    "High Grade" = ≥93.75% dominant breed. Override the auto-calculation in the animal form if needed.
  </div>
  `;
}

/* ════════════════════════════════════════════════════════════════
   CLIENTS PAGE
   ════════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════
   INDIVIDUAL PEDIGREES — for small-scale owners with 1-5 animals
   Animals here are NOT part of a registered herd.
   They can be registered directly with owner name + contact only.
   ════════════════════════════════════════════════════════════════ */

/* Individual animal: ownerId="" or ownerId refers to an "indiv_*" record */
/* We store lightweight individual owners separately */
let indivOwners = [];
const INDIV_KEY = "lp_indiv_owners";

function loadIndivOwners() {
  try {
    indivOwners = JSON.parse(localStorage.getItem(INDIV_KEY) || "[]");
  } catch (e) {
    indivOwners = [];
  }
}
function saveIndivOwners() {
  try {
    localStorage.setItem(INDIV_KEY, JSON.stringify(indivOwners));
  } catch (e) {}
}
function indivOwnerById(id) {
  return indivOwners.find(o => o.id === id);
}

/* Combined lookup: check both herds and individual owners */
function anyOwnerById(id) {
  if (!id) return null;
  return ownerById(id) || indivOwnerById(id);
}

/* ── Render Individual Pedigrees page ── */
function renderIndivPage() {
  const el = document.getElementById("pg-indiv");
  if (!el) return;

  loadIndivOwners();

  // Animals that belong to individual owners (indiv_ prefix) or have no herd
  const allIndivAnimals = animals.filter(
    a =>
      (a.ownerId && a.ownerId.startsWith("indiv_")) || (!a.ownerId && (a.ownerName || a.ownerPhone))
  );

  // Get all individual owners with their animals
  const indivData = indivOwners
    .map(io => {
      const myAnimals = animals.filter(a => a.ownerId === io.id);
      return { io, myAnimals };
    })
    .filter(d => d.myAnimals.length > 0 || true); // show all, even with 0 animals

  // Province grouping for summary
  const byProv = {};
  allIndivAnimals.forEach(a => {
    const p = a.province || "Unknown";
    if (!byProv[p]) byProv[p] = [];
    byProv[p].push(a);
  });

  // Stats
  const totalAnimals = allIndivAnimals.length;
  const totalOwners = indivOwners.length;

  el.innerHTML = `
  <!-- HEADER BANNER -->
  <div style="background:linear-gradient(135deg,#4c1d95,#7c3aed);border-radius:var(--rl);padding:14px 18px;color:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact">
    <div style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px">
      <div>
        <div style="font-size:clamp(14px,2vw,17px);font-weight:bold">👤 Individual Pedigrees</div>
        <div style="font-size:clamp(9px,1vw,11px);opacity:.8;margin-top:3px">
          For small-scale owners with 1–5 animals · No herd registration required
        </div>
      </div>
      <button class="btn bw bsm" onclick="openIndivOwnerModal()" style="font-size:clamp(11px,1vw,13px);color:#4c1d95">
        ＋ Register Individual Owner
      </button>
    </div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-top:12px">
      ${[
        [totalOwners, "Individual Owners"],
        [totalAnimals, "Total Animals"],
        [allIndivAnimals.filter(a => a.sex === "Female").length, "Cows"],
        [allIndivAnimals.filter(a => a.sex === "Male").length, "Bulls"],
        [Object.keys(byProv).length, "Provinces"],
      ]
        .map(
          ([
            v,
            l,
          ]) => `<div style="background:rgba(255,255,255,.12);border-radius:8px;padding:9px 10px;text-align:center">
        <div style="font-size:clamp(18px,2.5vw,24px);font-weight:bold">${v}</div>
        <div style="font-size:clamp(8px,.9vw,10px);opacity:.8;font-family:sans-serif">${l}</div>
      </div>`
        )
        .join("")}
    </div>
  </div>

  <!-- SEARCH + FILTER -->
  <div class="card">
    <div class="cb" style="padding:10px 14px">
      <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
        <div style="flex:1;min-width:180px;position:relative">
          <span style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--tl)">🔍</span>
          <input id="indivSearch" placeholder="Search by owner name, animal name, ear tag, province…"
            style="width:100%;padding:9px 12px 9px 32px;border:1px solid var(--bdr);border-radius:var(--r);font-family:sans-serif;font-size:12px"
            oninput="filterIndivCards()">
        </div>
        <select id="indivBreed" onchange="filterIndivCards()" style="font-family:sans-serif;font-size:12px;padding:7px 10px;border:1px solid var(--bdr);border-radius:var(--r)">
          <option value="">All Breeds</option>
          <option value="Holstein">🖤 Holstein</option>
          <option value="Montbeliard">🟠 Montbéliarde</option>
          <option value="Local">🌿 Local</option>
        </select>
        <select id="indivProv" onchange="filterIndivCards()" style="font-family:sans-serif;font-size:12px;padding:7px 10px;border:1px solid var(--bdr);border-radius:var(--r)">
          <option value="">All Provinces</option>
          ${[
            ...new Set(
              [...allIndivAnimals.map(a => a.province), ...indivOwners.map(o => o.province)].filter(
                Boolean
              )
            ),
          ]
            .sort()
            .map(p => `<option>${p}</option>`)
            .join("")}
        </select>
        <button class="btn bd bsm" onclick="clearIndivSearch()">✕</button>
        <span id="indivCount" style="font-family:sans-serif;font-size:11px;color:var(--tl)"></span>
      </div>
    </div>
  </div>

  <!-- INDIVIDUAL OWNER CARDS -->
  <div id="indivCards" style="display:flex;flex-direction:column;gap:12px">
  ${
    indivOwners.length === 0
      ? `<div class="card"><div class="cb"><div class="empty" style="padding:30px 20px">
        <div class="ei">👤</div>
        <h3>No individual owners registered yet</h3>
        <p>Register individual owners who have 1–5 animals but don't need a full herd.</p>
        <button class="btn bg" style="margin-top:12px" onclick="openIndivOwnerModal()">＋ Register First Individual Owner</button>
      </div></div></div>`
      : indivOwners.map(io => buildIndivCard(io)).join("")
  }
  </div>

  <!-- MODAL for individual owner -->
  <div id="indivModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center;overflow-y:auto">
    <div style="background:#fff;border-radius:var(--rl);padding:20px;width:min(520px,96vw);max-height:90vh;overflow-y:auto;box-shadow:var(--shl);margin:20px auto">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:14px">
        <h3 id="indivModalTitle" style="font-size:15px;color:#4c1d95">Register Individual Owner</h3>
        <button class="ib" onclick="closeIndivModal()" style="font-size:18px">✕</button>
      </div>
      <div class="st" style="background:#7c3aed">👤 Owner Details</div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:8px">
        <div class="fr full"><label>Full Name *</label><input id="im_name" placeholder="e.g. Ali Mohammad"></div>
        <div class="fr"><label>Phone Number</label><input id="im_phone" placeholder="+93-700-000001"></div>
        <div class="fr"><label>Email (optional)</label><input id="im_email" placeholder="email@example.com"></div>
      </div>
      <div class="st" style="background:#3a6bc4">📍 Location</div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:8px">
        <div class="fr"><label>Province</label>
          <select id="im_province" style="font-size:13px" onchange="cascadeDistrict('im_province','im_district')">
            <option value="">— Select Province —</option>
          </select>
        </div>
        <div class="fr"><label>District</label>
          <select id="im_district" style="font-size:13px">
            <option value="">— Select District —</option>
          </select>
        </div>
        <div class="fr full"><label>Address (optional)</label><input id="im_address" placeholder="Village / area address"></div>
      </div>
      <div class="st" style="background:#16a34a">📝 Notes</div>
      <textarea id="im_notes" style="width:100%;min-height:50px;font-size:13px;padding:8px 10px;border:1px solid var(--bdr);border-radius:var(--r)" placeholder="Notes about this owner's animals or breeding program…"></textarea>
      <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px;flex-wrap:wrap">
        <button class="btn bd" onclick="closeIndivModal()">Cancel</button>
        <button class="btn bg" style="background:#7c3aed" onclick="saveIndivOwner()">💾 Save Owner</button>
      </div>
    </div>
  </div>
  `;

  // Populate province dropdown in modal
  setTimeout(() => {
    const provEl = document.getElementById("im_province");
    if (provEl && typeof getProvincesAF === "function") {
      provEl.innerHTML =
        '<option value="">— Select Province —</option>' +
        getProvincesAF()
          .map(p => `<option>${p}</option>`)
          .join("");
    }
  }, 30);
}

/* ── Build individual owner card ── */
function buildIndivCard(io) {
  const myAnimals = animals.filter(a => a.ownerId === io.id);
  const cows = myAnimals.filter(a => a.sex === "Female");
  const bulls = myAnimals.filter(a => a.sex === "Male");
  const avgMilk = cows.filter(c => c.milk).length
    ? (
        cows.filter(c => c.milk).reduce((s, c) => s + parseFloat(c.milk), 0) /
        cows.filter(c => c.milk).length
      ).toFixed(0)
    : null;
  return `<div class="card" data-individ="${io.id}">
    <div class="ch" style="background:linear-gradient(135deg,#f3e8ff,#ede9fe);flex-wrap:wrap;gap:8px">
      <div style="display:flex;align-items:center;gap:10px;flex:1;min-width:0">
        <div style="width:38px;height:38px;border-radius:50%;background:#7c3aed22;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0;border:2px solid #7c3aed33">👤</div>
        <div style="min-width:0">
          <div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap">
            <h2 style="font-size:clamp(13px,1.5vw,15px);color:#4c1d95">${io.name}</h2>
            <span style="font-family:sans-serif;font-size:10px;background:#7c3aed18;color:#7c3aed;padding:2px 7px;border-radius:10px;border:1px solid #7c3aed33">Individual Owner</span>
          </div>
          <div style="font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:var(--tl);margin-top:2px">
            ${io.phone ? "📞 " + io.phone + " &nbsp;" : ""}${io.province ? "📍 " + io.district + ", " + io.province : ""}
          </div>
        </div>
      </div>
      <div style="display:flex;gap:5px;flex-wrap:wrap;flex-shrink:0">
        <button class="btn bw bsm" style="font-size:clamp(10px,1vw,12px);border:1px solid #7c3aed;color:#7c3aed" onclick="addAnimalToIndiv('${io.id}')">＋ Add Animal</button>
        <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px)" onclick="editIndivOwner('${io.id}')">✏️ Edit</button>
        <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px)" onclick="deleteIndivOwner('${io.id}')">🗑</button>
      </div>
    </div>
    <div class="cb" style="padding:12px 15px">
      <!-- Stats row -->
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:7px;margin-bottom:12px">
        ${[
          [myAnimals.length, "Animals", "#7c3aed"],
          [bulls.length, "🐂 Bulls", "#3a6bc4"],
          [cows.length, "🐄 Cows", "#8e44ad"],
          [avgMilk ? Number(avgMilk).toLocaleString() + " kg" : "—", "Avg Milk", "#c07a00"],
        ]
          .map(
            ([
              v,
              l,
              c,
            ]) => `<div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:6px;padding:7px 9px;text-align:center">
            <div style="font-size:clamp(14px,2vw,18px);font-weight:bold;color:${c}">${v}</div>
            <div style="font-size:9px;font-family:sans-serif;color:var(--tl)">${l}</div>
          </div>`
          )
          .join("")}
      </div>
      <!-- Animal pedigree list -->
      ${
        myAnimals.length === 0
          ? `<div style="font-family:sans-serif;font-size:12px;color:var(--tl);text-align:center;padding:12px">
            No animals registered yet.
            <button class="btn bw bsm" style="border:1px solid #7c3aed;color:#7c3aed;margin-left:8px" onclick="addAnimalToIndiv('${io.id}')">＋ Add Animal</button>
          </div>`
          : `<div>
          <div style="font-family:sans-serif;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:7px">
            📋 Pedigrees (${myAnimals.length})
          </div>
          <div style="overflow-x:auto">
          <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1vw,12px);min-width:440px">
            <thead><tr style="background:#f3e8ff">
              <th style="text-align:left;padding:5px 8px">Animal</th>
              <th style="padding:5px 8px">Ear Tag</th>
              <th style="padding:5px 8px">Breed &amp; Gen</th>
              <th style="padding:5px 8px;min-width:100px">Composition</th>
              <th style="padding:5px 8px">Sire</th>
              <th style="padding:5px 8px">Milk 305d</th>
              <th style="padding:5px 8px"></th>
            </tr></thead>
            <tbody>
            ${myAnimals
              .map((a, i) => {
                const comp = calcBreedComp(a);
                const gi = getGenInfo(comp);
                return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
                <td style="padding:5px 8px">
                  <div style="display:flex;align-items:center;gap:5px">
                    ${a.photo ? `<img src="${a.photo}" style="width:22px;height:22px;border-radius:50%;object-fit:cover">` : ""}
                    <div>
                      <span style="font-weight:bold;cursor:pointer;color:var(--gm)" onclick="selAnimal('${a.id}');goPage('herd')">${a.name}</span>
                      <div style="font-size:9px;color:var(--tl)">${a.regId || "—"} · ${a.sex === "Male" ? "🐂" : "🐄"}</div>
                    </div>
                  </div>
                </td>
                <td style="text-align:center;padding:5px 8px;font-size:10px">${a.earTag || "—"}</td>
                <td style="text-align:center;padding:5px 8px">
                  <span style="background:${gi.bg};color:${gi.color};padding:2px 6px;border-radius:7px;font-weight:700;font-size:10px">${gi.gen}</span>
                  <div style="font-size:9px;color:var(--tl)">${a.breed}</div>
                </td>
                <td style="padding:5px 8px">${compBar(comp, 10)}</td>
                <td style="padding:5px 8px;font-size:10px">${a.sireName || "—"}</td>
                <td style="text-align:center;padding:5px 8px;font-size:10px">${a.milk ? fmtN(a.milk) + " kg" : "—"}</td>
                <td style="text-align:center;padding:5px 8px">
                  <button class="btn bw bsm" style="font-size:10px" onclick="selAnimal('${a.id}');goPage('herd')">📋</button>
                  <button class="btn bd bsm" style="font-size:10px" onclick="doEdit('${a.id}')">✏️</button>
                </td>
              </tr>`;
              })
              .join("")}
            </tbody>
          </table>
          </div>
        </div>`
      }
      ${io.notes ? `<div style="margin-top:9px;padding:6px 9px;background:#f3e8ff;border-radius:6px;font-family:sans-serif;font-size:10px;color:#4c1d95;border-left:3px solid #7c3aed">${io.notes}</div>` : ""}
    </div>
  </div>`;
}

/* ── Filter individual cards ── */
function filterIndivCards() {
  const q = (document.getElementById("indivSearch")?.value || "").toLowerCase();
  const br = document.getElementById("indivBreed")?.value || "";
  const pv = document.getElementById("indivProv")?.value || "";
  let shown = 0;
  document.querySelectorAll("[data-individ]").forEach(card => {
    const ioid = card.getAttribute("data-individ");
    const io = indivOwnerById(ioid) || {};
    const myA = animals.filter(a => a.ownerId === ioid);
    const show =
      (!q ||
        (io.name || "").toLowerCase().includes(q) ||
        (io.province || "").toLowerCase().includes(q) ||
        (io.phone || "").includes(q) ||
        myA.some(
          a =>
            (a.name || "").toLowerCase().includes(q) ||
            (a.earTag || "").toLowerCase().includes(q) ||
            (a.regId || "").toLowerCase().includes(q)
        )) &&
      (!br || myA.some(a => a.breed === br)) &&
      (!pv || (io.province || "") === pv || myA.some(a => a.province === pv));
    card.style.display = show ? "" : "none";
    if (show) shown++;
  });
  const cnt = document.getElementById("indivCount");
  if (cnt) cnt.textContent = q || br || pv ? `${shown} owner${shown !== 1 ? "s" : ""} found` : "";
}
function clearIndivSearch() {
  ["indivSearch", "indivBreed", "indivProv"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  filterIndivCards();
}

/* ── Add animal to individual owner ── */
function addAnimalToIndiv(ownerId) {
  openNew("", "", "", ownerId);
}

/* ── Individual owner CRUD ── */
let editIndivId = null;
function openIndivOwnerModal() {
  editIndivId = null;
  document.getElementById("indivModalTitle").textContent = "Register Individual Owner";
  ["im_name", "im_phone", "im_email", "im_address", "im_notes"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  const provEl = document.getElementById("im_province");
  if (provEl && typeof getProvincesAF === "function")
    provEl.innerHTML =
      '<option value="">— Select Province —</option>' +
      getProvincesAF()
        .map(p => `<option>${p}</option>`)
        .join("");
  const distEl = document.getElementById("im_district");
  if (distEl) distEl.innerHTML = '<option value="">— Select District —</option>';
  document.getElementById("indivModal").style.display = "flex";
}
function editIndivOwner(id) {
  const io = indivOwnerById(id);
  if (!io) return;
  editIndivId = id;
  document.getElementById("indivModalTitle").textContent = "Edit — " + io.name;
  const s = (el, v) => {
    const e = document.getElementById(el);
    if (e) e.value = v || "";
  };
  s("im_name", io.name);
  s("im_phone", io.phone);
  s("im_email", io.email);
  s("im_address", io.address);
  s("im_notes", io.notes);
  const provEl = document.getElementById("im_province");
  if (provEl && typeof getProvincesAF === "function") {
    provEl.innerHTML =
      '<option value="">— Select Province —</option>' +
      getProvincesAF()
        .map(p => `<option${p === io.province ? " selected" : ""}>${p}</option>`)
        .join("");
  }
  const dists =
    io.province && typeof getDistrictsAF === "function" ? getDistrictsAF(io.province) : [];
  const distEl = document.getElementById("im_district");
  if (distEl)
    distEl.innerHTML =
      '<option value="">— Select District —</option>' +
      dists.map(d => `<option${d === io.district ? " selected" : ""}>${d}</option>`).join("");
  document.getElementById("indivModal").style.display = "flex";
}
function closeIndivModal() {
  document.getElementById("indivModal").style.display = "none";
}
function saveIndivOwner() {
  const name = document.getElementById("im_name")?.value.trim();
  if (!name) {
    alert("Owner name is required.");
    return;
  }
  const g = id => {
    const e = document.getElementById(id);
    return e ? e.value.trim() : "";
  };
  const data = {
    name,
    phone: g("im_phone"),
    email: g("im_email"),
    province: g("im_province"),
    district: g("im_district"),
    address: g("im_address"),
    notes: g("im_notes"),
  };
  if (editIndivId) {
    Object.assign(indivOwnerById(editIndivId), data);
  } else {
    data.id = "indiv_" + Date.now();
    indivOwners.push(data);
  }
  saveIndivOwners();
  closeIndivModal();
  renderIndivPage();
  renderOwnerBar();
  showToast(editIndivId ? "Owner updated ✓" : "Individual owner registered ✓");
}
function deleteIndivOwner(id) {
  const cnt = animals.filter(a => a.ownerId === id).length;
  if (!confirm(`Delete this individual owner?\n${cnt} animal(s) will become unassigned.`)) return;
  indivOwners = indivOwners.filter(o => o.id !== id);
  animals.forEach(a => {
    if (a.ownerId === id) a.ownerId = "";
  });
  saveIndivOwners();
  persist();
  renderIndivPage();
  renderOwnerBar();
  showToast("Individual owner deleted");
}

function filterHerdCards() {
  const q = (document.getElementById("herdSearch")?.value || "").toLowerCase().trim();
  const bf = document.getElementById("herdBreedFilter")?.value || "";
  const pf = document.getElementById("herdProvFilter")?.value || "";
  const cards = document.querySelectorAll("[data-herdid]");
  let visible = 0;
  cards.forEach(card => {
    const hid = card.getAttribute("data-herdid");
    const h = ownerById(hid) || {};
    const herdAnimals = animals.filter(a => a.ownerId === hid);
    const show =
      (!q ||
        (h.herdName || "").toLowerCase().includes(q) ||
        (h.name || "").toLowerCase().includes(q) ||
        (h.herdCode || "").toLowerCase().includes(q) ||
        (h.province || "").toLowerCase().includes(q) ||
        (h.district || "").toLowerCase().includes(q) ||
        (h.phone || "").includes(q) ||
        herdAnimals.some(
          a =>
            (a.name || "").toLowerCase().includes(q) ||
            (a.earTag || "").toLowerCase().includes(q) ||
            (a.regId || "").toLowerCase().includes(q)
        )) &&
      (!bf || (h.breedFocus || "") === bf) &&
      (!pf || (h.province || "") === pf);
    card.style.display = show ? "" : "none";
    if (show) visible++;
  });
  const cnt = document.getElementById("herdSearchCount");
  if (cnt)
    cnt.textContent = q || bf || pf ? `${visible} herd${visible !== 1 ? "s" : ""} found` : "";
}
function clearHerdSearch() {
  ["herdSearch", "herdBreedFilter", "herdProvFilter"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  filterHerdCards();
}

function renderClientsPage() {
  const el = document.getElementById("pg-clients");
  if (!el) return;

  const herds = owners; // owners ARE herds
  // Build per-herd stats
  const herdStats = herds.map(h => {
    const herd = animals.filter(a => a.ownerId === h.id);
    const bulls = herd.filter(a => a.sex === "Male");
    const cows = herd.filter(a => a.sex === "Female");
    const avgMilk = cows.filter(c => c.milk).length
      ? (
          cows.filter(c => c.milk).reduce((s, c) => s + parseFloat(c.milk), 0) /
          cows.filter(c => c.milk).length
        ).toFixed(0)
      : null;
    const genMap = {};
    herd.forEach(a => {
      const g = getGenInfo(calcBreedComp(a)).gen;
      genMap[g] = (genMap[g] || 0) + 1;
    });
    return { h, herd, bulls, cows, avgMilk, genMap };
  });

  const allProvinces = [...new Set(animals.map(a => a.province).filter(Boolean))].sort();
  const allBreeds = [...new Set(animals.map(a => a.breed).filter(Boolean))].sort();
  const sireList = animals.filter(a => a.sex === "Male");

  el.innerHTML = `
  <!-- ══ ADMIN OVERVIEW BANNER ══ -->
  <div style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);border-radius:var(--rl);padding:16px 20px;color:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact">
    <div style="display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px">
      <div>
        <div style="font-size:clamp(14px,2vw,18px);font-weight:bold">🐄 Larspeenghar Herd Registry</div>
        <div style="font-size:clamp(9px,1.1vw,11px);opacity:.8;margin-top:3px">Manage herds, owners and pedigrees · Classified by herd, breed, sire, province</div>
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap">
        <button class="btn bw bsm" onclick="openClientModal()" style="font-size:clamp(11px,1vw,13px)">＋ Register New Herd</button>
      </div>
    </div>
    <!-- Global stats -->
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-top:14px">
      ${[
        [herds.length, "Registered Herds"],
        [animals.length, "Total Animals"],
        [animals.filter(a => a.sex === "Male").length, "Bulls / Sires"],
        [animals.filter(a => a.sex === "Female").length, "Cows / Heifers"],
        [[...new Set(animals.map(a => a.province).filter(Boolean))].length, "Provinces"],
        [animals.filter(a => a.breed === "Holstein").length, "Holstein"],
        [animals.filter(a => a.breed === "Montbeliard").length, "Montbéliarde"],
        [animals.filter(a => a.breed === "Local" || a.breedCompOverride).length, "Local/Cross"],
      ]
        .map(
          ([
            v,
            l,
          ]) => `<div style="background:rgba(255,255,255,.12);border-radius:8px;padding:9px 10px;text-align:center">
        <div style="font-size:clamp(18px,2.5vw,26px);font-weight:bold">${v}</div>
        <div style="font-size:clamp(8px,0.9vw,10px);opacity:.8;font-family:sans-serif">${l}</div>
      </div>`
        )
        .join("")}
    </div>
    <!-- Current session -->
    <div style="margin-top:12px;background:rgba(255,255,255,.1);border-radius:6px;padding:8px 12px;font-family:sans-serif;font-size:clamp(10px,1vw,12px)">
      "🔐 <b>Admin view</b> — all herds and pedigrees visible"
    </div>
  </div>

  <!-- ══ HERD QUICK SEARCH ══ -->
  <div class="card">
    <div class="cb" style="padding:10px 14px">
      <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
        <div style="flex:1;min-width:200px;position:relative">
          <span style="position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--tl);font-size:14px">🔍</span>
          <input id="herdSearch" placeholder="Search herds by name, owner, province, breed code…"
            style="width:100%;padding:9px 12px 9px 34px;border:1px solid var(--bdr);border-radius:var(--r);font-family:sans-serif;font-size:13px"
            oninput="filterHerdCards()">
        </div>
        <select id="herdBreedFilter" onchange="filterHerdCards()" style="font-family:sans-serif;font-size:12px;padding:8px 10px;border:1px solid var(--bdr);border-radius:var(--r)">
          <option value="">All Breeds</option>
          <option value="Holstein">🖤 Holstein</option>
          <option value="Montbeliard">🟠 Montbéliarde</option>
          <option value="Local">🌿 Local</option>
          <option value="Mixed">🔀 Mixed</option>
        </select>
        <select id="herdProvFilter" onchange="filterHerdCards()" style="font-family:sans-serif;font-size:12px;padding:8px 10px;border:1px solid var(--bdr);border-radius:var(--r)">
          <option value="">All Provinces</option>
        </select>
        <button class="btn bd bsm" onclick="clearHerdSearch()" style="font-size:12px">✕ Clear</button>
        <span id="herdSearchCount" style="font-family:sans-serif;font-size:11px;color:var(--tl)"></span>
      </div>
    </div>
  </div>

  <!-- ══ MULTI-DIMENSION FILTER ══ -->
  <div class="card">
    <div class="ch" style="cursor:pointer" onclick="bpToggle('cl_filters')">
      <h2>🔍 Search &amp; Filter — By Herd · Breed · Sire · Province · Generation</h2>
      <span id="cl_filters_ico" style="font-size:18px;color:var(--gm)">▲</span>
    </div>
    <div id="cl_filters">
      <div style="padding:14px">
        <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:9px;margin-bottom:12px">
          <div class="fr"><label>🐄 Herd / Owner</label>
            <select id="cf_owner" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All Herds</option>
              ${herds.map(h => `<option value="${h.id}">${h.herdName || h.name} (${h.name})</option>`).join("")}
            </select>
          </div>
          <div class="fr"><label>🐂 Breed</label>
            <select id="cf_breed" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All Breeds</option>
              ${allBreeds.map(b => `<option>${b}</option>`).join("")}
            </select>
          </div>
          <div class="fr"><label>📍 Province</label>
            <select id="cf_prov" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All Provinces</option>
              ${getProvincesAF()
                .map(
                  p =>
                    `<option${allProvinces.includes(p) ? " style='font-weight:bold'" : ""}>${p}</option>`
                )
                .join("")}
            </select>
          </div>
          <div class="fr"><label>🐂 Sire</label>
            <select id="cf_sire" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All Sires</option>
              ${sireList.map(s => `<option value="${s.id}">${s.name} (${s.regId || "—"})</option>`).join("")}
            </select>
          </div>
          <div class="fr"><label>🔀 Generation</label>
            <select id="cf_gen" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All Generations</option>
              ${["Purebred", "F1", "F2", "F3", "F4", "F5", "High Grade", "Cross"].map(g => `<option>${g}</option>`).join("")}
            </select>
          </div>
          <div class="fr"><label>⚧ Sex</label>
            <select id="cf_sex" onchange="renderFilteredHerd()" style="font-size:12px">
              <option value="">All</option>
              <option value="Female">Female (Cow)</option>
              <option value="Male">Male (Bull)</option>
            </select>
          </div>
          <div class="fr" style="justify-content:flex-end;padding-top:14px">
            <button class="btn bd bsm" onclick="clearClientFilters()" style="font-size:clamp(11px,1vw,12px)">✕ Clear</button>
          </div>
        </div>
        <div id="cf_result">${renderFilteredHerd(true)}</div>
      </div>
    </div>
  </div>

  <!-- ══ HERD CARDS ══ -->
  <div style="display:flex;flex-direction:column;gap:14px">
  ${herdStats
    .map(({ h, herd, bulls, cows, avgMilk, genMap }, hi) => {
      const breedBC = BREEDS_CFG[h.breedFocus] || {
        emoji: "🐄",
        color:
          h.breedFocus === "Holstein"
            ? "#3a6bc4"
            : h.breedFocus === "Montbeliard"
              ? "#e67e22"
              : "#16a34a",
      };
      return `
    <div class="card" data-herdid="${h.id}">
      <!-- HERD HEADER -->
      <div class="ch" style="background:linear-gradient(135deg,${breedBC.color}18,${breedBC.color}08);flex-wrap:wrap;gap:8px">
        <div style="display:flex;align-items:center;gap:10px;min-width:0;flex:1">
          <div style="width:44px;height:44px;border-radius:50%;background:${breedBC.color}22;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;border:2px solid ${breedBC.color}44">${breedBC.emoji}</div>
          <div style="min-width:0">
            <div style="display:flex;align-items:center;gap:7px;flex-wrap:wrap">
              <h2 style="font-size:clamp(13px,1.5vw,16px)">${h.herdName || h.name}</h2>
              <span style="font-family:sans-serif;font-size:10px;background:${breedBC.color}18;color:${breedBC.color};padding:2px 8px;border-radius:10px;font-weight:700;border:1px solid ${breedBC.color}33">${h.herdCode || ""}</span>
              
            </div>
            <div style="font-family:sans-serif;font-size:clamp(10px,1vw,11px);color:var(--tl);margin-top:2px">
              👤 ${h.name} &nbsp;·&nbsp; 📞 ${h.phone || "—"} &nbsp;·&nbsp; 📍 ${h.district}, ${h.province}
            </div>
          </div>
        </div>
        <div style="display:flex;gap:5px;flex-wrap:wrap;flex-shrink:0">
          <button class="btn bg bsm" style="font-size:clamp(10px,1vw,12px)" onclick="setOwnerF('${h.id}');goPage('herd')">👁 View Herd</button>
          <button class="btn bw bsm" style="font-size:clamp(10px,1vw,12px)" onclick="openNew('','','${h.id}')">＋ Add Animal</button>
          <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px)" onclick="editClientModal('${h.id}')">✏️ Edit</button>
          <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px)" onclick="deleteOwner('${h.id}')">🗑</button>
        </div>
      </div>

      <div class="cb" style="padding:14px">
        <!-- TWO COLUMN: Info + Stats -->
        <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:14px">
          <!-- Owner / Location info -->
          <div>
            <div style="font-family:sans-serif;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:7px">📋 Herd Information</div>
            <div style="display:grid;grid-template-columns:1fr 1fr;gap:5px">
              ${[
                ["Herd Code", h.herdCode || "—"],
                ["Breed Focus", h.breedFocus || "—"],
                ["Owner", h.name],
                ["Phone", h.phone || "—"],
                ["Email", h.email || "—"],
                ["Province", h.province || "—"],
                ["District", h.district || "—"],
                [
                  "Established",
                  h.established
                    ? new Date(h.established + "T12:00").toLocaleDateString("en-GB", {
                        month: "short",
                        year: "numeric",
                      })
                    : "—",
                ],
              ]
                .map(
                  ([l, v]) =>
                    `<div style="font-family:sans-serif"><span style="font-size:9px;color:var(--tl);display:block">${l}</span><b style="font-size:11px;color:var(--td)">${v}</b></div>`
                )
                .join("")}
            </div>
            ${h.notes ? `<div style="margin-top:9px;padding:6px 9px;background:#f9f9f9;border-radius:6px;font-family:sans-serif;font-size:10px;color:var(--tm);border-left:3px solid var(--gm)">${h.notes}</div>` : ""}
          </div>

          <!-- Herd Stats -->
          <div>
            <div style="font-family:sans-serif;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:7px">📊 Herd Statistics</div>
            <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:10px">
              ${[
                [herd.length, "Animals", "var(--gd)"],
                [bulls.length, "🐂 Bulls", "#3a6bc4"],
                [cows.length, "🐄 Cows", "#8e44ad"],
                [avgMilk ? Number(avgMilk).toLocaleString() + " kg" : "—", "Avg Milk", "#c07a00"],
                [cows.filter(c => c.milk).length, "With Records", "var(--gm)"],
                [Object.keys(genMap).length, "Generations", "#7c3aed"],
              ]
                .map(
                  ([
                    v,
                    l,
                    c,
                  ]) => `<div style="background:#f9f9f9;border-radius:6px;padding:7px 9px;border:1px solid var(--bdr);text-align:center">
                  <div style="font-size:clamp(14px,2vw,18px);font-weight:bold;color:${c}">${v}</div>
                  <div style="font-size:9px;font-family:sans-serif;color:var(--tl)">${l}</div>
                </div>`
                )
                .join("")}
            </div>
            <!-- Generation breakdown -->
            ${
              Object.keys(genMap).length
                ? `<div>
              <div style="font-size:9px;font-family:sans-serif;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--tl);margin-bottom:5px">Generation Breakdown</div>
              <div style="display:flex;flex-wrap:wrap;gap:4px">
                ${Object.entries(genMap)
                  .sort((a, b) => b[1] - a[1])
                  .map(([g, cnt]) => {
                    const fakeComp =
                      g === "Purebred"
                        ? { Holstein: 100 }
                        : g === "F1"
                          ? { Holstein: 50, Local: 50 }
                          : g === "F2"
                            ? { Holstein: 75, Local: 25 }
                            : { Holstein: 87.5, Local: 12.5 };
                    const gi = getGenInfo(fakeComp);
                    return `<span style="background:${gi.bg};color:${gi.color};padding:2px 9px;border-radius:9px;font-family:sans-serif;font-size:10px;font-weight:700;border:1px solid ${gi.color}33">${g}: ${cnt}</span>`;
                  })
                  .join("")}
              </div>
            </div>`
                : ""
            }
          </div>
        </div>

        <!-- Collapsible pedigree list -->
        <div>
          <div style="display:flex;align-items:center;justify-content:space-between;cursor:pointer;padding:7px 0;border-top:1px solid var(--bdr)"
            onclick="bpToggle('hl_${h.id}')">
            <div style="font-family:sans-serif;font-size:11px;font-weight:700;color:var(--gd)">
              📋 Pedigrees in this Herd (${herd.length} animals)
            </div>
            <span id="hl_${h.id}_ico" style="color:var(--gm);font-size:16px">▼</span>
          </div>
          <div id="hl_${h.id}" style="display:none">
            ${
              !herd.length
                ? `<div style="font-family:sans-serif;font-size:12px;color:var(--tl);padding:12px 0">No animals registered yet. <button class="btn bg bsm" onclick="openNew('','','${h.id}')">＋ Add first animal</button></div>`
                : `<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1vw,12px);min-width:500px;margin-top:8px">
                <thead><tr style="background:var(--gp)">
                  <th style="text-align:left;padding:6px 9px">Animal / Pedigree</th>
                  <th style="padding:6px 9px">Ear Tag</th>
                  <th style="padding:6px 9px">Sex</th>
                  <th style="padding:6px 9px">DOB</th>
                  <th style="padding:6px 9px">Generation</th>
                  <th style="padding:6px 9px;min-width:120px">Composition</th>
                  <th style="padding:6px 9px">Sire</th>
                  <th style="padding:6px 9px">Milk 305d</th>
                  <th style="padding:6px 9px"></th>
                </tr></thead>
                <tbody>
                  ${herd
                    .map((a, i) => {
                      const comp = calcBreedComp(a);
                      const gi = getGenInfo(comp);
                      return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
                      <td style="padding:6px 9px">
                        <div style="display:flex;align-items:center;gap:6px">
                          ${a.photo ? `<img src="${a.photo}" style="width:24px;height:24px;border-radius:50%;object-fit:cover;flex-shrink:0">` : ""}
                          <div>
                            <span style="font-weight:bold;cursor:pointer;color:var(--gm)" onclick="selAnimal('${a.id}');goPage('herd')">${a.name}</span>
                            <div style="font-size:9px;color:var(--tl)">${a.regId || "—"}</div>
                          </div>
                        </div>
                      </td>
                      <td style="text-align:center;padding:6px 9px;font-size:10px">${a.earTag || "—"}</td>
                      <td style="text-align:center;padding:6px 9px">${a.sex === "Male" ? "🐂" : "🐄"}</td>
                      <td style="text-align:center;padding:6px 9px;font-size:10px">${fmtD(a.dob)}</td>
                      <td style="text-align:center;padding:6px 9px">
                        <span style="background:${gi.bg};color:${gi.color};padding:2px 7px;border-radius:8px;font-weight:700;font-size:10px">${gi.gen}</span>
                      </td>
                      <td style="padding:6px 9px">${compBar(comp, 12)}</td>
                      <td style="padding:6px 9px;font-size:10px">${a.sireName || "—"}</td>
                      <td style="text-align:center;padding:6px 9px;font-size:10px">${a.milk ? fmtN(a.milk) + " kg" : "—"}</td>
                      <td style="text-align:center;padding:6px 9px">
                        <button class="btn bg bsm" onclick="selAnimal('${a.id}');goPage('herd')" style="font-size:10px">📋 Pedigree</button>
                      </td>
                    </tr>`;
                    })
                    .join("")}
                </tbody>
              </table></div>`
            }
          </div>
        </div>
      </div>
    </div>`;
    })
    .join("")}
  </div>

  <!-- No herds -->
  ${(() => {
    setTimeout(() => {
      const pf = document.getElementById("herdProvFilter");
      if (pf) {
        const ep = [...new Set(owners.map(o => o.province).filter(Boolean))].sort();
        const ap = getProvincesAF();
        pf.innerHTML =
          '<option value="">All Provinces</option>' +
          ap
            .map(
              p =>
                `<option value="${p}"${ep.includes(p) ? ' style="font-weight:bold"' : ""}>${p}</option>`
            )
            .join("");
      }
    }, 30);
    return "";
  })()}
  ${herds.length === 0 ? `<div class="empty"><div class="ei">🐄</div><h3>No herds registered yet</h3><p>Click + Register New Herd to get started.</p></div>` : ""}

  <!-- CLIENT / HERD MODAL -->
  <div id="clientModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:300;align-items:center;justify-content:center;overflow-y:auto">
    <div style="background:#fff;border-radius:var(--rl);padding:20px;width:min(600px,96vw);max-height:92vh;overflow-y:auto;box-shadow:var(--shl);margin:20px auto">
      <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px">
        <h3 id="cmTitle" style="font-size:16px;color:var(--gd)">Register New Herd</h3>
        <button class="ib" onclick="closeClientModal()" style="font-size:18px">✕</button>
      </div>

      <div class="st">🐄 Herd Information</div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:4px">
        <div class="fr"><label>Herd Name *</label><input id="cm_herdName" placeholder="e.g. Karimi Holstein Farm"></div>
        <div class="fr"><label>Herd Code / ID</label><input id="cm_herdCode" placeholder="e.g. LK-HRD-001"></div>
        <div class="fr"><label>Breed Focus</label>
          <select id="cm_breedFocus" style="font-size:13px">
            <option value="Holstein">🖤 Holstein</option>
            <option value="Montbeliard">Montbéliarde</option>
            <option value="Local">🌿 Local / Indigenous</option>
            <option value="Mixed">🔀 Mixed / Crossbred</option>
          </select>
        </div>
        <div class="fr"><label>Date Established</label><input type="date" id="cm_established"></div>
      </div>

      <div class="st std">👤 Owner Information</div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:4px">
        <div class="fr"><label>Owner Full Name *</label><input id="cm_name" placeholder="e.g. Ahmad Karimi"></div>
        <div class="fr"><label>Phone Number</label><input id="cm_phone" placeholder="+93-700-000001"></div>
        <div class="fr"><label>Email Address</label><input id="cm_email" placeholder="email@example.com"></div>
        <div class="fr full"><label>Farm / Herd Address</label><input id="cm_address" placeholder="Full address of the farm"></div>
      </div>

      <div class="st" style="background:#3a6bc4">📍 Location</div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:10px;margin-bottom:4px">
        <div class="fr"><label>Province</label>
          <select id="cm_province" style="font-size:13px" onchange="cascadeDistrict('cm_province','cm_district')">
            <option value="">— Select Province —</option>
          </select>
        </div>
        <div class="fr"><label>District</label>
          <select id="cm_district" style="font-size:13px">
            <option value="">— Select District —</option>
          </select>
        </div>
      </div>

      <div class="st" style="background:#7c3aed">📝 Notes</div>
      <div style="margin-bottom:4px">
        <textarea id="cm_notes" style="width:100%;min-height:60px;font-size:13px;padding:8px 10px;border:1px solid var(--bdr);border-radius:var(--r)" placeholder="Breeding program description, special notes…"></textarea>
      </div>

      <div style="display:flex;justify-content:flex-end;gap:8px;margin-top:14px;flex-wrap:wrap">
        <button class="btn bd" onclick="closeClientModal()" style="font-size:13px">Cancel</button>
        <button class="btn bg" onclick="saveClient()" style="font-size:13px">💾 Save Herd</button>
      </div>
    </div>
  </div>
  `;
}

/* ── Client modal ── */
let editClientId = null;
function openClientModal() {
  editClientId = null;
  document.getElementById("cmTitle").textContent = "Register New Herd";
  [
    "cm_herdName",
    "cm_herdCode",
    "cm_established",
    "cm_name",
    "cm_phone",
    "cm_email",
    "cm_district",
    "cm_province",
    "cm_address",
    "cm_notes",
  ].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  const bf = document.getElementById("cm_breedFocus");
  if (bf) bf.value = "Holstein";
  (function () {
    const provEl = document.getElementById("cm_province");
    const distEl = document.getElementById("cm_district");
    if (provEl)
      provEl.innerHTML =
        '<option value="">— Select Province —</option>' +
        getProvincesAF()
          .map(p => `<option>${p}</option>`)
          .join("");
    if (distEl) distEl.innerHTML = '<option value="">— Select District —</option>';
  })();
  document.getElementById("clientModal").style.display = "flex";
}
function editClientModal(id) {
  const o = ownerById(id);
  if (!o) return;
  editClientId = id;
  document.getElementById("cmTitle").textContent = "Edit Herd — " + (o.herdName || o.name);
  const s = (el, v) => {
    const e = document.getElementById(el);
    if (e) e.value = v || "";
  };
  s("cm_herdName", o.herdName || o.name);
  s("cm_herdCode", o.herdCode);
  s("cm_established", o.established);
  const bf = document.getElementById("cm_breedFocus");
  if (bf) bf.value = o.breedFocus || "Holstein";
  s("cm_name", o.name);
  s("cm_phone", o.phone);
  s("cm_email", o.email);
  // Province + district for herd modal
  (function () {
    const provEl = document.getElementById("cm_province");
    const distEl = document.getElementById("cm_district");
    const provs = getProvincesAF();
    if (provEl)
      provEl.innerHTML =
        '<option value="">— Select Province —</option>' +
        provs.map(p => `<option${p === o.province ? " selected" : ""}>${p}</option>`).join("");
    const dists = o.province ? getDistrictsAF(o.province) : [];
    if (distEl)
      distEl.innerHTML =
        '<option value="">— Select District —</option>' +
        dists.map(d => `<option${d === o.district ? " selected" : ""}>${d}</option>`).join("");
  })();
  s("cm_address", o.address);
  s("cm_notes", o.notes);
  document.getElementById("clientModal").style.display = "flex";
}
function closeClientModal() {
  document.getElementById("clientModal").style.display = "none";
}
function saveClient() {
  const herdName = document.getElementById("cm_herdName").value.trim();
  const name = document.getElementById("cm_name").value.trim();
  if (!herdName && !name) {
    alert("Herd name or owner name is required.");
    return;
  }
  const g = id => {
    const e = document.getElementById(id);
    return e ? e.value.trim() : "";
  };
  const data = {
    herdName: herdName || name,
    herdCode: g("cm_herdCode"),
    breedFocus: g("cm_breedFocus") || "Holstein",
    established: g("cm_established"),
    name: name || herdName,
    phone: g("cm_phone"),
    email: g("cm_email"),
    district: g("cm_district"),
    province: g("cm_province"),
    address: g("cm_address"),
    notes: g("cm_notes"),
  };
  if (editClientId) {
    Object.assign(ownerById(editClientId), data);
  } else {
    data.id = "ow" + Date.now();
    owners.push(data);
  }
  saveOwners();
  closeClientModal();
  renderClientsPage();
  renderOwnerBar();
  populateOwnerSelect("");
  showToast(editClientId ? "Herd updated ✓" : "Herd registered ✓");
}
function deleteOwner(id) {
  const cnt = animals.filter(a => a.ownerId === id).length;
  if (!confirm(`Delete this client?\n${cnt} animal(s) will become unassigned.`)) return;
  owners = owners.filter(o => o.id !== id);
  animals.forEach(a => {
    if (a.ownerId === id) a.ownerId = "";
  });
  if (session.ownerId === id) {
    session = { role: "admin", ownerId: null };
    saveSession();
  }
  saveOwners();
  persist();
  renderClientsPage();
  renderOwnerBar();
  renderSB();
  showToast("Client deleted");
}

function switchSession(role, ownerId) {
  // Admin-only mode: just filter sidebar by that herd
  ownerFilter = ownerId || "";
  renderOwnerBar();
  renderSB();
  const first = ownerId ? animals.find(a => a.ownerId === ownerId) : null;
  if (first) {
    selAnimal(first.id);
  }
  goPage("herd");
  showToast(
    ownerId ? "Filtering: " + (ownerById(ownerId) || {}).herdName + " herd" : "Showing all herds"
  );
}

/* ── Multi-dimension filter ── */
function clearClientFilters() {
  ["cf_breed", "cf_prov", "cf_owner", "cf_sire", "cf_gen", "cf_sex"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  renderFilteredHerd();
}
function renderFilteredHerd(returnOnly) {
  const breed = (document.getElementById("cf_breed") || {}).value || "";
  const prov = (document.getElementById("cf_prov") || {}).value || "";
  const ownr = (document.getElementById("cf_owner") || {}).value || "";
  const sire = (document.getElementById("cf_sire") || {}).value || "";
  const gen = (document.getElementById("cf_gen") || {}).value || "";

  const sex = (document.getElementById("cf_sex") || {}).value || "";
  const filtered = animals.filter(a => {
    if (breed && a.breed !== breed) return false;
    if (prov && a.province !== prov) return false;
    if (ownr && a.ownerId !== ownr) return false;
    if (sire && a.sireLinkedId !== sire) return false;
    if (sex && a.sex !== sex) return false;
    if (gen) {
      const gg = getGenInfo(calcBreedComp(a));
      if (gg.gen !== gen) return false;
    }
    return true;
  });

  const html =
    filtered.length === 0
      ? `<div style="font-family:sans-serif;font-size:12px;color:var(--tl);text-align:center;padding:20px">No animals match the selected filters.</div>`
      : `<div style="margin-bottom:8px;font-family:sans-serif;font-size:11px;color:var(--tm);font-weight:700">${filtered.length} animal(s) found</div>
       <div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1vw,12px);min-width:560px">
         <thead><tr style="background:var(--gp)">
           <th style="text-align:left;padding:6px 9px">Animal</th>
           <th style="padding:6px 9px">Ear Tag</th>
           <th style="padding:6px 9px">Breed</th>
           <th style="padding:6px 9px">Generation</th>
           <th style="padding:6px 9px">Owner</th>
           <th style="padding:6px 9px">Province</th>
           <th style="padding:6px 9px">Sire</th>
           ${filtered.some(a => a.milk) ? `<th style="padding:6px 9px">Milk 305d</th>` : ""}
         </tr></thead>
         <tbody>
         ${filtered
           .map((a, i) => {
             const ow = ownerById(a.ownerId);
             const gc = calcBreedComp(a);
             const gg = getGenInfo(gc);
             return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
             <td style="padding:6px 9px;font-weight:bold;cursor:pointer;color:var(--gm)" onclick="selAnimal('${a.id}');goPage('herd')">${a.name}<div style="font-size:9px;color:var(--tl)">${a.regId || "—"}</div></td>
             <td style="text-align:center;padding:6px 9px">${a.earTag || "—"}</td>
             <td style="text-align:center;padding:6px 9px"><span class="tag ${a.breed === "Holstein" ? "tgh" : a.breed === "Montbeliard" ? "tgm" : "tgl"}">${a.breed}</span></td>
             <td style="text-align:center;padding:6px 9px"><span style="background:${gg.bg};color:${gg.color};padding:2px 7px;border-radius:8px;font-weight:700;font-size:10px">${gg.gen}</span></td>
             <td style="padding:6px 9px">${ow ? ow.herdName || ow.name : "—"}<div style='font-size:9px;color:var(--tl)'>${ow ? ow.name : ""}</div></td>
             <td style="padding:6px 9px">${a.province || "—"}<div style="font-size:9px;color:var(--tl)">${a.district || ""}</div></td>
             <td style="padding:6px 9px;font-size:10px">${a.sireName || "—"}</td>
             ${filtered.some(x => x.milk) ? `<td style="text-align:center;padding:6px 9px">${a.milk ? fmtN(a.milk) + " kg" : "—"}</td>` : ""}
           </tr>`;
           })
           .join("")}
         </tbody>
       </table></div>`;

  if (returnOnly) return html;
  const el = document.getElementById("cf_result");
  if (el) el.innerHTML = html;
  return html;
}

function renderBreedAvgTable() {
  const entries = Object.entries(BREED_AVG);
  if (!entries.length)
    return `<div style="font-family:sans-serif;font-size:12px;color:var(--tl)">No breed averages entered yet. Click + Add / Edit to add.</div>`;

  // Calculate herd averages per breed for comparison
  const herdByBreed = {};
  animals.forEach(a => {
    const res = animalF(a);
    if (!res || res.F <= 0) return;
    const k = a.breed;
    if (!herdByBreed[k]) herdByBreed[k] = { sum: 0, cnt: 0 };
    herdByBreed[k].sum += res.F;
    herdByBreed[k].cnt++;
  });

  return `
  <div style="overflow-x:auto">
  <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:12px">
    <thead><tr style="background:var(--gp)">
      <th style="text-align:left;padding:7px 10px">Breed</th>
      <th style="text-align:center;padding:7px 10px">Population Avg F%</th>
      <th style="text-align:center;padding:7px 10px">Annual Trend</th>
      <th style="text-align:center;padding:7px 10px">Source</th>
      <th style="text-align:center;padding:7px 10px">Your Herd Avg</th>
      <th style="text-align:center;padding:7px 10px">Difference</th>
      <th style="padding:7px 10px"></th>
    </tr></thead>
    <tbody>
    ${entries
      .map(([breed, data], i) => {
        // Find matching herd breed
        const hk = Object.keys(herdByBreed).find(k =>
          breed.toLowerCase().includes(k.toLowerCase())
        );
        const hAvg = hk && herdByBreed[hk].cnt ? herdByBreed[hk].sum / herdByBreed[hk].cnt : null;
        const diff = hAvg !== null ? hAvg - data.avg : null;
        return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:7px 10px;font-weight:bold">${breed}</td>
        <td style="text-align:center;padding:7px 10px">${fBadge(data.avg)}</td>
        <td style="text-align:center;padding:7px 10px;color:var(--tl)">${data.trend || "—"}</td>
        <td style="text-align:center;padding:7px 10px;color:var(--tl);font-size:10px">${data.source || "—"}</td>
        <td style="text-align:center;padding:7px 10px">${hAvg !== null ? fBadge(hAvg) : '<span style="color:#aaa;font-size:10px">No data</span>'}</td>
        <td style="text-align:center;padding:7px 10px;font-weight:600;font-size:12px">
          ${diff !== null ? `<span style="color:${diff > 0 ? "#c0392b" : "#16a34a"}">${diff > 0 ? "+" : ""}${diff.toFixed(2)}%</span>` : "—"}
        </td>
        <td style="text-align:center;padding:7px 10px">
          <button class="btn bd bsm" onclick="editBreedAvg('${breed.replace(/'/g, "\\'")}')">✏️</button>
          <button class="btn bd bsm" onclick="deleteBreedAvg('${breed.replace(/'/g, "\\'")}')">🗑</button>
        </td>
      </tr>`;
      })
      .join("")}
    </tbody>
  </table>
  </div>`;
}

/* ── Predictor update ── */
function bpUpdate() {
  // legacy alias — now handled by bpUpdateResults
  bpUpdateResults();
}

function bpManualSireChange() {
  const v = (document.getElementById("bp_extSire") || {}).value || "";
  const val = v.trim();
  if (val) {
    bpState.sireId = "";
    // Clear register sire if set
    const sq = document.getElementById("bp_sireQ");
    if (sq) sq.value = "";
    const ss = document.getElementById("bp_sireSel");
    if (ss) ss.style.display = "none";
    bpState.manualSire = {
      name: val,
      regId: "",
      sireLinkedId: "",
      damLinkedId: "",
      sireName: (document.getElementById("bp_pgs") || {}).value || "",
      sireId: (document.getElementById("bp_pgsid") || {}).value || "",
      damName: (document.getElementById("bp_pgd") || {}).value || "",
      damId: (document.getElementById("bp_pgdid") || {}).value || "",
      ssName: "",
      sdName: "",
      dsName: "",
      ddName: "",
    };
    const mp = document.getElementById("bp_manualPed");
    if (mp) mp.style.display = "block";
  } else {
    bpState.manualSire = null;
    const mp = document.getElementById("bp_manualPed");
    if (mp) mp.style.display = "none";
  }
  bpUpdateResults();
}
function bpManualPedChange() {
  if (bpState.manualSire) {
    bpState.manualSire.sireName = document.getElementById("bp_pgs").value;
    bpState.manualSire.sireId = document.getElementById("bp_pgsid").value;
    bpState.manualSire.damName = document.getElementById("bp_pgd").value;
    bpState.manualSire.damId = document.getElementById("bp_pgdid").value;
    bpUpdate();
  }
}

/* ── Breed avg CRUD ── */
function showAddBreedAvg() {
  document.getElementById("ba_breed").value = "";
  document.getElementById("ba_avg").value = "";
  document.getElementById("ba_trend").value = "";
  document.getElementById("ba_source").value = "";
  document.getElementById("bp_breedForm").style.display = "block";
}
function editBreedAvg(breed) {
  const d = BREED_AVG[breed];
  if (!d) return;
  document.getElementById("ba_breed").value = breed;
  document.getElementById("ba_avg").value = d.avg;
  document.getElementById("ba_trend").value = d.trend || "";
  document.getElementById("ba_source").value = d.source || "";
  document.getElementById("bp_breedForm").style.display = "block";
}
function saveBreedAvgEntry() {
  const b = document.getElementById("ba_breed").value.trim();
  const a = parseFloat(document.getElementById("ba_avg").value);
  if (!b || isNaN(a)) {
    alert("Breed name and average % are required.");
    return;
  }
  BREED_AVG[b] = {
    avg: a,
    trend: document.getElementById("ba_trend").value.trim(),
    source: document.getElementById("ba_source").value.trim(),
  };
  saveBreedAvg();
  document.getElementById("bp_breedForm").style.display = "none";
  document.getElementById("bp_breedTable").innerHTML = renderBreedAvgTable();
  showToast("Breed average saved ✓");
}
function deleteBreedAvg(breed) {
  if (!confirm("Delete breed average for " + breed + "?")) return;
  delete BREED_AVG[breed];
  saveBreedAvg();
  document.getElementById("bp_breedTable").innerHTML = renderBreedAvgTable();
}

/* ════════════════════════════════════════════════════════════════
   REPORTS & EXPORT ENGINE
   Supports: Excel (.xlsx via SheetJS CDN), PDF (print window), CSV
   Reports: Herd Register, Production, Pedigree, Inbreeding,
            Crossbreeding, By Owner/Herd, By Province, Sire Progeny
   ════════════════════════════════════════════════════════════════ */

/* ── State for current report filters ── */
const RPT = {
  breed: "",
  province: "",
  ownerId: "",
  sireId: "",
  generation: "",
  sex: "",
  dateFrom: "",
  dateTo: "",
  type: "herd",
};

/* ── Load SheetJS from CDN (lazy) ── */
function loadXLSX(cb) {
  if (window.XLSX) {
    cb();
    return;
  }
  const s = document.createElement("script");
  s.src = "https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js";
  s.onload = cb;
  s.onerror = () => {
    const st = document.getElementById("rpt-status");
    if (st) {
      st.textContent = "❌ Could not load Excel library (needs internet). Try PDF export instead.";
      st.className = "bkst er";
    }
  };
  document.head.appendChild(s);
}

/* ── Get filtered animals ── */
function getRptAnimals() {
  return animals.filter(a => {
    if (!a || !a.id) return false;
    if (RPT.breed && a.breed !== RPT.breed) return false;
    if (RPT.province && a.province !== RPT.province) return false;
    if (RPT.ownerId && a.ownerId !== RPT.ownerId) return false;
    if (RPT.sireId && a.sireLinkedId !== RPT.sireId) return false;
    if (RPT.sex && a.sex !== RPT.sex) return false;
    if (RPT.generation) {
      const gi = getGenInfo(calcBreedComp(a));
      if (gi.gen !== RPT.generation) return false;
    }
    if (RPT.dateFrom && a.dob && a.dob < RPT.dateFrom) return false;
    if (RPT.dateTo && a.dob && a.dob > RPT.dateTo) return false;
    return true;
  });
}

/* ── Report definitions ── */
const REPORT_TYPES = [
  {
    id: "herd",
    icon: "📋",
    title: "Full Herd Register",
    desc: "All animals with identity, ear tag, owner, herd, province, breed",
    color: "#1a4a2e",
    bg: "#e8f5ec",
    cols: [
      "Name",
      "Reg ID",
      "Ear Tag",
      "Sex",
      "Breed",
      "DOB",
      "Lactation",
      "Color",
      "Owner",
      "Herd",
      "Province",
      "District",
      "Notes",
    ],
    row: a => {
      const ow = anyOwnerById(a.ownerId) || {};
      return [
        a.name,
        a.regId || "",
        a.earTag || "",
        a.sex || "",
        a.breed || "",
        a.dob || "",
        a.lact || "",
        a.color || "",
        ow.name || "",
        ow.herdName || ow.name || "",
        a.province || "",
        a.district || "",
        a.notes || "",
      ];
    },
  },
  {
    id: "production",
    icon: "🥛",
    title: "Production Report (305-Day)",
    desc: "Cow production records: milk, fat%, protein%, fat kg, protein kg, peak, DIM, SCS",
    color: "#1e40af",
    bg: "#dbeafe",
    cols: [
      "Name",
      "Reg ID",
      "Ear Tag",
      "Breed",
      "Lactation",
      "Calving Date",
      "Milk 305d (kg)",
      "Fat %",
      "Protein %",
      "Fat kg",
      "Protein kg",
      "Peak (kg/d)",
      "DIM",
      "SCS",
      "Owner",
      "Province",
    ],
    row: a => {
      if (a.sex !== "Female") return null;
      const r = calc(a.milk, a.fat, a.prot);
      const ow = anyOwnerById(a.ownerId) || {};
      return [
        a.name,
        a.regId || "",
        a.earTag || "",
        a.breed || "",
        a.lact || "",
        a.calving || "",
        a.milk || "",
        a.fat || "",
        a.prot || "",
        r.fatKg !== null ? r.fatKg : "",
        r.protKg !== null ? r.protKg : "",
        a.peak || "",
        a.dim || "",
        a.scs || "",
        ow.name || "",
        a.province || "",
      ];
    },
  },
  {
    id: "pedigree",
    icon: "🌳",
    title: "Pedigree Summary",
    desc: "3-generation pedigree: sire, dam, grandparents with IDs and breeds",
    color: "#854d0e",
    bg: "#fef3c7",
    cols: [
      "Animal",
      "Reg ID",
      "Breed",
      "Sex",
      "DOB",
      "Sire Name",
      "Sire ID",
      "Sire Breed",
      "PGS Name",
      "PGS ID",
      "PGD Name",
      "PGD ID",
      "PGD Milk",
      "Dam Name",
      "Dam ID",
      "Dam Breed",
      "Dam Milk",
      "MGS Name",
      "MGS ID",
      "MGD Name",
      "MGD ID",
      "MGD Milk",
      "Owner",
      "Province",
    ],
    row: a => {
      const ow = anyOwnerById(a.ownerId) || {};
      return [
        a.name,
        a.regId || "",
        a.breed || "",
        a.sex || "",
        a.dob || "",
        a.sireName || "",
        a.sireId || "",
        a.sireBreed || "",
        a.ssName || "",
        a.ssId || "",
        a.sdName || "",
        a.sdId || "",
        a.sdMilk || "",
        a.damName || "",
        a.damId || "",
        a.damBreed || "",
        a.damMilk || "",
        a.dsName || "",
        a.dsId || "",
        a.ddName || "",
        a.ddId || "",
        a.ddMilk || "",
        ow.name || "",
        a.province || "",
      ];
    },
  },
  {
    id: "inbreeding",
    icon: "🧬",
    title: "Inbreeding Analysis",
    desc: "Inbreeding coefficient (F%) per animal with common ancestors and risk level",
    color: "#6b21a8",
    bg: "#f3e8ff",
    cols: [
      "Animal",
      "Reg ID",
      "Breed",
      "Sex",
      "DOB",
      "Inbreeding F%",
      "Risk Level",
      "Common Ancestors",
      "vs Breed Average",
      "Owner",
      "Province",
    ],
    row: a => {
      const res = animalF(a);
      const ow = anyOwnerById(a.ownerId) || {};
      const fc = res && res.F > 0 ? fColor(res.F) : { label: "Unknown" };
      const breedKey = Object.keys(BREED_AVG).find(k =>
        k.toLowerCase().includes(a.breed.toLowerCase())
      );
      const bAvg = breedKey ? BREED_AVG[breedKey].avg : null;
      const vsAvg =
        bAvg !== null && res && res.F > 0 ? (res.F > bAvg ? "Above avg" : "Below avg") : "—";
      return [
        a.name,
        a.regId || "",
        a.breed || "",
        a.sex || "",
        a.dob || "",
        res && res.F > 0 ? res.F.toFixed(2) : "Not calculable",
        res && res.F > 0 ? fc.label : "—",
        res && res.common.length ? res.common.map(c => c.label).join("; ") : "None",
        vsAvg,
        ow.name || "",
        a.province || "",
      ];
    },
  },
  {
    id: "crossbreed",
    icon: "🔀",
    title: "Crossbreeding & Generation",
    desc: "Breed composition, generation (F1-F6), purity % per animal",
    color: "#065f46",
    bg: "#d1fae5",
    cols: [
      "Animal",
      "Reg ID",
      "Breed",
      "Sex",
      "DOB",
      "Generation",
      "Purity %",
      "Holstein %",
      "Montbéliarde %",
      "Local %",
      "Other %",
      "Sire",
      "Dam",
      "Owner",
      "Province",
    ],
    row: a => {
      const comp = calcBreedComp(a);
      const gi = getGenInfo(comp);
      const ow = anyOwnerById(a.ownerId) || {};
      return [
        a.name,
        a.regId || "",
        a.breed || "",
        a.sex || "",
        a.dob || "",
        gi.gen,
        gi.pct.toFixed(2),
        (comp["Holstein"] || 0).toFixed(2),
        (comp["Montbeliard"] || 0).toFixed(2),
        (comp["Local"] || 0).toFixed(2),
        Object.entries(comp)
          .filter(([k]) => !["Holstein", "Montbeliard", "Local"].includes(k))
          .map(([k, v]) => k + ":" + v.toFixed(1))
          .join("; "),
        a.sireName || "",
        a.damName || "",
        ow.name || "",
        a.province || "",
      ];
    },
  },
  {
    id: "sire_progeny",
    icon: "🐂",
    title: "Sire Progeny Report",
    desc: "All offspring per breeding sire with performance data",
    color: "#1e40af",
    bg: "#dbeafe",
    cols: [
      "Sire Name",
      "Sire ID",
      "Sire Breed",
      "Offspring Name",
      "Offspring ID",
      "Offspring Sex",
      "Offspring Breed",
      "DOB",
      "Generation",
      "Milk 305d (kg)",
      "Fat %",
      "Prot %",
      "Owner",
      "Province",
    ],
    row: a => {
      if (a.sex !== "Male" || (a.role !== "Sire" && a.role)) return null;
      const offspr = offspring(a.id);
      if (!offspr.length)
        return [
          [
            a.name,
            a.regId || "",
            a.breed || "",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
            "—",
          ],
        ];
      return offspr.map(o => {
        const gi = getGenInfo(calcBreedComp(o));
        const ow = anyOwnerById(o.ownerId) || {};
        return [
          a.name,
          a.regId || "",
          a.breed || "",
          o.name,
          o.regId || "",
          o.sex || "",
          o.breed || "",
          o.dob || "",
          gi.gen,
          o.milk || "",
          o.fat || "",
          o.prot || "",
          ow.name || "",
          o.province || "",
        ];
      });
    },
    multirow: true,
  },
  {
    id: "province",
    icon: "📍",
    title: "Province Report",
    desc: "Animals grouped by province with breed and generation breakdown",
    color: "#b45309",
    bg: "#fef3c7",
    cols: [
      "Province",
      "District",
      "Animal",
      "Reg ID",
      "Ear Tag",
      "Breed",
      "Sex",
      "Generation",
      "Milk 305d",
      "Owner",
      "Herd Code",
    ],
    row: a => {
      const ow = anyOwnerById(a.ownerId) || {};
      const gi = getGenInfo(calcBreedComp(a));
      return [
        a.province || "—",
        a.district || "—",
        a.name,
        a.regId || "",
        a.earTag || "",
        a.breed || "",
        a.sex || "",
        gi.gen,
        a.milk || "",
        ow.name || "",
        ow.herdCode || "",
      ];
    },
  },
  {
    id: "owner",
    icon: "👤",
    title: "Owner / Herd Report",
    desc: "Animals grouped by owner and herd with contact details",
    color: "#4c1d95",
    bg: "#f3e8ff",
    cols: [
      "Owner Name",
      "Phone",
      "Province",
      "Herd / Type",
      "Animal",
      "Reg ID",
      "Ear Tag",
      "Breed",
      "Sex",
      "Generation",
      "Milk 305d",
      "DOB",
    ],
    row: a => {
      const ow = anyOwnerById(a.ownerId) || {};
      const isIndiv = ow.id && ow.id.startsWith("indiv_");
      const gi = getGenInfo(calcBreedComp(a));
      return [
        ow.name || "Unassigned",
        ow.phone || "",
        a.province || ow.province || "",
        isIndiv ? "Individual" : ow.herdName || ow.name || "—",
        a.name,
        a.regId || "",
        a.earTag || "",
        a.breed || "",
        a.sex || "",
        gi.gen,
        a.milk || "",
        a.dob || "",
      ];
    },
  },
];

/* ══════════════════════════════════════════════════════
   RENDER REPORTS PAGE
══════════════════════════════════════════════════════ */
function renderReportsPage() {
  const el = document.getElementById("pg-reports");
  if (!el) return;

  const allSires = animals.filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role));
  const allOwners = [...owners, ...indivOwners];
  const allProvs = getProvincesAF();
  const usedProvs = new Set(animals.map(a => a.province).filter(Boolean));

  el.innerHTML = `
  <!-- HEADER -->
  <div style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);border-radius:var(--rl);padding:14px 18px;color:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact">
    <div style="font-size:clamp(14px,2vw,18px);font-weight:bold">📊 Reports &amp; Export</div>
    <div style="font-size:clamp(9px,1vw,11px);opacity:.8;margin-top:3px">Export pedigree data to Excel or PDF · Filter by breed, province, owner, generation</div>
  </div>

  <!-- FILTER PANEL -->
  <div class="card">
    <div class="ch"><h2>🔍 Report Filters</h2>
      <button class="btn bd bsm" onclick="clearRptFilters()">✕ Clear All</button>
    </div>
    <div class="cb">
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:10px;margin-bottom:10px">
        <div class="fr">
          <label>🐄 Breed</label>
          <select id="rpt_breed" onchange="RPT.breed=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All Breeds</option>
            <option value="Holstein">🖤 Holstein</option>
            <option value="Montbeliard">🟠 Montbéliarde</option>
            <option value="Local">🌿 Local / Indigenous</option>
            <option value="Crossbred">🔀 Crossbred</option>
          </select>
        </div>
        <div class="fr">
          <label>📍 Province</label>
          <select id="rpt_prov" onchange="RPT.province=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All Provinces</option>
            ${allProvs.map(p => `<option value="${p}"${usedProvs.has(p) ? "" : ' style="color:#ccc"'}>${p}</option>`).join("")}
          </select>
        </div>
        <div class="fr">
          <label>👤 Owner / Herd</label>
          <select id="rpt_owner" onchange="RPT.ownerId=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All Owners</option>
            <optgroup label="Registered Herds">${owners.map(o => `<option value="${o.id}">[Herd] ${o.herdName || o.name}</option>`).join("")}</optgroup>
            <optgroup label="Individual Owners">${indivOwners.map(o => `<option value="${o.id}">[Indiv] ${o.name}</option>`).join("")}</optgroup>
          </select>
        </div>
        <div class="fr">
          <label>🐂 Sire</label>
          <select id="rpt_sire" onchange="RPT.sireId=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All Sires</option>
            ${allSires.map(s => `<option value="${s.id}">${s.name} (${s.regId || "—"})</option>`).join("")}
          </select>
        </div>
        <div class="fr">
          <label>🔀 Generation</label>
          <select id="rpt_gen" onchange="RPT.generation=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All Generations</option>
            ${["Purebred", "F1", "F2", "F3", "F4", "F5", "F6", "High Grade", "Cross"].map(g => `<option>${g}</option>`).join("")}
          </select>
        </div>
        <div class="fr">
          <label>⚧ Sex</label>
          <select id="rpt_sex" onchange="RPT.sex=this.value;updateRptPreview()" style="font-size:12px">
            <option value="">All</option>
            <option value="Female">🐄 Female (Cows)</option>
            <option value="Male">🐂 Male (Bulls)</option>
          </select>
        </div>
        <div class="fr">
          <label>📅 Born From</label>
          <input type="date" id="rpt_from" onchange="RPT.dateFrom=this.value;updateRptPreview()" style="font-size:12px">
        </div>
        <div class="fr">
          <label>📅 Born To</label>
          <input type="date" id="rpt_to" onchange="RPT.dateTo=this.value;updateRptPreview()" style="font-size:12px">
        </div>
      </div>
      <!-- Active filter summary -->
      <div id="rpt_summary" style="font-family:sans-serif;font-size:11px;color:var(--tm);background:var(--gp);padding:7px 12px;border-radius:var(--r)">
        Filters: <b>None</b> — showing all <b>${animals.length}</b> animals
      </div>
    </div>
  </div>

  <!-- REPORT TYPE CARDS -->
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px">
    ${REPORT_TYPES.map(rt => buildRptCard(rt)).join("")}
  </div>

  <!-- STATUS -->
  <div id="rpt-status" class="bkst" style="text-align:center;padding:6px;font-size:12px"></div>
  `;
}

function buildRptCard(rt) {
  return `<div class="card" style="border-top:3px solid ${rt.color}">
    <div class="ch" style="background:${rt.bg}">
      <div>
        <h2 style="color:${rt.color};font-size:clamp(12px,1.3vw,14px)">${rt.icon} ${rt.title}</h2>
        <div style="font-family:sans-serif;font-size:10px;color:var(--tm);margin-top:2px">${rt.desc}</div>
      </div>
    </div>
    <div class="cb" style="padding:12px 14px">
      <div style="font-family:sans-serif;font-size:11px;color:var(--tl);margin-bottom:9px">
        ${rt.cols.length} columns · Exports applied filters
      </div>
      <div style="display:flex;gap:7px;flex-wrap:wrap">
        <button class="btn bg bsm" style="background:${rt.color};font-size:clamp(10px,1vw,12px)" onclick="doExcelExport('${rt.id}')">
          📊 Export to Excel
        </button>
        <button class="btn bw bsm" style="border:1px solid ${rt.color};color:${rt.color};font-size:clamp(10px,1vw,12px)" onclick="doPdfExport('${rt.id}')">
          📄 Export to PDF
        </button>
        <button class="btn bd bsm" style="font-size:clamp(10px,1vw,12px)" onclick="doCsvExport('${rt.id}')">
          📋 CSV
        </button>
      </div>
    </div>
  </div>`;
}

function updateRptPreview() {
  const filtered = getRptAnimals();
  const parts = [];
  if (RPT.breed) parts.push("Breed: " + RPT.breed);
  if (RPT.province) parts.push("Province: " + RPT.province);
  if (RPT.ownerId) parts.push("Owner: " + ((anyOwnerById(RPT.ownerId) || {}).name || RPT.ownerId));
  if (RPT.sireId) parts.push("Sire: " + ((byId(RPT.sireId) || {}).name || RPT.sireId));
  if (RPT.generation) parts.push("Gen: " + RPT.generation);
  if (RPT.sex) parts.push("Sex: " + RPT.sex);
  if (RPT.dateFrom) parts.push("From: " + RPT.dateFrom);
  if (RPT.dateTo) parts.push("To: " + RPT.dateTo);
  const el = document.getElementById("rpt_summary");
  if (el)
    el.innerHTML = parts.length
      ? `Filters: <b>${parts.join(" · ")}</b> — <b>${filtered.length}</b> animal${filtered.length !== 1 ? "s" : ""} match`
      : `Filters: <b>None</b> — showing all <b>${filtered.length}</b> animals`;
}

function clearRptFilters() {
  Object.assign(RPT, {
    breed: "",
    province: "",
    ownerId: "",
    sireId: "",
    generation: "",
    sex: "",
    dateFrom: "",
    dateTo: "",
  });
  ["rpt_breed", "rpt_prov", "rpt_owner", "rpt_sire", "rpt_gen", "rpt_sex"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  ["rpt_from", "rpt_to"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  updateRptPreview();
}

/* ── Build rows for a report ── */
function buildRows(rt) {
  const filtered = getRptAnimals();
  const rows = [];
  filtered.forEach(a => {
    if (rt.multirow) {
      const r = rt.row(a);
      if (r && Array.isArray(r[0])) r.forEach(row => rows.push(row));
      else if (r) r.forEach(row => rows.push(row));
    } else {
      const r = rt.row(a);
      if (r) rows.push(r);
    }
  });
  return rows;
}

/* ════════════════
   EXCEL EXPORT
════════════════ */
function doExcelExport(typeId) {
  const rt = REPORT_TYPES.find(r => r.id === typeId);
  if (!rt) return;
  const st = document.getElementById("rpt-status");
  if (st) {
    st.textContent = "Loading Excel library…";
    st.className = "bkst";
  }
  loadXLSX(() => {
    try {
      const rows = buildRows(rt);
      if (!rows.length) {
        if (st) {
          st.textContent = "⚠️ No data matches the current filters.";
          st.className = "bkst er";
        }
        return;
      }
      const wb = XLSX.utils.book_new();
      // Main data sheet
      const wsData = [rt.cols, ...rows];
      const ws = XLSX.utils.aoa_to_sheet(wsData);
      // Style header row (bold — basic)
      const headerStyle = {
        font: { bold: true },
        fill: { fgColor: { rgb: "1a4a2e" } },
        font: { color: { rgb: "FFFFFF" }, bold: true },
      };
      rt.cols.forEach((_, ci) => {
        const cell = XLSX.utils.encode_cell({ r: 0, c: ci });
        if (!ws[cell]) return;
        ws[cell].s = headerStyle;
      });
      // Column widths
      ws["!cols"] = rt.cols.map(h => ({ wch: Math.max(h.length + 2, 14) }));
      XLSX.utils.book_append_sheet(wb, ws, rt.title.substring(0, 31));

      // Summary sheet
      const sumData = [
        ["Report", "Larspeenghar Pedigree Manager"],
        ["Report Type", rt.title],
        ["Generated", new Date().toLocaleString("en-GB")],
        ["Total Records", rows.length],
        [],
        ["Applied Filters"],
        ["Breed", RPT.breed || "All"],
        ["Province", RPT.province || "All"],
        ["Owner", RPT.ownerId ? (anyOwnerById(RPT.ownerId) || {}).name || RPT.ownerId : "All"],
        ["Sire", RPT.sireId ? (byId(RPT.sireId) || {}).name || RPT.sireId : "All"],
        ["Generation", RPT.generation || "All"],
        ["Sex", RPT.sex || "All"],
        ["Date From", RPT.dateFrom || "—"],
        ["Date To", RPT.dateTo || "—"],
      ];
      const wsSum = XLSX.utils.aoa_to_sheet(sumData);
      wsSum["!cols"] = [{ wch: 18 }, { wch: 40 }];
      XLSX.utils.book_append_sheet(wb, wsSum, "Report Info");

      // Download
      const stamp = new Date().toISOString().slice(0, 10);
      XLSX.writeFile(wb, `larspeenghar_${rt.id}_${stamp}.xlsx`);
      if (st) {
        st.textContent = `✅ Excel exported — ${rows.length} records in ${rt.title}`;
        st.className = "bkst ok";
      }
    } catch (e) {
      if (st) {
        st.textContent = "❌ Export error: " + e.message;
        st.className = "bkst er";
      }
      console.error(e);
    }
  });
}

/* ════════════════
   CSV EXPORT
════════════════ */
function doCsvExport(typeId) {
  const rt = REPORT_TYPES.find(r => r.id === typeId);
  if (!rt) return;
  const rows = buildRows(rt);
  const st = document.getElementById("rpt-status");
  if (!rows.length) {
    if (st) {
      st.textContent = "⚠️ No data matches.";
      st.className = "bkst er";
    }
    return;
  }
  const esc = v => {
    const s = String(v == null ? "" : v);
    return s.includes(",") || s.includes('"') || s.includes("\n")
      ? `"${s.replace(/"/g, '""')}"`
      : s;
  };
  const csv = [[...rt.cols], ...rows].map(row => row.map(esc).join(",")).join("\n");
  const blob = new Blob(["﻿" + csv], { type: "text/csv;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  const stamp = new Date().toISOString().slice(0, 10);
  a.href = url;
  a.download = `larspeenghar_${rt.id}_${stamp}.csv`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  if (st) {
    st.textContent = `✅ CSV exported — ${rows.length} records`;
    st.className = "bkst ok";
  }
}

/* ════════════════
   PDF EXPORT
════════════════ */
function doPdfExport(typeId) {
  const rt = REPORT_TYPES.find(r => r.id === typeId);
  if (!rt) return;
  const rows = buildRows(rt);
  const st = document.getElementById("rpt-status");
  if (!rows.length) {
    if (st) {
      st.textContent = "⚠️ No data matches.";
      st.className = "bkst er";
    }
    return;
  }

  const today = new Date().toLocaleDateString("en-GB", {
    day: "2-digit",
    month: "long",
    year: "numeric",
  });
  const stamp = new Date().toLocaleString("en-GB");
  const filterDesc =
    [
      RPT.breed ? "Breed: " + RPT.breed : "",
      RPT.province ? "Province: " + RPT.province : "",
      RPT.ownerId ? "Owner: " + ((anyOwnerById(RPT.ownerId) || {}).name || "") : "",
      RPT.generation ? "Generation: " + RPT.generation : "",
      RPT.sex ? "Sex: " + RPT.sex : "",
    ]
      .filter(Boolean)
      .join(" · ") || "No filters applied — all records";

  // Build table rows HTML
  const tHead = rt.cols.map(c => `<th>${c}</th>`).join("");
  const tBody = rows
    .map(
      (row, i) => `<tr class="${i % 2 ? "alt" : ""}">
    ${row.map(v => `<td>${v !== null && v !== undefined && v !== "" ? String(v).replace(/&/g, "&amp;").replace(/</g, "&lt;") : "—"}</td>`).join("")}
  </tr>`
    )
    .join("");

  const html = `<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>${rt.title} — Larspeenghar</title>
<style>
  @page{size:A4 landscape;margin:10mm 12mm}
  *{box-sizing:border-box;margin:0;padding:0}
  body{font-family:'Segoe UI',Arial,sans-serif;font-size:9pt;color:#1a2e1a;background:#fff}
  .header{background:#1a4a2e;color:#fff;padding:10px 14px;display:flex;justify-content:space-between;align-items:center;border-radius:4px 4px 0 0;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .header h1{font-size:13pt;font-weight:bold}
  .header .sub{font-size:8pt;opacity:.8;margin-top:2px}
  .subheader{background:#2d7a4f;color:#fff;padding:6px 14px;display:flex;justify-content:space-between;font-size:9pt;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .meta{padding:7px 14px;background:#f0fdf4;border-bottom:1px solid #c8d8c8;font-size:8pt;display:flex;gap:20px;flex-wrap:wrap}
  .meta b{color:#1a4a2e}
  table{width:100%;border-collapse:collapse;font-size:7.5pt;margin-top:0}
  th{background:#1a4a2e;color:#fff;padding:5px 6px;text-align:left;font-weight:600;-webkit-print-color-adjust:exact;print-color-adjust:exact;white-space:nowrap}
  td{padding:4px 6px;border-bottom:1px solid #e0e8e0;vertical-align:top}
  tr.alt td{background:#f5faf5;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  tr:hover td{background:#e8f5ec}
  .footer{background:#1a4a2e;color:#fff;padding:5px 14px;font-size:7pt;display:flex;justify-content:space-between;margin-top:8px;border-radius:0 0 4px 4px;-webkit-print-color-adjust:exact;print-color-adjust:exact}
  .no-print{position:fixed;top:10px;right:10px;display:flex;gap:8px;z-index:100}
  .btn{padding:8px 16px;border:none;border-radius:6px;cursor:pointer;font-size:11pt;font-weight:600}
  .btn-print{background:#1a4a2e;color:#fff}
  .btn-close{background:#f3f4f6;color:#333}
  @media print{.no-print{display:none!important}}
</style>
</head>
<body>
<div class="no-print">
  <button class="btn btn-print" onclick="window.print()">🖨 Print / Save PDF</button>
  <button class="btn btn-close" onclick="window.close()">✕ Close</button>
</div>
<div class="header">
  <div>
    <div class="sub">🐄 Larspeenghar Livestock and Agriculture Company</div>
    <h1>${rt.icon} ${rt.title}</h1>
  </div>
  <div style="text-align:right;font-size:8pt;opacity:.85">
    <div>Generated: ${stamp}</div>
    <div>${rows.length} record${rows.length !== 1 ? "s" : ""}</div>
  </div>
</div>
<div class="subheader">
  <span><b>Filters:</b> ${filterDesc}</span>
  <span><b>LARSPEENGHAR PEDIGREE SYSTEM</b></span>
</div>
<div class="meta">
  <span><b>Report:</b> ${rt.title}</span>
  <span><b>Records:</b> ${rows.length}</span>
  ${RPT.breed ? `<span><b>Breed:</b> ${RPT.breed}</span>` : ""}
  ${RPT.province ? `<span><b>Province:</b> ${RPT.province}</span>` : ""}
  ${RPT.ownerId ? `<span><b>Owner:</b> ${(anyOwnerById(RPT.ownerId) || {}).name || ""}</span>` : ""}
  ${RPT.generation ? `<span><b>Generation:</b> ${RPT.generation}</span>` : ""}
</div>
<table>
  <thead><tr>${tHead}</tr></thead>
  <tbody>${tBody}</tbody>
</table>
<div class="footer">
  <span>Larspeenghar Livestock and Agriculture Company — Official Pedigree Report</span>
  <span>Printed: ${today}</span>
</div>
</body>
</html>
`;
  const win = window.open("", "_blank", "width=1100,height=800");
  if (!win) {
    alert("Please allow popups to open the PDF preview.");
    return;
  }
  win.document.write(html);
  win.document.close();
  setTimeout(() => win.focus(), 300);
  if (st) {
    st.textContent = `✅ PDF preview opened — ${rows.length} records`;
    st.className = "bkst ok";
  }
}

/* ════════════════════════════════════════════════════════════
   SIRE AUTOCOMPLETE — User portal request form
════════════════════════════════════════════════════════════ */
function upSireSearch(inp) {
  const q = (inp.value || "").toLowerCase().trim();
  const dd = document.getElementById("req_sireDD");
  if (!dd) return;
  let ra = [];
  try {
    ra = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
  } catch (e) {}
  const sires = ra.filter(
    a =>
      a.sex === "Male" &&
      (a.role === "Sire" || !a.role) &&
      (!q ||
        (a.name || "").toLowerCase().includes(q) ||
        (a.regId || "").toLowerCase().includes(q) ||
        (a.earTag || "").toLowerCase().includes(q))
  );
  let html = "";
  if (sires.length) {
    html +=
      '<div style="padding:5px 10px;font-size:9px;font-weight:700;text-transform:uppercase;color:#92400e;background:#fffbeb;border-bottom:1px solid #fde68a">Registered Sires</div>';
    html += sires
      .slice(0, 10)
      .map(a => {
        const n = a.name || "—",
          r = a.regId || "",
          b = a.breed || "",
          bL = b === "Montbeliard" ? "Montbéliarde" : b;
        const ph = a.photo
          ? `<img src="${a.photo}" style="width:26px;height:26px;border-radius:50%;object-fit:cover">`
          : "<span style='font-size:16px'>🐂</span>";
        const nE = n.replace(/'/g, "\\'"),
          rE = r.replace(/'/g, "\\'"),
          bE = b.replace(/'/g, "\\'"),
          eE = (a.earTag || "").replace(/'/g, "\\'");
        return `<div style="padding:9px 12px;cursor:pointer;font-family:sans-serif;font-size:12px;border-bottom:1px solid #f9f5e8;display:flex;align-items:center;gap:8px"
        onmouseover="this.style.background='#fffbeb'" onmouseout="this.style.background='#fff'"
        onclick="upSireSelect('${a.id}','${nE}','${rE}','${bE}','${eE}')">
        ${ph}
        <div><div style="font-weight:700;color:#1a2e1a">${n}</div>
        <div style="font-size:10px;color:#9ca3af">${r} · ${bL}${a.earTag ? " · 🏷 " + a.earTag : ""}</div></div>
        <span style="margin-left:auto;font-size:9px;background:#d1fae5;color:#065f46;padding:1px 6px;border-radius:8px">✓</span>
      </div>`;
      })
      .join("");
  }
  if (q) {
    const esc = (inp.value || "").replace(/'/g, "\\'");
    html += `<div style="padding:8px 12px;cursor:pointer;font-family:sans-serif;font-size:12px;background:#f9f9f9;border-top:1px solid #f0f0f0;display:flex;align-items:center;gap:8px"
      onmouseover="this.style.background='#f3f4f6'" onmouseout="this.style.background='#f9f9f9'"
      onclick="upSireSelectManual('${esc}')">
      <span>✏️</span><div><div style="font-weight:600">Use as manual entry</div>
      <div style="font-size:10px;color:#9ca3af">AI straw code or unlisted sire</div></div>
    </div>`;
  }
  if (!html)
    html =
      '<div style="padding:10px 12px;font-size:11px;color:#9ca3af;font-family:sans-serif">Type a sire name or AI code</div>';
  dd.innerHTML = html;
  dd.style.display = "block";
}
function upSireSelect(id, name, regId, breed, earTag) {
  const inp = document.getElementById("req_sireName"),
    dd = document.getElementById("req_sireDD"),
    sel = document.getElementById("req_sireSelected"),
    lbl = document.getElementById("req_sireSelectedLabel");
  if (inp) {
    inp.value = name + (regId ? " (" + regId + ")" : "");
    inp.dataset.sireId = id;
  }
  if (dd) dd.style.display = "none";
  if (lbl)
    lbl.innerHTML = `✅ <b>${name}</b> · ${regId || "—"} · ${breed === "Montbeliard" ? "Montbéliarde" : breed}`;
  if (sel) sel.style.display = "flex";
  const breedEl = document.querySelector('select[name="sireBreed"]');
  if (breedEl && breed) {
    const disp = breed === "Montbeliard" ? "Montbéliarde" : breed;
    for (let o of breedEl.options) {
      if (o.text.includes(disp) || o.value === breed) {
        breedEl.value = o.value;
        break;
      }
    }
  }
}
function upSireSelectManual(text) {
  const inp = document.getElementById("req_sireName"),
    dd = document.getElementById("req_sireDD"),
    sel = document.getElementById("req_sireSelected"),
    lbl = document.getElementById("req_sireSelectedLabel");
  if (inp) {
    inp.value = text;
    if (inp.dataset) delete inp.dataset.sireId;
  }
  if (dd) dd.style.display = "none";
  if (lbl) lbl.innerHTML = `✏️ Manual entry: <b>${text}</b>`;
  if (sel) sel.style.display = "flex";
}
function upSireClear() {
  const inp = document.getElementById("req_sireName"),
    dd = document.getElementById("req_sireDD"),
    sel = document.getElementById("req_sireSelected");
  if (inp) {
    inp.value = "";
    if (inp.dataset) delete inp.dataset.sireId;
    inp.focus();
  }
  if (dd) dd.style.display = "none";
  if (sel) sel.style.display = "none";
}
document.addEventListener("click", function upSireOutside(e) {
  const dd = document.getElementById("req_sireDD");
  if (dd && !e.target.closest("#req_sireDD") && e.target.id !== "req_sireName")
    dd.style.display = "none";
});

/* ════════════════════════════════════════════════════════════════
   CORE APP FUNCTIONS — Storage, Navigation, Rendering
════════════════════════════════════════════════════════════════ */

/* ── Utilities ── */
function persist() {
  try {
    localStorage.setItem(CFG.storageKey, JSON.stringify(animals));
  } catch (e) {
    console.error("persist() failed:", e);
    // QuotaExceededError is the common cause — usually too many/large photos.
    const quota =
      e &&
      (e.name === "QuotaExceededError" || e.code === 22 || /quota/i.test(String(e.message || "")));
    if (typeof showToast === "function") {
      showToast(
        quota
          ? "⚠️ Storage full — remove photos or animals, then retry."
          : "⚠️ Could not save data: " + (e.message || e)
      );
    }
  }
  // Push to the server (fire-and-forget; the sync layer handles retry).
  if (window.LpSync && window.LpSync.user) {
    window.LpSync.push("animals", animals);
  }
}
function byId(id) {
  return animals.find(a => a.id === id) || null;
}
function fmtD(d) {
  if (!d) return "—";
  try {
    const dt = new Date(d + "T12:00");
    if (isNaN(dt.getTime())) return d; // bad input → show raw
    return dt.toLocaleDateString("en-GB", { day: "2-digit", month: "short", year: "numeric" });
  } catch (e) {
    return d;
  }
}
function fmtN(n) {
  return n ? Number(n).toLocaleString() : "—";
}
function fmt(v) {
  return v || "—";
}
function calc(milk, fat, prot) {
  const m = parseFloat(milk),
    f = parseFloat(fat),
    p = parseFloat(prot);
  return {
    fatKg: m && f ? Math.round(((m * f) / 100) * 10) / 10 : null,
    protKg: m && p ? Math.round(((m * p) / 100) * 10) / 10 : null,
  };
}
function miniProd(milk, fat, prot) {
  if (!milk) return "";
  const r = calc(milk, fat, prot);
  return `${fmtN(milk)}kg${fat ? " F" + fat + "%" : ""}${prot ? " P" + prot + "%" : ""}`;
}
function offspring(sireOrDamId) {
  return animals.filter(a => a.sireLinkedId === sireOrDamId || a.damLinkedId === sireOrDamId);
}
function showToast(msg) {
  const t = document.getElementById("toast");
  if (!t) return;
  t.textContent = msg;
  t.classList.add("on");
  setTimeout(() => t.classList.remove("on"), 2800);
}

/* ── Navigation ── */
function goPage(p) {
  [
    "dashboard",
    "herd",
    "sires",
    "kin",
    "indiv",
    "clients",
    "reports",
    "requests",
    "backup",
    "tagcfg",
    "breed",
    "mateplan",
    "herdgen",
    "selindex",
    "deepped",
    "landing",
    "translations",
  ].forEach(
    n => {
      const el = document.getElementById("pg-" + n);
      if (el) el.style.display = "none";
      const nb = document.getElementById("nb-" + n);
      if (nb) nb.classList.remove("act");
    }
  );
  const pg = document.getElementById("pg-" + p);
  if (pg) pg.style.display = "flex";
  // Grouped pages light up their group's nav button (the Genetics tools →
  // Genetics; Backup & Landing → Settings) so the main nav stays compact.
  const navFor = {
    kin: "breed", deepped: "breed", mateplan: "breed", herdgen: "breed", selindex: "breed",
    backup: "settings", landing: "settings", tagcfg: "settings", translations: "settings",
  }[p] || p;
  const nb = document.getElementById("nb-" + navFor);
  if (nb) nb.classList.add("act");
  if (p === "sires") renderSires();
  if (p === "kin") renderKin();
  if (p === "indiv") renderIndivPage();
  if (p === "clients") renderClientsPage();
  if (p === "reports") renderReportsPage();
  if (p === "requests") {
    renderAdminRequests();
    updateAdminBadge();
  }
  if (p === "backup") {
    renderBackupPage();
    setTimeout(renderUserMgmt, 50);
  }
  if (p === "breed") renderBreedPage();
  if (p === "deepped") dpEnsureAndRender();
  if (p === "mateplan") renderMatePlan();
  if (p === "herdgen") { _hgStats = null; renderHerdGenetics(); }
  if (p === "selindex") renderSelIndex();
  if (p === "dashboard") renderDashboard();
  if (p === "tagcfg") renderTagConfig();
  if (p === "landing") renderLandingAdmin();
  if (p === "translations") renderTranslations();
  // Inject the sub-tab bar for grouped sections (idempotent — safe even when
  // an async renderer like Deep Pedigree replaces the page content later).
  ensureSubtabBar(p);
}

/* Sub-navigation groups: one main nav button, sibling pages reachable via a
   sub-tab bar at the top of the page (keeps the header uncluttered). */
const GENETICS_TABS = [
  ["breed", "🧬 Breeding"],
  ["mateplan", "📋 Mate Plan"],
  ["herdgen", "📊 Herd Genetics"],
  ["selindex", "⭐ Selection"],
  ["kin", "🔗 Kinship"],
  ["deepped", "🌳 Deep Pedigree"],
];
const SETTINGS_TABS = [
  ["backup", "💾 Backup"],
  ["tagcfg", "🏷 Ear Tags"],
  ["landing", "🌐 Landing Page"],
  ["translations", "🗣 Translations"],
];
const NAV_GROUPS = {
  breed: GENETICS_TABS,
  mateplan: GENETICS_TABS,
  herdgen: GENETICS_TABS,
  selindex: GENETICS_TABS,
  kin: GENETICS_TABS,
  deepped: GENETICS_TABS,
  backup: SETTINGS_TABS,
  tagcfg: SETTINGS_TABS,
  landing: SETTINGS_TABS,
  translations: SETTINGS_TABS,
};
function subTabBarHTML(current) {
  const tabs = NAV_GROUPS[current];
  if (!tabs) return "";
  return (
    `<div class="subtab-bar">` +
    tabs
      .map(
        ([id, label]) =>
          `<button class="btn ${id === current ? "bg" : "bd"} bsm" onclick="goPage('${id}')">${label}</button>`
      )
      .join("") +
    `</div>`
  );
}
/* Add the sub-tab bar to a grouped page if it isn't already the first child. */
function ensureSubtabBar(p) {
  const pg = document.getElementById("pg-" + p);
  if (!pg || !NAV_GROUPS[p]) return;
  const first = pg.firstElementChild;
  if (first && first.classList && first.classList.contains("subtab-bar")) return;
  pg.insertAdjacentHTML("afterbegin", subTabBarHTML(p));
}

/* ── Sidebar Filter ── */
function setSF(f) {
  sbf = f;
  document.querySelectorAll(".sf").forEach(b => b.classList.remove("on"));
  const btn = document.getElementById("sf-" + f);
  if (btn) btn.classList.add("on");
  renderSB();
}
function renderOwnerBar() {
  const sireEl = document.getElementById("sb_sire");
  if (sireEl) {
    const sires = animals.filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role));
    sireEl.innerHTML =
      '<option value="">🐂 All Sires</option>' +
      sires.map(a => `<option value="${a.id}">${a.name} (${a.regId || "—"})</option>`).join("");
  }
  const provEl = document.getElementById("sb_prov");
  if (provEl) {
    const used = new Set(animals.map(a => a.province).filter(Boolean));
    const all = typeof getProvincesAF === "function" ? getProvincesAF() : [];
    provEl.innerHTML =
      '<option value="">📍 All Provinces</option>' +
      all
        .map(
          p => `<option value="${p}"${used.has(p) ? ' style="font-weight:bold"' : ""}>${p}</option>`
        )
        .join("");
  }
}

/* ── Sidebar Render ── */
/* Render one sidebar animal row (shared by local + DB mode). */
function sbRowHTML(a) {
  const ac = calcBreedComp(a);
  const ag = getGenInfo(ac);
  const roleIcon = a.sex === "Male" ? (a.role === "Son" ? "🐃" : "🐂") : "🐄";
  const roleBadge =
    a.sex === "Male"
      ? `<span style="font-size:8px;font-family:sans-serif;background:${a.role === "Son" ? "#fef3c7" : "#dbeafe"};color:${a.role === "Son" ? "#92400e" : "#1e40af"};padding:1px 4px;border-radius:3px;margin-left:2px">${a.role === "Son" ? "Son" : "Sire"}</span>`
      : "";
  const av = a.photo ? `<img src="${a.photo}" alt="">` : roleIcon;
  const ow = anyOwnerById(a.ownerId) || {};
  return `<div class="ai${String(a.id) === String(curId) ? " on" : ""}" onclick="selAnimal('${a.id}')">
    <div class="av ${a.breed === "Holstein" ? "bh" : a.breed === "Montbeliard" ? "bm" : "al"}">${av}</div>
    <div class="ain">
      <div class="anm">${a.name || "(Unnamed)"}${roleBadge}</div>
      ${a.earTag ? `<div style="font-size:10px;font-family:sans-serif;font-weight:700;color:var(--gd)">🏷 ${a.earTag}</div>` : ""}
      <div class="amt">${a.regId || "—"} · ${fmtD(a.dob)}</div>
      ${!ag.isPure ? `<span style="font-size:9px;font-family:sans-serif;font-weight:700;color:${ag.color};background:${ag.bg};padding:1px 5px;border-radius:6px">${ag.gen}</span>` : ""}
      ${ow.name ? `<div style="font-size:9px;font-family:sans-serif;color:#3a6bc4;margin-top:1px">🐄 ${ow.herdName || ow.name}</div>` : ""}
    </div>
    <div class="aac">
      <button class="ib" onclick="event.stopPropagation();doEdit('${a.id}')" title="Edit">✏️</button>
      <button class="ib" onclick="event.stopPropagation();doDelete('${a.id}')" title="Delete">🗑</button>
    </div>
  </div>`;
}

/* DB mode: load one page from the server (search + filters run in SQL). */
let _sbDebounce = null;
function renderSBServer(opts) {
  opts = opts || {};
  const el = document.getElementById("alist");
  if (!el) return;
  const sbEl = document.getElementById("sbQ");
  const q = sbEl ? sbEl.value.trim() : "";
  const fBreed = (document.getElementById("sb_breed") || {}).value || "";
  const fProv = (document.getElementById("sb_prov") || {}).value || "";
  const sexFilter = sbf === "male" ? "Male" : sbf === "fem" ? "Female" : "";
  const breedFilter = sbf === "h" ? "Holstein" : sbf === "m" ? "Montbeliard" : fBreed;

  const append = !!opts.append;
  if (!append) sbPage = 1;
  if (sbLoading) return;
  sbLoading = true;
  if (!append) el.innerHTML = '<div style="padding:14px;text-align:center;color:#aaa;font-family:sans-serif;font-size:11px">Loading…</div>';

  window.LpDB
    .animalsPage({ q, breed: breedFilter, sex: sexFilter, province: fProv, page: sbPage, perPage: sbPerPage, sort: "name" })
    .then(res => {
      sbTotal = res.total || 0;
      sbPages = res.pages || 1;
      lpCacheMerge(res.rows || []);
      const rowsHTML = (res.rows || []).map(sbRowHTML).join("");
      if (append) {
        const more = document.getElementById("sb_more_wrap");
        if (more) more.remove();
        el.insertAdjacentHTML("beforeend", rowsHTML);
      } else if (!res.rows || !res.rows.length) {
        el.innerHTML = '<div style="padding:14px;text-align:center;color:#aaa;font-family:sans-serif;font-size:11px">No animals found.</div>';
        sbLoading = false;
        return;
      } else {
        el.innerHTML = rowsHTML;
      }
      // Pagination footer
      const shown = Math.min(sbPage * sbPerPage, sbTotal);
      let footer = `<div id="sb_more_wrap" style="padding:10px;text-align:center;font-family:sans-serif;font-size:11px;color:var(--tl)">
        Showing ${shown.toLocaleString()} of ${sbTotal.toLocaleString()}`;
      if (sbPage < sbPages) {
        footer += `<br><button class="btn bd bsm" style="margin-top:6px" onclick="sbLoadMore()">Load more ▾</button>`;
      }
      footer += `</div>`;
      el.insertAdjacentHTML("beforeend", footer);
      sbLoading = false;
    })
    .catch(err => {
      sbLoading = false;
      el.innerHTML = '<div style="padding:10px;color:#c0392b;font-size:11px">Could not load animals: ' + (err.message || err) + "</div>";
    });
}
function sbLoadMore() {
  sbPage++;
  renderSBServer({ append: true });
}

function renderSB() {
  if (DB_MODE) {
    // Debounce typing so we don't hit the server on every keystroke.
    if (_sbDebounce) clearTimeout(_sbDebounce);
    _sbDebounce = setTimeout(() => renderSBServer({ append: false }), 220);
    return;
  }
  const sbEl = document.getElementById("sbQ");
  const el = document.getElementById("alist");
  if (!el) return;
  const q = (sbEl ? sbEl.value : "").toLowerCase();
  const fBreed = (document.getElementById("sb_breed") || {}).value || "";
  const fSire = (document.getElementById("sb_sire") || {}).value || "";
  const fProv = (document.getElementById("sb_prov") || {}).value || "";
  const list = animals.filter(a => {
    if (!a || !a.id) return false;
    if (sbf === "male" && a.sex !== "Male") return false;
    if (sbf === "fem" && a.sex !== "Female") return false;
    if (sbf === "h" && a.breed !== "Holstein") return false;
    if (sbf === "m" && a.breed !== "Montbeliard") return false;
    if (fBreed && a.breed !== fBreed) return false;
    if (fSire && a.sireLinkedId !== fSire) return false;
    if (fProv && a.province !== fProv) return false;
    const ow = anyOwnerById(a.ownerId) || {};
    return (
      !q ||
      (a.name || "").toLowerCase().includes(q) ||
      (a.regId || "").toLowerCase().includes(q) ||
      (a.earTag || "").toLowerCase().includes(q) ||
      (a.province || "").toLowerCase().includes(q) ||
      (a.district || "").toLowerCase().includes(q) ||
      (a.breed || "").toLowerCase().includes(q) ||
      (ow.name || "").toLowerCase().includes(q) ||
      (ow.herdName || "").toLowerCase().includes(q) ||
      (ow.herdCode || "").toLowerCase().includes(q)
    );
  });
  if (!list.length) {
    el.innerHTML =
      '<div style="padding:14px;text-align:center;color:#aaa;font-family:sans-serif;font-size:11px">No animals found.</div>';
    return;
  }
  try {
    el.innerHTML = list
      .map(a => {
        const ac = calcBreedComp(a);
        const ag = getGenInfo(ac);
        const roleIcon = a.sex === "Male" ? (a.role === "Son" ? "🐃" : "🐂") : "🐄";
        const roleBadge =
          a.sex === "Male"
            ? `<span style="font-size:8px;font-family:sans-serif;background:${a.role === "Son" ? "#fef3c7" : "#dbeafe"};color:${a.role === "Son" ? "#92400e" : "#1e40af"};padding:1px 4px;border-radius:3px;margin-left:2px">${a.role === "Son" ? "Son" : "Sire"}</span>`
            : "";
        const av = a.photo ? `<img src="${a.photo}" alt="">` : roleIcon;
        const ow = anyOwnerById(a.ownerId) || {};
        return `<div class="ai${a.id === curId ? " on" : ""}" onclick="selAnimal('${a.id}')">
        <div class="av ${a.breed === "Holstein" ? "bh" : a.breed === "Montbeliard" ? "bm" : "al"}">${av}</div>
        <div class="ain">
          <div class="anm">${a.name || "(Unnamed)"}${roleBadge}</div>
          ${a.earTag ? `<div style="font-size:10px;font-family:sans-serif;font-weight:700;color:var(--gd)">🏷 ${a.earTag}</div>` : ""}
          <div class="amt">${a.regId || "—"} · ${fmtD(a.dob)}</div>
          ${!ag.isPure ? `<span style="font-size:9px;font-family:sans-serif;font-weight:700;color:${ag.color};background:${ag.bg};padding:1px 5px;border-radius:6px">${ag.gen}</span>` : ""}
          ${ow.name ? `<div style="font-size:9px;font-family:sans-serif;color:#3a6bc4;margin-top:1px">🐄 ${ow.herdName || ow.name}</div>` : ""}
        </div>
        <div class="aac">
          <button class="ib" onclick="event.stopPropagation();doEdit('${a.id}')" title="Edit">✏️</button>
          <button class="ib" onclick="event.stopPropagation();doDelete('${a.id}')" title="Delete">🗑</button>
        </div>
      </div>`;
      })
      .join("");
  } catch (err) {
    el.innerHTML =
      '<div style="padding:10px;color:red;font-size:11px">Error: ' + err.message + "</div>";
  }
}

/* ── Select Animal ── */
function selAnimal(id) {
  curId = id;
  curTab = "pedigree";
  if (DB_MODE) return selAnimalServer(id);
  try {
    renderSB();
  } catch (e) {}
  try {
    renderHerd();
  } catch (e) {}
  goPage("herd");
}

/* DB mode: ensure the viewed animal + its linked parents + offspring are in
   the cache (so byId()/offspring() inside renderHerd resolve), then render. */
async function selAnimalServer(id) {
  goPage("herd");
  const pg = document.getElementById("pg-herd");
  if (pg) pg.innerHTML = '<div class="empty"><div class="ei">⏳</div><h3>Loading…</h3></div>';
  try {
    const a = await window.LpDB.getAnimal(id);
    if (!a) {
      if (pg) pg.innerHTML = '<div class="empty"><div class="ei">🐄</div><h3>Animal not found</h3></div>';
      return;
    }
    lpCacheMerge(a);
    // Pull linked parents so the pedigree panel can show them.
    const need = [];
    if (a.sireLinkedId) need.push(a.sireLinkedId);
    if (a.damLinkedId) need.push(a.damLinkedId);
    const parents = await Promise.all(need.map(pid => window.LpDB.getAnimal(pid).catch(() => null)));
    lpCacheMerge(parents.filter(Boolean));
    // Offspring for the offspring panel.
    try {
      const kids = await window.LpDB.offspring(id);
      lpCacheMerge(kids);
    } catch (e) {}
    renderHerd();
    // Reflect selection highlight in the sidebar without a full reload.
    document.querySelectorAll("#alist .ai").forEach(el => {
      el.classList.toggle("on", el.getAttribute("onclick") === `selAnimal('${id}')`);
    });
  } catch (err) {
    if (pg) pg.innerHTML = '<div class="empty"><div class="ei">⚠️</div><h3>Could not load animal</h3><p>' + (err.message || err) + "</p></div>";
  }
}

/* ── Home / Herd Page ── */
/* ════════════════════════════════════════════════════════════════
   DASHBOARD — herd overview landing page
   ════════════════════════════════════════════════════════════════ */
function dashCard(value, label, color, icon, onclick) {
  const click = onclick ? `onclick="${onclick}" style="cursor:pointer"` : "";
  return `<div class="sc" ${click}>
    <div style="font-size:18px">${icon || ""}</div>
    <div class="sv" style="color:${color || "var(--gd)"}">${value}</div>
    <div class="sl">${label}</div>
  </div>`;
}

function renderDashboard() {
  const el = document.getElementById("pg-dashboard");
  if (!el) return;

  const shell = body => {
    el.innerHTML = `
    <div class="card">
      <div class="ch" style="background:linear-gradient(135deg,#0f3a23,#2f7d52);-webkit-print-color-adjust:exact;print-color-adjust:exact">
        <h2 style="color:#fff">📊 Herd Dashboard</h2>
        <div style="display:flex;gap:6px;flex-wrap:wrap">
          <button class="btn bw bsm" onclick="openNew('','Female')">＋ Cow</button>
          <button class="btn bw bsm" onclick="openNew('','Male')">＋ Bull</button>
        </div>
      </div>
      <div class="cb">${body}</div>
    </div>`;
  };

  const quickActions = `
    <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:14px">
      <button class="btn bg bsm" onclick="goPage('herd')">🐄 Herd Register</button>
      <button class="btn bd bsm" onclick="goPage('deepped')">🌳 Deep Pedigree</button>
      <button class="btn bd bsm" onclick="goPage('kin')">🔗 Kinship</button>
      <button class="btn bd bsm" onclick="goPage('breed')">🧬 Breeding</button>
      <button class="btn bd bsm" onclick="goPage('reports')">📈 Reports</button>
    </div>`;

  if (DB_MODE) {
    shell('<div style="padding:14px;color:var(--tl);font-family:sans-serif;font-size:12px">Loading dashboard…</div>');
    window.LpDB
      .stats()
      .then(s => {
        const cards = `<div class="dp-stats" style="grid-template-columns:repeat(auto-fit,minmax(150px,1fr))">
          ${dashCard((s.animals || 0).toLocaleString(), "Animals in herd", "var(--gd)", "🐄", "goPage('herd')")}
          ${dashCard((s.owners || 0).toLocaleString(), "Herds / owners", "#3a6bc4", "🏡", "goPage('clients')")}
          ${dashCard((s.users || 0).toLocaleString(), "User accounts", "#7c3aed", "👥", "goPage('backup')")}
        </div>`;
        let unread = 0;
        try { unread = getAdminUnreadCount ? getAdminUnreadCount() : 0; } catch (e) {}
        const reqCard = unread
          ? `<div style="margin-top:12px"><button class="btn bb bsm" onclick="goPage('requests')">📬 ${unread} pending request${unread > 1 ? "s" : ""}</button></div>`
          : "";
        shell(cards + reqCard + quickActions);
      })
      .catch(() => shell('<div style="color:#c0392b;font-size:12px">Could not load stats.</div>' + quickActions));
    return;
  }

  // Local mode: compute from the in-memory herd.
  const total = animals.length;
  const cows = animals.filter(a => a.sex === "Female").length;
  const bulls = animals.filter(a => a.sex === "Male").length;
  const sires = animals.filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role)).length;
  const breeds = {};
  animals.forEach(a => { const b = a.breed || "—"; breeds[b] = (breeds[b] || 0) + 1; });
  const withPhoto = animals.filter(a => a.photo).length;

  // Inbreeding snapshot
  let fSum = 0, fCount = 0, fMax = 0, fMaxName = "";
  animals.forEach(a => {
    const r = animalF(a);
    if (r && r.F > 0) { fSum += r.F; fCount++; if (r.F > fMax) { fMax = r.F; fMaxName = a.name; } }
  });
  const avgF = fCount ? (fSum / fCount).toFixed(2) : null;

  // Recent additions (by id timestamp if present)
  const recent = [...animals].slice(-5).reverse();

  let unread = 0;
  try { unread = getAdminUnreadCount ? getAdminUnreadCount() : 0; } catch (e) {}

  const breedBars = Object.entries(breeds)
    .sort((a, b) => b[1] - a[1])
    .map(([b, n]) => {
      const pct = total ? Math.round((n * 100) / total) : 0;
      const col = b === "Holstein" ? "#3a6bc4" : b === "Montbeliard" ? "#e67e22" : b === "Local" ? "#16a34a" : "#888";
      return `<div style="margin-bottom:6px">
        <div style="display:flex;justify-content:space-between;font-family:sans-serif;font-size:11px"><span>${b === "Montbeliard" ? "Montbéliarde" : b}</span><span style="color:var(--tl)">${n} · ${pct}%</span></div>
        <div style="background:#eef2ee;border-radius:4px;height:8px;overflow:hidden"><div style="width:${pct}%;height:100%;background:${col}"></div></div>
      </div>`;
    })
    .join("");

  const recentRows = recent
    .map(a => `<div class="ofi" onclick="selAnimal('${a.id}')">
      <div class="av ${a.breed === "Holstein" ? "bh" : a.breed === "Montbeliard" ? "bm" : "al"}" style="width:26px;height:26px;font-size:13px">${a.photo ? `<img src="${a.photo}" alt="">` : a.sex === "Male" ? "🐂" : "🐄"}</div>
      <div style="flex:1;min-width:0"><b style="font-size:12px">${dpEscape(a.name || "—")}</b><div style="font-size:10px;color:var(--tl)">${dpEscape(a.regId || "—")} · ${fmtD(a.dob)}</div></div>
    </div>`)
    .join("") || '<div style="color:var(--tl);font-size:11px;font-family:sans-serif;padding:8px">No animals yet.</div>';

  // Population-genetics quick metrics
  let neVal = null;
  try { const ir = inbreedingRate(); neVal = ir.ne != null ? Math.round(ir.ne) : null; } catch (e) {}
  const carriers = animals.filter(a => a.defects && Object.values(a.defects).some(v => v === "C" || v === "A")).length;

  const cards = `<div class="dp-stats" style="grid-template-columns:repeat(auto-fit,minmax(130px,1fr))">
    ${dashCard(total.toLocaleString(), "Total animals", "var(--gd)", "🐄", "goPage('herd')")}
    ${dashCard(sires.toLocaleString(), "Breeding sires", "#3a6bc4", "🐂", "goPage('sires')")}
    ${dashCard(avgF != null ? avgF + "%" : "—", "Avg inbreeding F", avgF && parseFloat(avgF) > 6.25 ? "#c0392b" : "#16a34a", "🧬", "goPage('breed')")}
    ${dashCard(neVal != null ? neVal : "—", "Effective pop. (Ne)", neVal != null && neVal < 50 ? "#c0392b" : "#16a34a", "📊", "goPage('herdgen')")}
    ${dashCard(carriers.toLocaleString(), "Defect carriers", carriers ? "#b45309" : "#16a34a", "⛔", "goPage('breed')")}
  </div>`;

  const panels = `
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin-top:14px">
      <div style="border:1px solid var(--bdr);border-radius:var(--rl);padding:13px">
        <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd);margin-bottom:10px">🧬 Breed Composition</div>
        ${breedBars || '<div style="color:var(--tl);font-size:11px">No data</div>'}
        <div style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-top:8px">${withPhoto} of ${total} have photos</div>
      </div>
      <div style="border:1px solid var(--bdr);border-radius:var(--rl);padding:13px">
        <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd);margin-bottom:6px">🆕 Recently added</div>
        ${recentRows}
      </div>
      <div style="border:1px solid var(--bdr);border-radius:var(--rl);padding:13px">
        <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd);margin-bottom:8px">⚠️ Inbreeding watch</div>
        ${
          fCount
            ? `<div style="font-family:sans-serif;font-size:12px;line-height:1.8">
                Herd average F: <b>${avgF}%</b><br>
                Highest: <b style="color:#c0392b">${fMax.toFixed(2)}%</b> — ${dpEscape(fMaxName)}<br>
                Animals with F: <b>${fCount}</b> of ${total}
              </div>`
            : '<div style="color:var(--tl);font-size:11px">No inbreeding detected in linked pedigrees.</div>'
        }
        ${unread ? `<div style="margin-top:10px"><button class="btn bb bsm" onclick="goPage('requests')">📬 ${unread} pending request${unread > 1 ? "s" : ""}</button></div>` : ""}
      </div>
    </div>`;

  shell(cards + panels + quickActions);
}

function renderHerd() {
  const a = byId(curId);
  const pg = document.getElementById("pg-herd");
  if (!pg) return;
  if (!a) {
    pg.innerHTML =
      '<div class="empty"><div class="ei">🐄</div><h3>Select or add an animal</h3><p>Choose from the sidebar or click ＋ Bull / ＋ Cow</p></div>';
    return;
  }
  const r = calc(a.milk, a.fat, a.prot);
  const off = offspring(a.id);
  const sire = a.sireLinkedId ? byId(a.sireLinkedId) : null;
  const dam = a.damLinkedId ? byId(a.damLinkedId) : null;
  const aComp = calcBreedComp(a);
  const aGen = getGenInfo(aComp);
  const ow = anyOwnerById(a.ownerId) || {};
  const phH = a.photo
    ? `<img src="${a.photo}" class="aphoto" alt="${a.name}">`
    : `<div class="aphold">${a.sex === "Male" ? "🐂" : "🐄"}</div>`;
  pg.innerHTML = `
  <div id="pLH" style="display:none">${buildLetterhead(a, r, aComp, aGen)}</div>
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);-webkit-print-color-adjust:exact;print-color-adjust:exact;flex-wrap:wrap;gap:7px">
      <div style="display:flex;align-items:center;gap:10px">
        ${phH}
        <div>
          <h2 style="color:#fff;font-size:clamp(13px,1.8vw,17px)">${a.name || "(Unnamed)"}</h2>
          ${a.earTag ? `<div style="font-family:sans-serif;font-size:clamp(10px,1.2vw,13px);font-weight:700;color:#fff;background:rgba(255,255,255,.18);display:inline-block;padding:1px 9px;border-radius:8px;margin-top:2px">🏷 ${a.earTag}</div>` : ""}
          <div style="font-family:sans-serif;font-size:clamp(9px,1vw,11px);color:rgba(255,255,255,.8)">${a.breed === "Montbeliard" ? "Montbéliarde" : a.breed || "—"} · ${a.sex || "—"} · Born: ${fmtD(a.dob)}</div>
        </div>
      </div>
      <div class="hacts no-print" style="display:flex;gap:6px;flex-wrap:wrap">
        <button class="btn bo bsm" onclick="doEdit('${a.id}')">✏️ Edit</button>
        <button class="btn bo bsm" onclick="openPedigreeCertificate(byId(curId))">🖨 Print Pedigree</button>
      </div>
    </div>
    ${
      defectBadges(a)
        ? `<div style="padding:8px 15px;background:#fff;border-bottom:1px solid var(--bdr)"><span style="font-family:sans-serif;font-size:10px;color:var(--tl);font-weight:700;text-transform:uppercase;letter-spacing:.4px">🧬 Genetic health</span>${defectBadges(a)}</div>`
        : ""
    }
    <!-- Stats row -->
    <div class="srow">
      <div class="sc"><div class="sv">${a.regId || "—"}</div><div class="sl">Reg. ID</div></div>
      <div class="sc"><div class="sv">${a.earTag || "—"}</div><div class="sl">Ear Tag</div></div>
      <div class="sc"><div class="sv">${a.lact || "—"}</div><div class="sl">Lactation</div></div>
      <div class="sc"><div class="sv">${a.milk ? fmtN(a.milk) + " kg" : "—"}</div><div class="sl">Milk 305d</div></div>
      <div class="sc"><div class="sv">${off.length}</div><div class="sl">Offspring</div></div>
    </div>
    <!-- Generation row -->
    <div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:10px 12px;background:#fff;border:1px solid var(--bdr);border-radius:var(--rl);box-shadow:var(--sh)" class="no-print">
      <div style="flex-shrink:0">${genBadge(aComp)}</div>
      <div style="flex:1;min-width:120px">${compBar(aComp, 18)}</div>
      <div style="font-family:sans-serif;font-size:11px;flex-shrink:0">${compDetail(aComp)}</div>
    </div>
    <!-- Owner row -->
    ${
      ow.name
        ? `<div style="display:flex;flex-wrap:wrap;gap:10px;padding:10px 13px;background:#fff;border:1px solid var(--bdr);border-radius:var(--rl);box-shadow:var(--sh);font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)" class="no-print">
      ${[
        ["🐄 Herd", ow.herdName || ow.name],
        ["👤 Owner", ow.name],
        ["📞 Phone", ow.phone || "—"],
        ["📍 Province", a.province || ow.province || "—"],
        ["🏷 Tag", a.earTag || "—"],
      ]
        .map(
          ([l, v]) =>
            `<div style="min-width:90px"><span style="color:var(--tl);font-size:9px;display:block">${l}</span><b style="color:var(--td)">${v}</b></div>`
        )
        .join("")}
      <div style="margin-left:auto;flex-shrink:0"><button class="btn bg bsm" onclick="goPage('clients')">📋 View Herd</button></div>
    </div>`
        : ""
    } 
    <!-- Tabs -->
    <div class="mtabs">
      <button class="mtab${curTab === "pedigree" ? " on" : ""}" onclick="switchTab('pedigree')">🌳 Pedigree</button>
      <button class="mtab${curTab === "production" ? " on" : ""}" onclick="switchTab('production')">🥛 Production</button>
      <button class="mtab${curTab === "offspring" ? " on" : ""}" onclick="switchTab('offspring')">🐄 Offspring (${off.length})</button>
    </div>
    <div id="tabContent" class="cb"></div>
  </div>`;
  switchTab(curTab, a, r, off, sire, dam, aComp);
}

function switchTab(tab, aArg, rArg, offArg, sireArg, damArg, compArg) {
  curTab = tab;
  const a = aArg || byId(curId);
  if (!a) return;
  const r = rArg || calc(a.milk, a.fat, a.prot);
  const off = offArg || offspring(a.id);
  const sire = sireArg || (a.sireLinkedId ? byId(a.sireLinkedId) : null);
  const dam = damArg || (a.damLinkedId ? byId(a.damLinkedId) : null);
  const aComp = compArg || calcBreedComp(a);
  document
    .querySelectorAll(".mtab")
    .forEach(b =>
      b.classList.toggle("on", b.textContent.toLowerCase().includes(tab.toLowerCase().slice(0, 3)))
    );
  const tc = document.getElementById("tabContent");
  if (!tc) return;
  if (tab === "pedigree") tc.innerHTML = buildPedCard(a, aComp);
  if (tab === "production") tc.innerHTML = buildProd(a, r);
  if (tab === "offspring") tc.innerHTML = buildOffspring(a, off);
}

/* ── Pedigree Card ── */
function pedBox(cls, lbl, name, id, dob, breed, lnkId, milk, fat, prot, photo, breedComp) {
  const normB = normBreed(breed || "");
  const bc = BREEDS_CFG[normB] || { color: "#888" };
  const dot = `<span class="pdot" style="background:${bc.color}"></span>`;
  const prod = miniProd(milk, fat, prot);
  const ph = photo ? `<img src="${photo}" class="pedph" alt="">` : "";
  const nav = lnkId ? `onclick="selAnimal('${lnkId}')"` : "";
  const comp =
    breedComp && Object.keys(breedComp).length > 0 ? breedComp : breed ? { [normB]: 100 } : {};
  const gi = getGenInfo(comp);
  const miniBar = !gi.isPure && Object.keys(comp).length > 1 ? compBar(comp, 8) : "";
  const genTxt = !gi.isPure
    ? `<div style="font-size:8px;font-family:sans-serif;font-weight:700;color:${gi.color};margin-top:2px">${gi.gen} · ${gi.pct.toFixed(0)}% ${gi.dominant || ""}</div>`
    : "";
  return `<div class="pb ${cls}${!name ? " mt2" : ""}${lnkId ? " lk" : ""}" ${nav}>
    <div class="plbl">${lbl}</div>
    <div class="pnm">${ph}${dot}${name || "Not recorded"}</div>
    ${id ? `<div class="pid2">📋 ${id}</div>` : ""}
    ${dob ? `<div class="pdb">🗓 ${fmtD(dob)}</div>` : ""}
    ${genTxt}${miniBar ? `<div style="margin-top:3px">${miniBar}</div>` : ""}
    ${prod ? `<div class="ppr">🥛 ${prod}</div>` : ""}
    ${lnkId ? `<div class="plnk">🔗 View →</div>` : ""}
  </div>`;
}

function buildPedCard(a, aComp, gens) {
  if (!aComp) aComp = calcBreedComp(a);
  const g = gens || pedGens || 2;
  const sire = a.sireLinkedId ? byId(a.sireLinkedId) : null;
  const dam = a.damLinkedId ? byId(a.damLinkedId) : null;

  const toggleBtn = `<div style="display:flex;justify-content:flex-end;margin-bottom:8px;gap:6px">
    <span style="font-family:sans-serif;font-size:11px;color:var(--tl);align-self:center">Generations:</span>
    <button class="btn ${g === 2 ? "bg" : "bd"} bsm" onclick="pedGens=2;switchTab('pedigree')" style="font-size:11px;${g === 2 ? "" : "background:#fff"}">2-Gen</button>
    <button class="btn ${g >= 3 ? "bg" : "bd"} bsm" onclick="pedGens=3;switchTab('pedigree')" style="font-size:11px;${g >= 3 ? "" : "background:#fff"}">3-Gen</button>
  </div>`;

  if (g === 2) {
    // ── 2-GENERATION: Animal → Parents → Grandparents ──
    return (
      toggleBtn +
      `<div style="overflow-x:auto"><table class="pedtable" style="border-collapse:collapse;width:100%;min-width:380px">
    <tbody>
      <tr>
        <td rowspan="4" style="padding:6px;vertical-align:middle;width:28%">${pedBox("g1", "Subject", a.name, a.regId, a.dob, a.breed, "", a.milk, a.fat, a.prot, a.photo, aComp)}</td>
        <td rowspan="2" style="padding:6px;vertical-align:middle;width:30%">${pedBox("g2s", "Sire", a.sireName, a.sireId, a.sireDob, a.sireBreed || a.breed, a.sireLinkedId, "", "", "", sire ? sire.photo : "", sire ? calcBreedComp(sire) : { [normBreed(a.sireBreed || a.breed)]: 100 })}</td>
        <td style="padding:6px;vertical-align:middle">${pedBox("g3", "PGS (Sire's Sire)", a.ssName, a.ssId, "", a.ssBreed || a.breed, "", "", "", "", "")}</td>
      </tr>
      <tr><td style="padding:6px;vertical-align:middle">${pedBox("g3", "PGD (Sire's Dam)", a.sdName, a.sdId, "", a.sdBreed || a.breed, "", a.sdMilk, a.sdFat, a.sdProt, a.sdPhoto)}</td></tr>
      <tr>
        <td rowspan="2" style="padding:6px;vertical-align:middle">${pedBox("g2d", "Dam", a.damName, a.damId, a.damDob, a.damBreed || "Local", a.damLinkedId, a.damMilk, a.damFat, a.damProt, dam ? dam.photo : a.damPhoto, dam ? calcBreedComp(dam) : { [normBreed(a.damBreed || "Local")]: 100 })}</td>
        <td style="padding:6px;vertical-align:middle">${pedBox("g3", "MGS (Dam's Sire)", a.dsName, a.dsId, "", a.dsBreed || "Holstein", "", "", "", "", "")}</td>
      </tr>
      <tr><td style="padding:6px;vertical-align:middle">${pedBox("g3", "MGD (Dam's Dam)", a.ddName, a.ddId, "", a.ddBreed || "Local", "", a.ddMilk, a.ddFat, a.ddProt, a.ddPhoto)}</td></tr>
    </tbody>
    </table></div>`
    );
  }

  // ── 3-GENERATION: adds Great-Grandparents ──
  return (
    toggleBtn +
    `<div style="overflow-x:auto"><table class="pedtable" style="border-collapse:collapse;width:100%;min-width:520px">
  <tbody>
    <tr>
      <td rowspan="8" style="padding:5px;vertical-align:middle;width:22%">${pedBox("g1", "Subject", a.name, a.regId, a.dob, a.breed, "", a.milk, a.fat, a.prot, a.photo, aComp)}</td>
      <td rowspan="4" style="padding:5px;vertical-align:middle;width:24%">${pedBox("g2s", "Sire", a.sireName, a.sireId, a.sireDob, a.sireBreed || a.breed, a.sireLinkedId, "", "", "", sire ? sire.photo : "", sire ? calcBreedComp(sire) : { [normBreed(a.sireBreed || a.breed)]: 100 })}</td>
      <td rowspan="2" style="padding:5px;vertical-align:middle;width:24%">${pedBox("g3", "PGS", a.ssName, a.ssId, a.ssDob, a.ssBreed || a.breed, "", "", "", "", "")}</td>
      <td style="padding:5px;vertical-align:middle">${pedBox("g4", "PPGS", "", "", "", "Holstein", "", "", "", "", "")}</td>
    </tr>
    <tr><td style="padding:5px">${pedBox("g4", "PPGD", "", "", "", "Holstein", "", "", "", "", "")}</td></tr>
    <tr>
      <td rowspan="2" style="padding:5px;vertical-align:middle">${pedBox("g3", "PGD", a.sdName, a.sdId, a.sdDob, a.sdBreed || a.breed, "", a.sdMilk, a.sdFat, a.sdProt, a.sdPhoto)}</td>
      <td style="padding:5px">${pedBox("g4", "PDGS", "", "", "", "Holstein", "", "", "", "", "")}</td>
    </tr>
    <tr><td style="padding:5px">${pedBox("g4", "PDGD", "", "", "", "Holstein", "", "", "", "", "")}</td></tr>
    <tr>
      <td rowspan="4" style="padding:5px;vertical-align:middle">${pedBox("g2d", "Dam", a.damName, a.damId, a.damDob, a.damBreed || "Local", a.damLinkedId, a.damMilk, a.damFat, a.damProt, dam ? dam.photo : a.damPhoto, dam ? calcBreedComp(dam) : { [normBreed(a.damBreed || "Local")]: 100 })}</td>
      <td rowspan="2" style="padding:5px;vertical-align:middle">${pedBox("g3", "MGS", a.dsName, a.dsId, a.dsDob, a.dsBreed || "Holstein", "", "", "", "", "")}</td>
      <td style="padding:5px">${pedBox("g4", "MMGS", "", "", "", "Holstein", "", "", "", "", "")}</td>
    </tr>
    <tr><td style="padding:5px">${pedBox("g4", "MMGD", "", "", "", "Holstein", "", "", "", "", "")}</td></tr>
    <tr>
      <td rowspan="2" style="padding:5px;vertical-align:middle">${pedBox("g3", "MGD", a.ddName, a.ddId, a.ddDob, a.ddBreed || "Local", "", a.ddMilk, a.ddFat, a.ddProt, a.ddPhoto)}</td>
      <td style="padding:5px">${pedBox("g4", "MDGS", "", "", "", "Holstein", "", "", "", "", "")}</td>
    </tr>
    <tr><td style="padding:5px">${pedBox("g4", "MDGD", "", "", "", "Local", "", "", "", "", "")}</td></tr>
  </tbody>
  </table></div>`
  );
}

/* ── Production Tab ── */
function buildProd(a, r) {
  if (!a.milk)
    return `<div style="padding:20px;text-align:center;color:var(--tl);font-family:sans-serif;font-size:12px">No production record. <button class="btn bg bsm" onclick="doEdit('${a.id}')">Add Production</button></div>`;
  return `<div class="ppanel">
    ${[
      ["Milk 305d", fmtN(a.milk) + " kg", "var(--gm)"],
      ["Fat %", fmt(a.fat) + "%", "#c07a00"],
      ["Protein %", fmt(a.prot) + "%", "#3a6bc4"],
      ["Fat kg", r.fatKg !== null ? r.fatKg + " kg" : "—", "#c07a00"],
      ["Prot kg", r.protKg !== null ? r.protKg + " kg" : "—", "#3a6bc4"],
      ["Peak", fmt(a.peak) + " kg/d", "var(--gm)"],
      ["DIM", fmt(a.dim) + " days", "var(--tl)"],
      ["SCS", fmt(a.scs), "var(--tl)"],
      ["Calving", fmtD(a.calving), "var(--tl)"],
      ["Lactation", "#" + fmt(a.lact), "var(--tl)"],
    ]
      .map(
        ([l, v, c]) =>
          `<div class="pc"><div class="pcl">${l}</div><div class="pcv" style="color:${c}">${v}</div></div>`
      )
      .join("")}
  </div>`;
}

/* ── Offspring Tab ── */
function buildOffspring(a, off) {
  if (!off.length)
    return `<div style="padding:16px;text-align:center;color:var(--tl);font-family:sans-serif;font-size:12px">No offspring recorded.<br><button class="btn bg bsm" style="margin-top:8px" onclick="openNewProgeny('${a.id}')">＋ Add Progeny</button></div>`;
  return `<div style="padding:10px">${off
    .map(o => {
      const r2 = calc(o.milk, o.fat, o.prot);
      const ov = o.photo ? `<img src="${o.photo}" alt="">` : o.sex === "Male" ? "🐃" : "🐄";
      const gc = calcBreedComp(o);
      const gg = getGenInfo(gc);
      return `<div class="ofi" onclick="selAnimal('${o.id}')">
      <div class="oav ${o.breed === "Holstein" ? "bh" : "bm"}">${ov}</div>
      <div style="flex:1;min-width:0">
        <div style="font-weight:bold;font-size:12px">${o.name}</div>
        <div style="font-size:10px;font-family:sans-serif;color:var(--tl)">${o.regId || "—"} · ${fmtD(o.dob)}</div>
        ${!gg.isPure ? `<span style="font-size:9px;font-family:sans-serif;font-weight:700;color:${gg.color};background:${gg.bg};padding:1px 6px;border-radius:6px">${gg.gen}</span>` : ""}
        ${o.sex === "Female" && o.milk ? `<div style="font-size:10px;font-family:sans-serif;color:#7a3aad">🥛${fmtN(o.milk)}kg</div>` : ""}
      </div>
    </div>`;
    })
    .join("")}</div>`;
}

/* ── Print letterhead ── */
function buildLetterhead(a, r, aComp, aGen) {
  const ow = anyOwnerById(a.ownerId) || {};
  return `<div style="font-family:sans-serif;padding:10px">
    <div style="background:#1a4a2e;color:#fff;padding:12px 16px;border-radius:4px;margin-bottom:8px;-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <div style="font-size:14px;font-weight:bold">${CFG.company}</div>
      <div style="font-size:10px;opacity:.8">${CFG.pedTitle} — ${CFG.certSub}</div>
    </div>
    <div style="background:#2d7a4f;color:#fff;padding:8px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;-webkit-print-color-adjust:exact;print-color-adjust:exact">
      ${[
        ["Animal", a.name],
        ["Reg. ID", a.regId || "—"],
        ["Ear Tag", a.earTag || "—"],
        ["Breed", a.breed === "Montbeliard" ? "Montbéliarde" : a.breed || "—"],
        ["DOB", fmtD(a.dob)],
        ["Generation", aGen.gen + " · " + aGen.pct.toFixed(1) + "%"],
        ["Owner", ow.name || "—"],
        ["Province", a.province || "—"],
      ]
        .map(
          ([l, v]) =>
            `<div><div style="font-size:7px;text-transform:uppercase;opacity:.7">${l}</div><div style="font-size:10px;font-weight:bold">${v}</div></div>`
        )
        .join("")}
    </div>
  </div>`;
}

/* ── Sires Page ── */
function renderSires() {
  const q = (document.getElementById("sireQ")?.value || "").toLowerCase();
  const brd = document.getElementById("sireB")?.value || "";
  const sires = animals.filter(
    a =>
      a.sex === "Male" &&
      (a.role === "Sire" || (!a.role && !a.sireLinkedId)) &&
      (!q || a.name.toLowerCase().includes(q) || (a.regId || "").toLowerCase().includes(q)) &&
      (!brd || a.breed === brd)
  );
  const sons = animals.filter(
    a =>
      a.sex === "Male" &&
      a.role === "Son" &&
      (!q || a.name.toLowerCase().includes(q)) &&
      (!brd || a.breed === brd)
  );
  const cntEl = document.getElementById("sireCnt");
  if (cntEl)
    cntEl.textContent = `${sires.length} sire${sires.length !== 1 ? "s" : ""} · ${sons.length} son${sons.length !== 1 ? "s" : ""}`;
  const el = document.getElementById("sireCont");
  if (!el) return;
  let html = `<div style="margin-bottom:14px">
    <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:7px;margin-bottom:10px">
      <span style="background:#dbeafe;color:#1e40af;padding:4px 12px;border-radius:8px;font-size:12px;font-family:sans-serif;font-weight:700">🐂 Breeding Sires (${sires.length})</span>
      <button class="btn bg bsm" onclick="openNew('','Male')">＋ Register New Sire</button>
    </div>`;
  html += sires.length
    ? sires.map(a => buildSireCard(a)).join("")
    : `<div style="background:#f0fdf4;border:1px solid #a7f3d0;border-radius:var(--r);padding:16px;text-align:center;font-family:sans-serif;font-size:12px;color:var(--tm)">No breeding sires. <button class="btn bg bsm" onclick="openNew('','Male')">＋ Register first sire</button></div>`;
  html += `</div><div style="border-top:2px solid var(--bdr);padding-top:14px">
    <div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:7px;margin-bottom:10px">
      <span style="background:#fef3c7;color:#92400e;padding:4px 12px;border-radius:8px;font-size:12px;font-family:sans-serif;font-weight:700">🐃 Bull Sons (${sons.length})</span>
      <button class="btn bw bsm" onclick="openNewSon()" style="border:1px solid var(--gm)">＋ Register Bull Son</button>
    </div>`;
  if (sons.length) {
    const bySire = {};
    sons.forEach(s => {
      const k = s.sireLinkedId || "_";
      if (!bySire[k]) bySire[k] = [];
      bySire[k].push(s);
    });
    Object.entries(bySire).forEach(([sid, group]) => {
      const sa = sid !== "unlinked" ? byId(sid) : null;
      html += `<div style="border:1px solid #fde68a;border-radius:var(--rl);margin-bottom:10px;overflow:hidden">
        <div style="background:#fffbeb;padding:8px 14px;font-family:sans-serif;font-size:12px;font-weight:700;color:#92400e;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px">
          <span>🐃 Sons of ${sa ? sa.name : "(unlinked)"}</span>
          ${sa ? `<button class="btn bw bsm" style="font-size:11px;border:1px solid #fde68a;color:var(--gd)" onclick="selAnimal('${sa.id}');goPage('herd')">View Sire →</button>` : ""}
        </div>
        <div style="padding:10px 14px">${group.map(a => buildSonRow(a)).join("")}</div>
      </div>`;
    });
  } else {
    html += `<div style="background:#fffbeb;border:1px solid #fde68a;border-radius:var(--r);padding:14px;text-align:center;font-family:sans-serif;font-size:12px;color:var(--tm)">No bull sons registered.</div>`;
  }
  html += `</div>`;
  el.innerHTML = html;
}

/* ── Modal / Form ── */
/* ── Collapsible form sections (reduces "overwhelming form") ── */
function toggleFormSec(targetId, hdEl) {
  const body = document.getElementById(targetId);
  if (!body) return;
  const collapsed = body.classList.toggle("collapsed");
  const ico = hdEl ? hdEl.querySelector(".fsec-ico") : null;
  if (ico) ico.textContent = collapsed ? "▸" : "▾";
}
/* Collapse (true) or expand (false) the optional pedigree/production/breed sections.
   Identity always stays visible. */
function setFormSections(collapsed) {
  ["sire-sel", "dam-sel", "prodSec", "bcSec", "genSec"].forEach(id => {
    const body = document.getElementById(id);
    if (!body) return;
    body.classList.toggle("collapsed", collapsed);
    // Sync the matching header icon
    const hd = body.previousElementSibling;
    const ico = hd && hd.classList.contains("fsec-hd") ? hd.querySelector(".fsec-ico") : null;
    if (ico) ico.textContent = collapsed ? "▸" : "▾";
  });
}

function openNew(presetId, presetSex, presetHerdId, indivOwnerId) {
  editId = null;
  clearForm();
  document.getElementById("modalTitle").textContent = "Register New Animal";
  document.getElementById("modal").classList.add("on");
  lpBuildDefectForm({});
  const _tn = document.getElementById("f_tagNote");
  if (_tn) _tn.textContent = getTagConfig().enabled ? "Leave blank to auto-generate from region + breed on save." : "";
  // Quick-add: start with only Identity visible; pedigree/production are one
  // click away. Autofocus the name field so the user can type immediately.
  setFormSections(true);
  setTimeout(() => document.getElementById("f_name")?.focus(), 40);
  if (presetSex === "Male") {
    document.getElementById("f_sex").value = "Male";
    const rfr = document.getElementById("roleFieldRow");
    if (rfr) rfr.style.display = "block";
  }
  const hid = presetHerdId || indivOwnerId || "";
  populateOwnerSelect(hid);
  if (hid) {
    const oe = document.getElementById("f_ownerId");
    if (oe) oe.value = hid;
    const ow = anyOwnerById(hid);
    if (ow) {
      const de = document.getElementById("f_province");
      const di = document.getElementById("f_district");
      if (de && typeof getProvincesAF === "function" && de.options.length <= 1) {
        de.innerHTML =
          '<option value="">— Province —</option>' +
          getProvincesAF()
            .map(p => `<option>${p}</option>`)
            .join("");
      }
      if (de && ow.province) de.value = ow.province;
      if (de && ow.province && typeof cascadeDistrict === "function") {
        cascadeDistrict("f_province", "f_district");
        setTimeout(() => {
          const di = document.getElementById("f_district");
          if (di && ow.district) di.value = ow.district;
        }, 100);
      }
    }
  } else {
    populateOwnerSelect("");
    // Populate provinces
    const pe = document.getElementById("f_province");
    if (pe && typeof getProvincesAF === "function" && pe.options.length <= 1) {
      pe.innerHTML =
        '<option value="">— Province —</option>' +
        getProvincesAF()
          .map(p => `<option>${p}</option>`)
          .join("");
    }
  }
  if (presetId) {
    const par = byId(presetId);
    if (par) {
      const which = par.sex === "Male" ? "sire" : "dam";
      // Expand the pedigree section so the linked parent is visible
      const body = document.getElementById(which + "-sel");
      if (body) {
        body.classList.remove("collapsed");
        const ico = body.previousElementSibling?.querySelector(".fsec-ico");
        if (ico) ico.textContent = "▾";
      }
      setTimeout(() => ppSel(which, presetId), 80);
    }
  }
}

function closeModal() {
  document.getElementById("modal").classList.remove("on");
  clearForm();
  pendPh = {};
}

function clearForm() {
  [
    "f_name",
    "f_regid",
    "f_earTag",
    "f_lact",
    "f_color",
    "f_sireName",
    "f_sireId",
    "f_ssName",
    "f_ssId",
    "f_sdName",
    "f_sdId",
    "f_sdMilk",
    "f_sdFat",
    "f_sdProt",
    "f_damName",
    "f_damId",
    "f_dsName",
    "f_dsId",
    "f_ddName",
    "f_ddId",
    "f_ddMilk",
    "f_ddFat",
    "f_ddProt",
    "f_damMilk",
    "f_damFat",
    "f_damProt",
    "f_milk",
    "f_fat",
    "f_prot",
    "f_peak",
    "f_dim",
    "f_scs",
    "f_notes",
    "f_bc_ho",
    "f_bc_mo",
    "f_bc_lc",
    "f_bc_other_name",
    "f_bc_other_pct",
  ].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  [
    "f_dob",
    "f_sireDob",
    "f_damDob",
    "f_ssDob",
    "f_sdDob",
    "f_dsDob",
    "f_ddDob",
    "f_calving",
  ].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  const fsex = document.getElementById("f_sex");
  if (fsex) fsex.value = "Female";
  const rfr = document.getElementById("roleFieldRow");
  if (rfr) rfr.style.display = "none";
  const rff = document.getElementById("f_role");
  if (rff) rff.value = "Sire";
  const bp = document.getElementById("bc_prev");
  if (bp) bp.innerHTML = "Auto-calculated from pedigree";
  selSireId = null;
  selDamId = null;
  ["sire-sel", "dam-sel"].forEach(id => {
    const e = document.getElementById(id);
    if (e) {
      e.classList.remove("linked");
      const li = e.querySelector(".psell");
      if (li) li.remove();
    }
  });
  ["prev_photo", "prev_sdPhoto", "prev_damPhoto", "prev_ddPhoto"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.innerHTML = "";
  });
  curBreed = "Holstein";
  selBreed("Holstein");
  pendPh = {};
}

function selBreed(b) {
  curBreed = b;
  const tabs = { Holstein: "tabH", Montbeliard: "tabM", Local: "tabL", Crossbred: "tabX" };
  const classes = { Holstein: "ah", Montbeliard: "am", Local: "al", Crossbred: "ax" };
  Object.keys(tabs).forEach(br => {
    const el = document.getElementById(tabs[br]);
    if (el) el.className = "bttab" + (b === br ? " " + classes[br] : "");
  });
}

function updateRoleField() {
  const sex = document.getElementById("f_sex")?.value;
  const row = document.getElementById("roleFieldRow");
  if (row) row.style.display = sex === "Male" ? "block" : "none";
}

/* ── Parent Picker ── */
function openPP(side) {
  selSireId = side === "sire" ? selSireId : selSireId;
  const over = document.getElementById("ppOverlay");
  if (!over) return;
  const title = document.getElementById("ppTitle");
  if (title) title.textContent = side === "sire" ? "Select Sire" : "Select Dam";
  over.dataset.side = side;
  over.style.display = "flex";
  filterPP();
}
function closePP() {
  const over = document.getElementById("ppOverlay");
  if (over) over.style.display = "none";
}
function filterPP() {
  const over = document.getElementById("ppOverlay");
  if (!over) return;
  const side = over.dataset.side || "sire";
  const q = (document.getElementById("ppQ")?.value || "").toLowerCase();
  const list = document.getElementById("ppList");
  if (!list) return;
  const candidates = animals.filter(a => {
    if (side === "sire" && a.sex !== "Male") return false;
    if (side === "dam" && a.sex !== "Female") return false;
    return !q || a.name.toLowerCase().includes(q) || (a.regId || "").toLowerCase().includes(q);
  });
  list.innerHTML =
    candidates
      .slice(0, 20)
      .map(
        a => `
    <div style="padding:9px 12px;cursor:pointer;font-family:sans-serif;font-size:12px;border-bottom:1px solid #f0f0f0;display:flex;align-items:center;gap:8px"
      onmouseover="this.style.background='var(--gp)'" onmouseout="this.style.background='#fff'"
      onclick="ppSel('${side}','${a.id}')">
      ${a.photo ? `<img src="${a.photo}" style="width:28px;height:28px;border-radius:50%;object-fit:cover">` : a.sex === "Male" ? "🐂" : "🐄"}
      <div><div style="font-weight:bold">${a.name}</div>
      <div style="font-size:10px;color:var(--tl)">${a.regId || "—"} · ${fmtD(a.dob)}</div></div>
    </div>`
      )
      .join("") ||
    '<div style="padding:12px;color:var(--tl);font-family:sans-serif;font-size:12px">No animals found</div>';
}
function ppSel(side, id) {
  const a = byId(id);
  if (!a) return;
  if (side === "sire") {
    selSireId = id;
    const s = id => {
      const e = document.getElementById(id);
      if (e) e.value = "";
    };
    document.getElementById("f_sireName").value = a.name;
    document.getElementById("f_sireId").value = a.regId || "";
    if (document.getElementById("f_sireDob"))
      document.getElementById("f_sireDob").value = a.dob || "";
    const sb = document.getElementById("f_sireBreed");
    if (sb) sb.value = a.breed || "Holstein";
    if (a.ssName && document.getElementById("f_ssName"))
      document.getElementById("f_ssName").value = a.ssName;
    if (a.sdName && document.getElementById("f_sdName"))
      document.getElementById("f_sdName").value = a.sdName;
    // show link badge
    const sel = document.getElementById("sire-sel");
    if (sel) {
      sel.classList.add("linked");
      let li = sel.querySelector(".psell");
      if (!li) {
        li = document.createElement("div");
        li.className = "psell";
        sel.appendChild(li);
      }
      li.innerHTML = `<span style="font-size:10px;color:var(--gm)">🔗 Linked: ${a.name}</span> <button type="button" class="ib" onclick="clearPP('sire')">✕</button>`;
    }
  } else {
    selDamId = id;
    document.getElementById("f_damName").value = a.name;
    document.getElementById("f_damId").value = a.regId || "";
    if (document.getElementById("f_damDob"))
      document.getElementById("f_damDob").value = a.dob || "";
    const db = document.getElementById("f_damBreed");
    if (db) db.value = a.breed || "Local";
    if (a.milk && document.getElementById("f_damMilk"))
      document.getElementById("f_damMilk").value = a.milk;
    if (a.fat && document.getElementById("f_damFat"))
      document.getElementById("f_damFat").value = a.fat;
    if (a.prot && document.getElementById("f_damProt"))
      document.getElementById("f_damProt").value = a.prot;
    if (a.dsName && document.getElementById("f_dsName"))
      document.getElementById("f_dsName").value = a.dsName;
    if (a.ddName && document.getElementById("f_ddName"))
      document.getElementById("f_ddName").value = a.ddName;
    const sel = document.getElementById("dam-sel");
    if (sel) {
      sel.classList.add("linked");
      let li = sel.querySelector(".psell");
      if (!li) {
        li = document.createElement("div");
        li.className = "psell";
        sel.appendChild(li);
      }
      li.innerHTML = `<span style="font-size:10px;color:var(--gm)">🔗 Linked: ${a.name}</span> <button type="button" class="ib" onclick="clearPP('dam')">✕</button>`;
    }
  }
  closePP();
}
function clearPP(side) {
  if (side === "sire") {
    selSireId = null;
    ["f_sireName", "f_sireId", "f_sireDob", "f_ssName", "f_sdName"].forEach(id => {
      const e = document.getElementById(id);
      if (e) e.value = "";
    });
    const s = document.getElementById("sire-sel");
    if (s) {
      s.classList.remove("linked");
      const li = s.querySelector(".psell");
      if (li) li.remove();
    }
  } else {
    selDamId = null;
    [
      "f_damName",
      "f_damId",
      "f_damDob",
      "f_damMilk",
      "f_damFat",
      "f_damProt",
      "f_dsName",
      "f_ddName",
    ].forEach(id => {
      const e = document.getElementById(id);
      if (e) e.value = "";
    });
    const d = document.getElementById("dam-sel");
    if (d) {
      d.classList.remove("linked");
      const li = d.querySelector(".psell");
      if (li) li.remove();
    }
  }
}

/* ── Photo handler ── */
function pendPhoto(key, inp) {
  if (!inp || !inp.files || !inp.files[0]) return;
  const file = inp.files[0];
  // Validate type
  if (!/^image\//.test(file.type)) {
    showToast("⚠️ Please choose an image file.");
    inp.value = "";
    return;
  }
  // Guard against extreme files (raw upload before downscale). 15MB is plenty for any phone photo.
  const MAX_BYTES = 15 * 1024 * 1024;
  if (file.size > MAX_BYTES) {
    showToast("⚠️ Image too large (max 15 MB).");
    inp.value = "";
    return;
  }
  const reader = new FileReader();
  reader.onerror = function () {
    showToast("⚠️ Could not read image file.");
    inp.value = "";
  };
  reader.onload = function (e) {
    const img = new Image();
    img.onerror = function () {
      showToast("⚠️ Invalid or corrupt image.");
      inp.value = "";
    };
    img.onload = function () {
      const MAX = 400,
        c = document.getElementById("c_" + key + "Photo") || document.getElementById("c_photo");
      if (!c) return;
      let w = img.width,
        h = img.height;
      if (!w || !h) {
        showToast("⚠️ Could not read image dimensions.");
        return;
      }
      if (w > MAX) {
        h = Math.round((h * MAX) / w);
        w = MAX;
      }
      if (h > MAX) {
        w = Math.round((w * MAX) / h);
        h = MAX;
      }
      c.width = w;
      c.height = h;
      try {
        c.getContext("2d").drawImage(img, 0, 0, w, h);
        pendPh[key] = c.toDataURL("image/jpeg", 0.72);
      } catch (err) {
        console.error("Photo encode failed:", err);
        showToast("⚠️ Could not process image.");
        return;
      }
      const prev =
        document.getElementById("prev_" + key + "Photo") || document.getElementById("prev_photo");
      if (prev)
        prev.innerHTML = `<img src="${pendPh[key]}" style="max-width:80px;max-height:80px;border-radius:4px;border:1px solid var(--bdr);margin-top:4px">`;
    };
    img.src = e.target.result;
  };
  reader.readAsDataURL(file);
}

/* ── Save Animal ── */
function saveAnimal(addAnother) {
  // Name auto-generated if blank
  const g = id => {
    const e = document.getElementById(id);
    return e ? e.value.trim() : "";
  };
  const rawName = g("f_name");
  const name = rawName || curBreed + " #" + Date.now().toString().slice(-4);
  const data = {
    breed: curBreed,
    name,
    regId: g("f_regid"),
    dob: g("f_dob"),
    sex: g("f_sex"),
    role: g("f_sex") === "Male" ? g("f_role") || "Sire" : "Cow",
    lact: g("f_lact"),
    color: g("f_color"),
    earTag: g("f_earTag"),
    ownerId: g("f_ownerId"),
    district: g("f_district"),
    province: g("f_province"),
    sireName: g("f_sireName"),
    sireId: g("f_sireId"),
    sireDob: g("f_sireDob"),
    sireBreed: g("f_sireBreed"),
    sireLinkedId: selSireId || "",
    ssName: g("f_ssName"),
    ssId: g("f_ssId"),
    sdName: g("f_sdName"),
    sdId: g("f_sdId"),
    sdMilk: g("f_sdMilk"),
    sdFat: g("f_sdFat"),
    sdProt: g("f_sdProt"),
    damName: g("f_damName"),
    damId: g("f_damId"),
    damDob: g("f_damDob"),
    damBreed: g("f_damBreed"),
    damMilk: g("f_damMilk"),
    damFat: g("f_damFat"),
    damProt: g("f_damProt"),
    damLinkedId: selDamId || "",
    dsName: g("f_dsName"),
    dsId: g("f_dsId"),
    ddName: g("f_ddName"),
    ddId: g("f_ddId"),
    ddMilk: g("f_ddMilk"),
    ddFat: g("f_ddFat"),
    ddProt: g("f_ddProt"),
    milk: g("f_milk"),
    fat: g("f_fat"),
    prot: g("f_prot"),
    peak: g("f_peak"),
    dim: g("f_dim"),
    scs: g("f_scs"),
    calving: g("f_calving"),
    notes: g("f_notes"),
    photo: pendPh.subj || pendPh.photo || "",
    sdPhoto: pendPh.sd || "",
    damPhoto: pendPh.dam || "",
    ddPhoto: pendPh.dd || "",
    breedCompOverride: (function () {
      const ho = parseFloat(document.getElementById("f_bc_ho")?.value) || 0;
      const mo = parseFloat(document.getElementById("f_bc_mo")?.value) || 0;
      const lc = parseFloat(document.getElementById("f_bc_lc")?.value) || 0;
      const on = document.getElementById("f_bc_other_name")?.value.trim() || "";
      const op = parseFloat(document.getElementById("f_bc_other_pct")?.value) || 0;
      const tot = ho + mo + lc + op;
      if (tot < 0.1) return {};
      const c = {};
      if (ho > 0) c["Holstein"] = ho;
      if (mo > 0) c["Montbeliard"] = mo;
      if (lc > 0) c["Local"] = lc;
      if (on && op > 0) c[on] = op;
      return c;
    })(),
  };
  data.defects = lpCaptureDefects();

  // Auto-assign the Ear Tag on a NEW registration when left blank (local mode
  // here; DB mode does it in saveAnimalServer via the server atomic counter).
  if (!editId && !data.earTag && !DB_MODE) {
    data.earTag = assignTagLocal(data);
  }

  // ── DB mode: save through the scalable API, then refresh ──
  if (DB_MODE) {
    if (editId) data.id = editId;
    saveAnimalServer(data, !!addAnother);
    return;
  }

  if (editId) {
    const idx = animals.findIndex(a => a.id === editId);
    if (idx >= 0) {
      Object.assign(animals[idx], data);
    }
  } else {
    data.id = "a" + Date.now();
    animals.push(data);
    curId = data.id;
  }
  pendPh = {};
  try {
    persist();
  } catch (e) {}
  const wasEdit = !!editId;
  // "Save & Add Another": keep the form open, reset it, and start a fresh entry
  // so the user can batch-add animals without reopening the modal each time.
  if (addAnother && !wasEdit) {
    showToast("Registered ✓ — add the next one");
    const keepSex = document.getElementById("f_sex")?.value || "Female";
    const keepOwner = document.getElementById("f_ownerId")?.value || "";
    const keepProv = document.getElementById("f_province")?.value || "";
    editId = null;
    clearForm();
    // Carry forward sex + owner + province (usually the same across a batch)
    const sx = document.getElementById("f_sex"); if (sx) sx.value = keepSex;
    if (typeof updateRoleField === "function") updateRoleField();
    populateOwnerSelect(keepOwner);
    const oe = document.getElementById("f_ownerId"); if (oe) oe.value = keepOwner;
    const pe = document.getElementById("f_province");
    if (pe && typeof getProvincesAF === "function" && pe.options.length <= 1) {
      pe.innerHTML = '<option value="">— Select Province —</option>' +
        getProvincesAF().map(p => `<option>${p}</option>`).join("");
    }
    if (pe) pe.value = keepProv;
    setFormSections(true); // collapse extras again for the next quick entry
    try { renderSB(); } catch (e) {}
    setTimeout(() => document.getElementById("f_name")?.focus(), 30);
    return;
  }
  showToast(wasEdit ? "Updated ✓" : "Registered ✓");
  closeModal();
  try {
    renderSB();
  } catch (e) {}
  try {
    renderHerd();
  } catch (e) {}
  // Link back to pending request if applicable
  if (window._pendingRequestLink) {
    try {
      const lnk = window._pendingRequestLink;
      const savedA = animals.find(a => a.id === (editId || curId));
      if (savedA) {
        savedA.pendingRequest = lnk.msgId;
        persist();
      }
      const allA = JSON.parse(localStorage.getItem("larspeenghar_v7") || "[]");
      const filtered = allA.filter(a => a.id !== lnk.pendingId);
      localStorage.setItem("larspeenghar_v7", JSON.stringify(filtered));
      if (typeof adminUpdateStatus === "function") adminUpdateStatus(lnk.msgId, "completed");
      window._pendingRequestLink = null;
      showToast("Pedigree registered and linked ✓");
    } catch (linkErr) {
      console.warn("Link error:", linkErr);
    }
  }
}

/* DB mode: persist the animal via the API, then refresh the register. */
async function saveAnimalServer(data, addAnother) {
  try {
    // Auto-assign the Ear Tag on a NEW registration when left blank, using the
    // server's atomic per-region+breed counter (safe under concurrency).
    if (!editId && !data.earTag) {
      const cfg = getTagConfig();
      const parts = tagPartsFor(data, cfg);
      if (cfg.enabled && parts.key) {
        try {
          const n = await window.LpDB.nextTag(parts.key, cfg.start);
          if (n) data.earTag = formatTag(parts.regionCode, parts.breedCode, n, cfg.padding);
        } catch (e) {}
      }
    }
    const newId = await window.LpDB.saveAnimal(data);
    pendPh = {};
    const wasEdit = !!editId;
    if (addAnother && !wasEdit) {
      showToast("Registered ✓ — add the next one");
      const keepSex = document.getElementById("f_sex")?.value || "Female";
      const keepOwner = document.getElementById("f_ownerId")?.value || "";
      const keepProv = document.getElementById("f_province")?.value || "";
      editId = null;
      clearForm();
      const sx = document.getElementById("f_sex"); if (sx) sx.value = keepSex;
      if (typeof updateRoleField === "function") updateRoleField();
      populateOwnerSelect(keepOwner);
      const oe = document.getElementById("f_ownerId"); if (oe) oe.value = keepOwner;
      if (document.getElementById("f_province")) document.getElementById("f_province").value = keepProv;
      setFormSections(true);
      renderSBServer({ append: false });
      setTimeout(() => document.getElementById("f_name")?.focus(), 30);
      return;
    }
    showToast(wasEdit ? "Updated ✓" : "Registered ✓");
    closeModal();
    renderSBServer({ append: false });
    if (newId) selAnimal(String(newId));
  } catch (err) {
    alert("Save failed: " + (err.message || err));
  }
}

/* ── Edit / Delete ── */
function doEdit(id) {
  const a = byId(id);
  if (!a) return;
  editId = id;
  clearForm();
  document.getElementById("modalTitle").textContent = "Edit Animal — " + a.name;
  document.getElementById("modal").classList.add("on");
  setFormSections(false); // editing: show everything so existing data is visible
  selBreed(a.breed || "Holstein");
  lpBuildDefectForm(a.defects || {});
  const s = (el, v) => {
    const e = document.getElementById(el);
    if (e && v !== null && v !== undefined) e.value = v || "";
  };
  [
    "name",
    "regId",
    "dob",
    "lact",
    "color",
    "earTag",
    "sireName",
    "sireId",
    "sireDob",
    "ssName",
    "ssId",
    "sdName",
    "sdId",
    "sdMilk",
    "sdFat",
    "sdProt",
    "damName",
    "damId",
    "damDob",
    "damMilk",
    "damFat",
    "damProt",
    "damBreed",
    "sireBreed",
    "dsName",
    "dsId",
    "ddName",
    "ddId",
    "ddMilk",
    "ddFat",
    "ddProt",
    "milk",
    "fat",
    "prot",
    "peak",
    "dim",
    "scs",
    "calving",
    "notes",
  ].forEach(k => {
    const id2 = "f_" + k;
    const val = a[k] || "";
    s(id2, val);
  });
  s("f_sex", a.sex || "Female");
  if (a.sex === "Male") {
    s("f_role", a.role || "Sire");
    const rfr = document.getElementById("roleFieldRow");
    if (rfr) rfr.style.display = "block";
  }
  populateOwnerSelect(a.ownerId || "");
  s("f_ownerId", a.ownerId || "");
  const pe = document.getElementById("f_province");
  if (pe && typeof getProvincesAF === "function" && pe.options.length <= 1) {
    pe.innerHTML =
      '<option value="">— Province —</option>' +
      getProvincesAF()
        .map(p => `<option>${p}</option>`)
        .join("");
  }
  if (pe && a.province) pe.value = a.province;
  if (a.province && typeof cascadeDistrict === "function") {
    cascadeDistrict("f_province", "f_district");
    setTimeout(() => {
      const di = document.getElementById("f_district");
      if (di && a.district) di.value = a.district;
    }, 100);
  }
  selSireId = a.sireLinkedId || null;
  selDamId = a.damLinkedId || null;
  // Breed comp override
  const bco = a.breedCompOverride || {};
  s("f_bc_ho", bco["Holstein"] || "");
  s("f_bc_mo", bco["Montbeliard"] || "");
  s("f_bc_lc", bco["Local"] || "");
  setTimeout(() => typeof updateBCPreview === "function" && updateBCPreview(), 50);
}

function doDelete(id) {
  const a = byId(id);
  if (!a) return;
  if (!confirm(`Delete "${a.name}"?`)) return;
  if (DB_MODE) {
    window.LpDB
      .deleteAnimal(id)
      .then(() => {
        animals = animals.filter(x => String(x.id) !== String(id));
        if (String(curId) === String(id)) curId = null;
        renderSBServer({ append: false });
        const pg = document.getElementById("pg-herd");
        if (pg) pg.innerHTML = '<div class="empty"><div class="ei">🐄</div><h3>Select or add an animal</h3></div>';
        showToast("Deleted ✓");
      })
      .catch(err => alert("Delete failed: " + (err.message || err)));
    return;
  }
  animals = animals.filter(x => x.id !== id);
  if (curId === id) curId = animals.length ? animals[0].id : null;
  persist();
  renderSB();
  if (curId) renderHerd();
  else {
    const pg = document.getElementById("pg-herd");
    if (pg)
      pg.innerHTML =
        '<div class="empty"><div class="ei">🐄</div><h3>Select or add an animal</h3></div>';
  }
  showToast("Deleted ✓");
}

/* ════════════════════════════════════════════════════════════════
   LOGO MANAGEMENT
   Stored as base64 data URL in localStorage key 'lp_logo'
   Falls back to the built-in SVG cow illustration
════════════════════════════════════════════════════════════════ */
const LOGO_KEY = "lp_logo";
const DEFAULT_SVG_LOGO = `<svg viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
  <ellipse cx="32" cy="34" rx="18" ry="16" fill="white" opacity="0.95"/>
  <ellipse cx="32" cy="46" rx="10" ry="6" fill="white" opacity="0.85"/>
  <ellipse cx="16" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(-15 16 26)"/>
  <ellipse cx="48" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(15 48 26)"/>
  <path d="M18 20 Q10 8 6 6 Q12 10 16 22" fill="white" opacity="0.85"/>
  <path d="M46 20 Q54 8 58 6 Q52 10 48 22" fill="white" opacity="0.85"/>
  <circle cx="26" cy="31" r="2.5" fill="#1a4a2e"/>
  <circle cx="38" cy="31" r="2.5" fill="#1a4a2e"/>
  <ellipse cx="32" cy="42" rx="7" ry="4.5" fill="#2d7a4f" opacity="0.6"/>
  <circle cx="29" cy="42" r="1.5" fill="#1a4a2e"/>
  <circle cx="35" cy="42" r="1.5" fill="#1a4a2e"/>
</svg>`;

function getLogoDataUrl() {
  try {
    return localStorage.getItem(LOGO_KEY) || "";
  } catch (e) {
    return "";
  }
}
function saveLogoDataUrl(url) {
  try {
    localStorage.setItem(LOGO_KEY, url);
  } catch (e) {}
}
function clearLogo() {
  try {
    localStorage.removeItem(LOGO_KEY);
  } catch (e) {}
}

/* Returns an <img> or <svg> element HTML string for a given size circle */
function logoCircleHTML(size, extraStyle) {
  const url = getLogoDataUrl();
  const s = extraStyle || "";
  if (url) {
    return `<div style="width:${size}px;height:${size}px;background:rgba(255,255,255,.15);border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid rgba(255,255,255,.35);flex-shrink:0;overflow:hidden;${s}">
      <img src="${url}" style="width:100%;height:100%;object-fit:contain;border-radius:50%">
    </div>`;
  }
  const svgSize = Math.round(size * 0.65);
  return `<div style="width:${size}px;height:${size}px;background:rgba(255,255,255,.15);border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid rgba(255,255,255,.35);flex-shrink:0;${s}">
    <svg width="${svgSize}" height="${svgSize}" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
      <ellipse cx="32" cy="34" rx="18" ry="16" fill="white" opacity="0.95"/>
      <ellipse cx="32" cy="46" rx="10" ry="6" fill="white" opacity="0.85"/>
      <ellipse cx="16" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(-15 16 26)"/>
      <ellipse cx="48" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(15 48 26)"/>
      <path d="M18 20 Q10 8 6 6 Q12 10 16 22" fill="white" opacity="0.85"/>
      <path d="M46 20 Q54 8 58 6 Q52 10 48 22" fill="white" opacity="0.85"/>
      <circle cx="26" cy="31" r="2.5" fill="#1a4a2e"/>
      <circle cx="38" cy="31" r="2.5" fill="#1a4a2e"/>
      <ellipse cx="32" cy="42" rx="7" ry="4.5" fill="#2d7a4f" opacity="0.6"/>
      <circle cx="29" cy="42" r="1.5" fill="#1a4a2e"/>
      <circle cx="35" cy="42" r="1.5" fill="#1a4a2e"/>
    </svg>
  </div>`;
}

/* Returns logo for print certificate (img tag or inline SVG) */
function logoCertHTML(size) {
  const url = getLogoDataUrl();
  if (url) {
    return `<img src="${url}" style="width:${size}px;height:${size}px;object-fit:contain;border-radius:50%;border:2px solid rgba(255,255,255,.35)">`;
  }
  return `<div style="width:${size}px;height:${size}px;background:rgba(255,255,255,.15);border-radius:50%;display:flex;align-items:center;justify-content:center;border:2px solid rgba(255,255,255,.35)">
    <svg width="${Math.round(size * 0.65)}" height="${Math.round(size * 0.65)}" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
      <ellipse cx="32" cy="34" rx="18" ry="16" fill="white" opacity="0.95"/>
      <ellipse cx="32" cy="46" rx="10" ry="6" fill="white" opacity="0.85"/>
      <ellipse cx="16" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(-15 16 26)"/>
      <ellipse cx="48" cy="26" rx="6" ry="9" fill="white" opacity="0.9" transform="rotate(15 48 26)"/>
      <path d="M18 20 Q10 8 6 6 Q12 10 16 22" fill="white" opacity="0.85"/>
      <path d="M46 20 Q54 8 58 6 Q52 10 48 22" fill="white" opacity="0.85"/>
      <circle cx="26" cy="31" r="2.5" fill="#1a4a2e"/>
      <circle cx="38" cy="31" r="2.5" fill="#1a4a2e"/>
      <ellipse cx="32" cy="42" rx="7" ry="4.5" fill="#2d7a4f" opacity="0.6"/>
      <circle cx="29" cy="42" r="1.5" fill="#1a4a2e"/>
      <circle cx="35" cy="42" r="1.5" fill="#1a4a2e"/>
    </svg>
  </div>`;
}

/* Upload + resize logo from file input */
function handleLogoUpload(inp) {
  if (!inp.files || !inp.files[0]) return;
  const reader = new FileReader();
  reader.onload = function (e) {
    const img = new Image();
    img.onload = function () {
      const c = document.createElement("canvas");
      const MAX = 200;
      let w = img.width,
        h = img.height;
      if (w > MAX) {
        h = Math.round((h * MAX) / w);
        w = MAX;
      }
      if (h > MAX) {
        w = Math.round((w * MAX) / h);
        h = MAX;
      }
      c.width = w;
      c.height = h;
      c.getContext("2d").drawImage(img, 0, 0, w, h);
      const url = c.toDataURL("image/png", 0.9);
      saveLogoDataUrl(url);
      renderBackupPage();
      setTimeout(renderUserMgmt, 50);
      // Refresh all logo spots
      refreshLogos();
      showToast("Logo uploaded ✓ — visible everywhere");
    };
    img.src = e.target.result;
  };
  reader.readAsDataURL(inp.files[0]);
}

/* Refresh all logo displays after upload */
function refreshLogos() {
  // Header logo
  document.querySelectorAll(".hdr-logo-spot").forEach(el => {
    el.innerHTML = logoCircleHTML(42);
  });
  // User portal logo
  document.querySelectorAll(".up-logo-spot").forEach(el => {
    el.innerHTML = logoCircleHTML(34);
  });
  // Login logo
  document.querySelectorAll(".login-logo-spot").forEach(el => {
    el.innerHTML = logoCircleHTML(72);
  });
}

/* ════════════════════════════════════════════════════════════════
   CERTIFICATE PRINT SETTINGS
   Stored in localStorage key 'lp_cert_settings'
════════════════════════════════════════════════════════════════ */
const CERT_CFG_KEY = "lp_cert_settings";

function getCertSettings() {
  try {
    const s = JSON.parse(localStorage.getItem(CERT_CFG_KEY) || "null");
    if (s) return s;
  } catch (e) {}
  // Defaults
  return {
    companyName: "Larspeenghar Livestock & Agriculture Company",
    certSubtitle: "Official Pedigree Certificate · Islamic Republic of Afghanistan",
    sig1Role: "",
    sig1Org: "",
    sig2Role: "",
    sig2Org: "",
    sig3Role: "Larspeenghar Authority",
    sig3Org: "Larspeenghar Livestock & Agriculture Co.",
    numSigs: 3,
    footerText: "Larspeenghar Livestock & Agriculture Company",
  };
}

function saveCertSettings(s) {
  try {
    localStorage.setItem(CERT_CFG_KEY, JSON.stringify(s));
  } catch (e) {}
}

/* Render certificate settings card inside renderBackupPage */
function renderCertSettingsCard() {
  const cs = getCertSettings();
  return `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#854d0e,#b45309);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">📋 Pedigree Certificate Settings</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.75)">Controls the printed pedigree certificate layout and signatures</span>
    </div>
    <div class="cb">
      <!-- Company info -->
      <div class="st" style="background:#854d0e;color:#fff">🏢 Certificate Header</div>
      <div style="display:grid;grid-template-columns:1fr;gap:9px;margin-bottom:12px">
        <div class="fr">
          <label>Company Name (Line 1)</label>
          <input id="cs_compName" value="${cs.companyName}" placeholder="e.g. Larspeenghar Livestock & Agriculture Company">
        </div>
        <div class="fr">
          <label>Certificate Subtitle (Line 2)</label>
          <input id="cs_subtitle" value="${cs.certSubtitle}" placeholder="e.g. Official Pedigree Certificate · Afghanistan">
        </div>
        <div class="fr">
          <label>Footer Text</label>
          <input id="cs_footer" value="${cs.footerText}" placeholder="Footer printed at bottom of certificate">
        </div>
      </div>

      <!-- Signatures -->
      <div class="st" style="background:#854d0e;color:#fff">✍️ Signature Blocks</div>
      <div style="font-family:sans-serif;font-size:11px;color:var(--tl);margin-bottom:10px">
        Configure 1–3 signature blocks. Leave Role blank to hide that signature entirely.
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px">
        ${[1, 2, 3].map(n => `<button class="btn bsm ${cs.numSigs === n ? "bg" : "bd"}" onclick="setCertSigCount(${n})" id="cs_sigbtn${n}" style="font-size:12px">${n} Signature${n > 1 ? "s" : ""}</button>`).join("")}
      </div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px;margin-bottom:12px" id="cs_sig_grid">
        ${[1, 2, 3]
          .map(
            n => `
        <div style="border:1px solid var(--bdr);border-radius:var(--r);padding:12px;background:${n > cs.numSigs ? "#f5f5f5" : "#fff"};opacity:${n > cs.numSigs ? 0.5 : 1}" id="cs_sigblock${n}">
          <div style="font-family:sans-serif;font-size:11px;font-weight:700;color:var(--gd);margin-bottom:8px">Signature ${n}</div>
          <div class="fr" style="margin-bottom:6px">
            <label style="font-size:10px">Role / Title</label>
            <input id="cs_sig${n}role" value="${n === 1 ? cs.sig1Role : n === 2 ? cs.sig2Role : cs.sig3Role}"
              placeholder="${n === 3 ? "e.g. Larspeenghar Authority" : "Leave blank to hide this signature"}"
              style="font-size:12px">
          </div>
          <div class="fr">
            <label style="font-size:10px">Organisation (optional)</label>
            <input id="cs_sig${n}org" value="${n === 1 ? cs.sig1Org : n === 2 ? cs.sig2Org : cs.sig3Org}"
              placeholder="Organisation name below role"
              style="font-size:12px">
          </div>
        </div>`
          )
          .join("")}
      </div>

      <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
        <button class="btn bg" onclick="saveCertSettingsForm()" style="font-size:12px">💾 Save Certificate Settings</button>
        <button class="btn bd" onclick="resetCertSettings()" style="font-size:12px">↺ Reset to Defaults</button>
        <span id="cs_msg" style="font-family:sans-serif;font-size:11px"></span>
      </div>
    </div>
  </div>`;
}

function setCertSigCount(n) {
  const cs = getCertSettings();
  cs.numSigs = n;
  saveCertSettings(cs);
  renderBackupPage();
  setTimeout(renderUserMgmt, 50);
}

function saveCertSettingsForm() {
  const g = id => {
    const e = document.getElementById(id);
    return e ? e.value.trim() : "";
  };
  const cs = {
    companyName: g("cs_compName") || "Larspeenghar Livestock & Agriculture Company",
    certSubtitle: g("cs_subtitle") || "Official Pedigree Certificate",
    footerText: g("cs_footer") || "Larspeenghar Livestock & Agriculture Company",
    sig1Role: g("cs_sig1role"),
    sig1Org: g("cs_sig1org"),
    sig2Role: g("cs_sig2role"),
    sig2Org: g("cs_sig2org"),
    sig3Role: g("cs_sig3role"),
    sig3Org: g("cs_sig3org"),
    numSigs: getCertSettings().numSigs,
  };
  saveCertSettings(cs);
  const msg = document.getElementById("cs_msg");
  if (msg) {
    msg.textContent = "✅ Saved!";
    msg.style.color = "#16a34a";
    setTimeout(() => (msg.textContent = ""), 2000);
  }
  showToast("Certificate settings saved ✓");
}

function resetCertSettings() {
  if (!confirm("Reset certificate settings to defaults?")) return;
  localStorage.removeItem(CERT_CFG_KEY);
  renderBackupPage();
  setTimeout(renderUserMgmt, 50);
  showToast("Certificate settings reset ✓");
}

/* ── Sire card builder ── */
function buildSireCard(a) {
  const off = offspring(a.id);
  const cows = off.filter(o => o.sex === "Female");
  const bulls = off.filter(o => o.sex === "Male");
  const avgMilk = cows.filter(c => c.milk).length
    ? (
        cows.filter(c => c.milk).reduce((s, c) => s + parseFloat(c.milk), 0) /
        cows.filter(c => c.milk).length
      ).toFixed(0)
    : null;
  const bc = BREEDS_CFG[normBreed(a.breed)] || { color: "#3a6bc4" };
  return `<div style="border:1px solid var(--bdr);border-radius:var(--rl);overflow:hidden;margin-bottom:10px;border-top:3px solid ${bc.color}">
    <div style="display:flex;align-items:center;gap:10px;padding:11px 14px;background:#fafafa;flex-wrap:wrap;cursor:pointer" onclick="selAnimal('${a.id}');goPage('herd')">
      ${a.photo ? `<img src="${a.photo}" style="width:38px;height:38px;border-radius:50%;object-fit:cover;border:2px solid ${bc.color}33;flex-shrink:0">` : `<div style="width:38px;height:38px;border-radius:50%;background:${bc.color}22;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0">🐂</div>`}
      <div style="flex:1;min-width:0">
        <div style="font-weight:bold;font-size:14px;color:${bc.color}">${a.name}</div>
        <div style="font-family:sans-serif;font-size:10px;color:var(--tl)">${a.regId || "—"} · ${a.breed === "Montbeliard" ? "Montbéliarde" : a.breed} · Born: ${fmtD(a.dob)}</div>
      </div>
      <div style="display:flex;gap:5px;flex-wrap:wrap;flex-shrink:0">
        <button class="btn bg bsm" onclick="event.stopPropagation();doEdit('${a.id}')" style="font-size:11px">✏️ Edit</button>
        <button class="btn bw bsm" onclick="event.stopPropagation();openNewProgeny('${a.id}')" style="font-size:11px;border:1px solid var(--gm)">＋ Progeny</button>
        <button class="btn bd bsm" onclick="event.stopPropagation();promoteSon&&promoteSon('${a.id}','Son')" style="font-size:11px">↓ Son</button>
      </div>
    </div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:7px;padding:10px 14px;background:#fff">
      ${[
        [off.length, "Offspring", "#1a4a2e"],
        [cows.length, "🐄 Daughters", "#7c3aed"],
        [bulls.length, "🐃 Sons", "#3a6bc4"],
        [avgMilk ? Number(avgMilk).toLocaleString() + " kg" : "—", "Avg Daughter Milk", "#c07a00"],
      ]
        .map(
          ([
            v,
            l,
            c,
          ]) => `<div style="text-align:center;padding:7px;background:#f9fafb;border-radius:6px;border:1px solid var(--bdr)">
          <div style="font-size:clamp(14px,2vw,18px);font-weight:bold;color:${c}">${v}</div>
          <div style="font-size:9px;font-family:sans-serif;color:var(--tl)">${l}</div>
        </div>`
        )
        .join("")}
    </div>
  </div>`;
}

function buildSonRow(a) {
  const bc = BREEDS_CFG[normBreed(a.breed)] || { color: "#92400e" };
  return `<div style="display:flex;align-items:center;gap:9px;padding:8px 10px;border-bottom:1px solid var(--bdr);cursor:pointer;border-radius:5px" onclick="selAnimal('${a.id}');goPage('herd')" onmouseover="this.style.background='var(--gp)'" onmouseout="this.style.background='transparent'">
    ${a.photo ? `<img src="${a.photo}" style="width:28px;height:28px;border-radius:50%;object-fit:cover">` : `<span style="font-size:16px">🐃</span>`}
    <div style="flex:1;min-width:0">
      <div style="font-weight:bold;font-size:12px">${a.name}</div>
      <div style="font-size:10px;font-family:sans-serif;color:var(--tl)">${a.regId || "—"} · Born: ${fmtD(a.dob)}</div>
    </div>
    <span style="font-size:9px;background:${bc.color}22;color:${bc.color};padding:2px 8px;border-radius:8px;font-weight:700;flex-shrink:0">${a.breed === "Montbeliard" ? "Montbéliarde" : a.breed}</span>
    <button class="btn bg bsm" onclick="event.stopPropagation();promoteSon&&promoteSon('${a.id}','Sire')" style="font-size:10px;flex-shrink:0">▲ Promote</button>
  </div>`;
}

function openNewProgeny(sireId) {
  const sa = byId(sireId);
  if (!sa) return;
  if (!confirm("Add daughter or bull son?\n\nClick OK for Daughter, Cancel for Bull Son")) {
    openNew("", "Male", "");
    setTimeout(() => {
      document.getElementById("f_sireLinkedId") || void 0;
      const sn = document.getElementById("f_sireName");
      if (sn) sn.value = sa.name;
      const si = document.getElementById("f_sireId");
      if (si) si.value = sa.regId || "";
      const sb = document.getElementById("f_sireBreed");
      if (sb) sb.value = sa.breed || "Holstein";
      selSireId = sireId;
    }, 100);
  } else {
    openNew("", "Female", "");
    setTimeout(() => {
      const sn = document.getElementById("f_sireName");
      if (sn) sn.value = sa.name;
      const si = document.getElementById("f_sireId");
      if (si) si.value = sa.regId || "";
      const sb = document.getElementById("f_sireBreed");
      if (sb) sb.value = sa.breed || "Holstein";
      selSireId = sireId;
    }, 100);
  }
}

function openNewSon() {
  const sires = animals.filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role));
  if (!sires.length) {
    openNew("", "Male");
    return;
  }
  const opts = sires.map(s => `${s.name} (${s.regId || "—"})`).join("\n");
  const choice = prompt(
    "Register bull son under which sire?\n(Enter sire name or leave blank)\n\n" + opts
  );
  const sire = sires.find(
    s => s.name === choice || (choice && s.name.toLowerCase().includes(choice.toLowerCase()))
  );
  openNew("", "Male");
  if (sire) {
    setTimeout(() => {
      const sn = document.getElementById("f_sireName");
      if (sn) sn.value = sire.name;
      const si = document.getElementById("f_sireId");
      if (si) si.value = sire.regId || "";
      const sb = document.getElementById("f_sireBreed");
      if (sb) sb.value = sire.breed || "Holstein";
      selSireId = sire.id;
      const rf = document.getElementById("f_role");
      if (rf) rf.value = "Son";
    }, 100);
  }
}

function promoteSon(id, newRole) {
  const a = byId(id);
  if (!a) return;
  a.role = newRole;
  persist();
  renderSires();
  showToast(
    `${a.name} ${newRole === "Sire" ? "promoted to Breeding Sire" : "moved to Bull Sons"} ✓`
  );
}

/* ════════════════════════════════════════════════════════════════
   APP STARTUP & SESSION
════════════════════════════════════════════════════════════════ */
window.onload = function initApp() {
  refreshLogos(); // Update all logos (login, header, portal) from localStorage
  const sess = getSession();
  if (sess) {
    document.getElementById("loginGate").style.display = "none";
    if (sess.role === "admin") {
      document.querySelector("header").style.display = "";
      document.querySelector(".shell").style.display = "";
      startApp();
      setTimeout(updateAdminBadge, 500);
    } else {
      showUserPortal(sess);
    }
  }
};

function startApp() {
  if (typeof lpHideLanding === "function") lpHideLanding();
  [
    "larspeenghar_v6",
    "larspeenghar_v5",
    "larspeenghar_v4",
    "larspeenghar_v3",
    "larspeenghar_v2",
    "larspeenghar_v1",
    "dpv3",
    "dpv5",
  ].forEach(k => {
    try {
      localStorage.removeItem(k);
    } catch (e) {}
  });
  if (DB_MODE) {
    // Scalable mode: do NOT load the whole herd. The cache starts empty and
    // the sidebar pages animals from the server on demand.
    animals = [];
  } else {
    try {
      const raw = localStorage.getItem(CFG.storageKey);
      animals = raw ? JSON.parse(raw) : [];
    } catch (e) {
      animals = [];
    }
    if (!Array.isArray(animals) || animals.length === 0) {
      try {
        animals = JSON.parse(JSON.stringify(DEMO));
      } catch (e) {
        animals = [];
      }
      try {
        persist();
      } catch (e) {}
    }
  }
  loadOwners();
  loadIndivOwners();
  renderOwnerBar();
  (function () {
    const provs = typeof getProvincesAF === "function" ? getProvincesAF() : [];
    ["f_province", "cm_province"].forEach(id => {
      const el = document.getElementById(id);
      if (!el) return;
      el.innerHTML =
        "<option value=''>— Select Province —</option>" +
        provs.map(p => `<option>${p}</option>`).join("");
    });
  })();
  try {
    renderSB();
  } catch (e) {
    const el = document.getElementById("alist");
    if (el)
      el.innerHTML =
        '<div style="padding:12px;color:red;font-size:11px">⚠️ ' + e.message + "</div>";
  }
  try {
    if (!DB_MODE && animals.length) curId = animals[0].id;
  } catch (e) {}
  // Open the Dashboard as the landing page after login.
  try {
    goPage("dashboard");
  } catch (e) {}
  setTimeout(refreshLogos, 100);
  try {
    const s = document.createElement("script");
    s.src = "https://accounts.google.com/gsi/client";
    s.async = true;
    document.head.appendChild(s);
  } catch (e) {}
}

/* ════════════════════════════════════════════════════════════════
   BACKUP PAGE
════════════════════════════════════════════════════════════════ */
function renderBackupPage() {
  const el = document.getElementById("pg-backup");
  if (!el) return;
  const photoCount = animals.filter(a => a.photo).length;
  const logoUrl = getLogoDataUrl();

  el.innerHTML = `
  <!-- ══ LOGO MANAGEMENT ══ -->
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1a4a2e,#2d7a4f);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">🖼 Company Logo</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.75)">Appears in the header, login screen, user portal and pedigree certificates</span>
    </div>
    <div class="cb">
      <div style="display:flex;align-items:center;gap:16px;flex-wrap:wrap">
        <!-- Logo preview -->
        <div style="width:80px;height:80px;background:#f0fdf4;border:2px solid #a7f3d0;border-radius:12px;display:flex;align-items:center;justify-content:center;overflow:hidden;flex-shrink:0">
          ${
            logoUrl
              ? `<img id="logo-preview-img" src="${logoUrl}" style="width:100%;height:100%;object-fit:contain">`
              : `<div id="logo-preview-img" style="font-size:36px;line-height:1">🐄</div>`
          }
        </div>
        <!-- Controls -->
        <div style="flex:1;min-width:200px">
          <div style="font-family:sans-serif;font-size:13px;font-weight:700;color:#1a4a2e;margin-bottom:6px">
            ${logoUrl ? "✅ Custom logo uploaded" : "🐄 Using default cow illustration"}
          </div>
          <div style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:8px">
            <label class="btn bg" style="cursor:pointer;font-size:12px;padding:7px 14px">
              📁 Upload New Logo
              <input type="file" accept="image/png,image/jpeg,image/svg+xml,image/gif,image/webp"
                style="display:none" onchange="handleLogoUpload(this)">
            </label>
            ${
              logoUrl
                ? `<button class="btn bd" style="font-size:12px;color:#c0392b"
                   onclick="clearLogo();renderBackupPage();refreshLogos();showToast('Logo removed — using default')">
                   🗑 Remove Logo
                 </button>`
                : ""
            }
          </div>
          <div style="font-family:sans-serif;font-size:10px;color:var(--tl);line-height:1.5">
            Accepted formats: PNG · JPG · SVG · WebP<br>
            Recommended: square image, at least 200×200 pixels<br>
            The logo is saved locally and used in the header, login page, user portal and all pedigree certificates.
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- ══ CERTIFICATE SETTINGS ══ -->
  ${renderCertSettingsCard()}

  <!-- ══ BACKUP & RESTORE ══ -->
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1e3a5f,#3a6bc4);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">💾 Backup &amp; Restore</h2>
    </div>
    <div class="cb">
      <div class="bkst" id="bk-status"></div>
      <!-- Stats -->
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:8px;font-family:sans-serif;font-size:11px;margin-bottom:14px">
        <div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:8px 10px;text-align:center">
          <b style="font-size:15px;color:var(--gd)">${animals.length}</b><div style="color:var(--tl)">Animals</div></div>
        <div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:8px 10px;text-align:center">
          <b style="font-size:15px;color:var(--gd)">${owners.length}</b><div style="color:var(--tl)">Herds</div></div>
        <div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:8px 10px;text-align:center">
          <b style="font-size:15px;color:var(--gd)">${photoCount}</b><div style="color:var(--tl)">Photos</div></div>
        <div style="background:#f9f9f9;border:1px solid var(--bdr);border-radius:var(--r);padding:8px 10px;text-align:center">
          <b style="font-size:15px;color:var(--gd)">${(JSON.stringify(animals).length / 1024).toFixed(1)} KB</b><div style="color:var(--tl)">Storage</div></div>
      </div>
      <!-- PC Backup -->
      <div class="st">💻 Complete Backup &amp; Restore</div>
      <div style="font-family:sans-serif;font-size:11px;color:var(--tm);background:#f0fdf4;border:1px solid #a7f3d0;border-radius:var(--r);padding:9px 12px;margin-bottom:10px;line-height:1.5">
        📦 The download is a <b>complete backup of everything</b>: animals &amp; photos, herds, individual owners,
        messages, user logins, certificate settings, company logo and breed averages — plus the authoritative
        server copy when online. Restoring it brings the app back exactly as it was.
      </div>
      <div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:14px">
        <button class="btn bg" onclick="doPCBackup()">⬇ Download Complete Backup</button>
        <label class="btn bw" style="cursor:pointer">⬆ Restore from File
          <input type="file" accept=".json" style="display:none" onchange="doPCRestore(this)">
        </label>
      </div>
      <div style="margin-top:6px">
        <button class="btn br bsm" onclick="resetToDemo()">🔄 Reset to Demo Animals</button>
        <span style="font-family:sans-serif;font-size:10px;color:var(--tl);margin-left:8px">Clears all data and restores 10 example animals</span>
      </div>
    </div>
  </div>

  <!-- ══ USER MANAGEMENT ══ -->
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1e3a5f,#3a6bc4);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">👥 User Management</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.75)">Create and manage logins for herd owners</span>
    </div>
    <div class="cb"><div id="um_container"></div></div>
  </div>`;
}

/* Build a COMPLETE backup bundle from the current browser state.
   Includes every store so a restore fully re-creates the app. */
/* Every persistent localStorage key the app uses. Transient keys (sync retry
   queue, login session) are deliberately excluded from backups. */
const TRANSIENT_KEYS = ["lp_sync_pending", "lp_session_v1"];
const APP_STORAGE_KEYS = [
  "larspeenghar_v7", // animals
  "larspeenghar_v6", // legacy animals (only if still present)
  "lp_owners", // herds
  "lp_indiv_owners", // individual owners
  "lp_messages_v1", // inbox/sent messages
  "lp_users_v1", // logins
  "lp_cert_settings", // certificate settings
  "lp_logo", // company logo (base64)
  "lp_breed_avg", // breed average benchmarks
  "lp_landing_i18n", // landing-page translation overrides
  "lp_landing_photos", // landing-page showcase photos
  "lp_genetic_defects", // admin-edited tracked genetic conditions
  "lp_tag_config", // tag-numbering settings
  "lp_tag_counters", // issued tag serials per region+breed
];

/* Snapshot EVERY app-owned localStorage key into a {key: rawString} map.
   Uses the known list plus a prefix sweep so future keys are captured too. */
function snapshotAllStorage() {
  const snap = {};
  APP_STORAGE_KEYS.forEach(k => {
    const v = localStorage.getItem(k);
    if (v !== null) snap[k] = v;
  });
  try {
    for (let i = 0; i < localStorage.length; i++) {
      const k = localStorage.key(i);
      if (!k || snap[k] !== undefined || TRANSIENT_KEYS.includes(k)) continue;
      if (/^lp_|^larspeenghar_/.test(k)) {
        const v = localStorage.getItem(k);
        if (v !== null) snap[k] = v;
      }
    }
  } catch (e) {}
  return snap;
}

/* Replay a snapshot back into localStorage verbatim. Returns count restored. */
function restoreAllStorage(snap) {
  if (!snap || typeof snap !== "object") return 0;
  let n = 0;
  Object.keys(snap).forEach(k => {
    if (TRANSIENT_KEYS.includes(k)) return;
    try {
      localStorage.setItem(k, snap[k]);
      n++;
    } catch (e) {}
  });
  return n;
}

/* Build a COMPLETE backup of everything in the browser.
   - `localStorage` is the authoritative, exhaustive snapshot used for restore.
   - The structured fields below are human-readable copies for inspection and
     for importing into older builds. Nothing is omitted or stripped. */
function buildFullBackup() {
  const snapshot = snapshotAllStorage();
  // Guarantee logins are always captured: getUsers() falls back to the default
  // admin constant without writing lp_users_v1, so snapshot it explicitly.
  try {
    if (snapshot["lp_users_v1"] == null && typeof getUsers === "function") {
      snapshot["lp_users_v1"] = JSON.stringify(getUsers());
    }
  } catch (e) {}
  let messages = [];
  try { messages = typeof getMessages === "function" ? getMessages() : []; } catch (e) {}
  let settings = {};
  try { settings = typeof getCertSettings === "function" ? getCertSettings() : {}; } catch (e) {}
  let users = [];
  try { users = typeof getUsers === "function" ? getUsers() : []; } catch (e) {}
  let logo = "";
  try { logo = typeof getLogoDataUrl === "function" ? getLogoDataUrl() : ""; } catch (e) {}
  let breedAvg = {};
  try { breedAvg = typeof BREED_AVG !== "undefined" ? BREED_AVG : {}; } catch (e) {}

  return {
    _meta: {
      app: "Larspeenghar Pedigree Manager",
      kind: "full-backup",
      version: "7.1",
      exported: new Date().toISOString(),
      keys: Object.keys(snapshot),
      counts: {
        animals: animals.length,
        owners: owners.length,
        indiv_owners: (typeof indivOwners !== "undefined" ? indivOwners : []).length,
        messages: messages.length,
        users: users.length,
      },
    },
    // Authoritative — every persistent key, restored verbatim.
    localStorage: snapshot,
    // Readable structured copies (inspection / legacy import).
    animals,
    owners,
    indiv_owners: typeof indivOwners !== "undefined" ? indivOwners : [],
    messages,
    settings,
    users,
    logo,
    breedAvg,
  };
}

function triggerDownload(text, filename) {
  const blob = new Blob([text], { type: "application/json" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

async function doPCBackup() {
  const stamp = new Date().toISOString().slice(0, 10);
  const backup = buildFullBackup();
  // When the server is live, also attach its authoritative export (includes
  // bcrypt-hashed logins + server-side data) so the file restores fully in
  // either mode.
  if (window.LpSync && window.LpSync.hasBackend) {
    try {
      const r = await fetch("api.php?action=export_all", { credentials: "same-origin" });
      if (r.ok) backup.server = await r.json();
    } catch (e) {
      console.warn("Server export failed; local snapshot still complete:", e);
    }
  }
  triggerDownload(
    JSON.stringify(backup, null, 2),
    "larspeenghar_backup_" + stamp + ".json"
  );
  const n = Object.keys(backup.localStorage || {}).length;
  showToast(
    (backup.server ? "Complete backup (server + local" : "Complete backup (" + n + " stores") +
      ") downloaded ✓"
  );
}

async function doPCRestore(inp) {
  if (!inp.files || !inp.files[0]) return;
  const file = inp.files[0];
  const reader = new FileReader();
  reader.onload = async function (e) {
    let p;
    try {
      p = JSON.parse(e.target.result);
    } catch (err) {
      alert("Error reading backup: " + err.message);
      return;
    }

    // Accept both the new complete-snapshot format and older structured backups.
    const snap = p && p.localStorage && typeof p.localStorage === "object" ? p.localStorage : null;
    const readKey = (k, fb) => {
      if (snap && snap[k] != null) {
        try { return JSON.parse(snap[k]); } catch (e) { return fb; }
      }
      return fb;
    };
    const bkAnimals = snap ? readKey("larspeenghar_v7", []) : p.animals;
    if (!Array.isArray(bkAnimals)) {
      alert("Invalid backup file — could not find animal data.");
      return;
    }
    const bkOwners = snap ? readKey("lp_owners", []) : p.owners || [];
    const bkIndiv = snap ? readKey("lp_indiv_owners", []) : p.indiv_owners || [];
    const bkMsgs = snap ? readKey("lp_messages_v1", []) : p.messages || [];
    const bkUsers = snap ? readKey("lp_users_v1", []) : p.users || [];
    const hasLogo = snap ? !!snap["lp_logo"] : !!p.logo;
    const hasBreed = snap ? !!snap["lp_breed_avg"] : !!p.breedAvg;

    const summary =
      `Restore this COMPLETE backup?\n\n` +
      `• ${bkAnimals.length} animals\n` +
      `• ${bkOwners.length} herds\n` +
      `• ${bkIndiv.length} individual owners\n` +
      `• ${bkMsgs.length} messages\n` +
      `• ${bkUsers.length} user logins\n` +
      `• Certificate settings${hasLogo ? ", logo" : ""}${hasBreed ? ", breed averages" : ""}\n\n` +
      `This REPLACES all current data.`;
    if (!confirm(summary)) return;

    // ── Server mode: push server-bound stores, then restore local-only keys ──
    if (window.LpSync && window.LpSync.hasBackend) {
      const serverPayload = p.server || {
        animals: bkAnimals,
        owners: bkOwners,
        indiv_owners: bkIndiv,
        messages: bkMsgs,
        settings: snap ? readKey("lp_cert_settings", {}) : p.settings || {},
        users: bkUsers,
      };
      try {
        const r = await fetch("api.php?action=import_all", {
          method: "POST",
          credentials: "same-origin",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ data: serverPayload }),
        });
        if (!r.ok) {
          const b = await r.json().catch(() => ({}));
          throw new Error(b.error || "HTTP " + r.status);
        }
      } catch (err) {
        alert("Server restore failed: " + err.message);
        return;
      }
      // Logo, breed averages and cert settings are browser-local — restore them too.
      ["lp_logo", "lp_breed_avg", "lp_cert_settings"].forEach(k => {
        if (snap && snap[k] != null) {
          try { localStorage.setItem(k, snap[k]); } catch (e) {}
        }
      });
      if (!snap) {
        if (p.logo && typeof saveLogoDataUrl === "function") saveLogoDataUrl(p.logo);
        if (p.settings && typeof saveCertSettings === "function") saveCertSettings(p.settings);
      }
      await window.LpSync.hydrate();
      showToast("Complete restore (server) ✓ — reloading…");
      setTimeout(() => window.location.reload(), 900);
      return;
    }

    // ── Local mode ──
    if (snap) {
      // Replay every key verbatim — the most complete possible restore.
      const n = restoreAllStorage(snap);
      showToast("Complete restore ✓ (" + n + " stores) — reloading…");
      setTimeout(() => window.location.reload(), 700);
      return;
    }

    // Legacy structured backup (older files without a localStorage snapshot).
    animals = bkAnimals;
    persist();
    owners = bkOwners;
    saveOwners();
    if (typeof saveIndivOwners === "function") { indivOwners = bkIndiv; saveIndivOwners(); }
    if (typeof saveMessages === "function") saveMessages(bkMsgs);
    if (p.settings && typeof saveCertSettings === "function") saveCertSettings(p.settings);
    if (p.logo && typeof saveLogoDataUrl === "function") saveLogoDataUrl(p.logo);
    if (bkUsers.length && typeof saveUsers === "function") saveUsers(bkUsers);
    showToast("Restored from legacy backup ✓ — reloading…");
    setTimeout(() => window.location.reload(), 700);
  };
  reader.readAsText(file);
}

function resetToDemo() {
  if (!confirm("Reset to the 10 example animals?\nThis will erase all current data.")) return;
  try {
    ["larspeenghar_v7", "larspeenghar_v6"].forEach(k => {
      try {
        localStorage.removeItem(k);
      } catch (e) {}
    });
    animals = JSON.parse(JSON.stringify(DEMO));
    persist();
    owners = JSON.parse(JSON.stringify(DEMO_OWNERS));
    saveOwners();
    indivOwners = [];
    saveIndivOwners();
    curId = null;
    curTab = "pedigree";
    renderOwnerBar();
    renderSB();
    if (animals.length) selAnimal(animals[0].id);
    renderBackupPage();
    setTimeout(renderUserMgmt, 50);
    showToast("Reset to 10 demo animals ✓");
  } catch (e) {
    alert("Reset failed: " + e.message);
  }
}

/* ════════════════════════════════════════════════════════════════
   USER MANAGEMENT (Admin only)
════════════════════════════════════════════════════════════════ */
function renderUserMgmt() {
  const el = document.getElementById("um_container");
  if (!el) return;
  const users = getUsers();
  el.innerHTML = `
  <div style="overflow-x:auto;margin-bottom:14px">
  <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:12px;min-width:500px">
    <thead><tr style="background:var(--gp)">
      <th style="padding:7px 10px;text-align:left">Username</th>
      <th style="padding:7px 10px">Role</th>
      <th style="padding:7px 10px">Name</th>
      <th style="padding:7px 10px">Assigned Herd</th>
      <th style="padding:7px 10px">Actions</th>
    </tr></thead>
    <tbody>
    ${users
      .map((u, i) => {
        const ow = u.ownerId ? anyOwnerById(u.ownerId) : null;
        return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:7px 10px;font-weight:bold">${u.username}</td>
        <td style="text-align:center;padding:7px 10px">
          <span style="background:${u.role === "admin" ? "#dbeafe" : "#f3e8ff"};color:${u.role === "admin" ? "#1e40af" : "#7c3aed"};padding:2px 8px;border-radius:8px;font-size:10px;font-weight:700">${u.role === "admin" ? "🔐 Admin" : "👤 User"}</span>
        </td>
        <td style="padding:7px 10px">${u.name || "—"}</td>
        <td style="padding:7px 10px;font-size:11px">${ow ? ow.herdName || ow.name : "—"}</td>
        <td style="text-align:center;padding:7px 10px;display:flex;gap:4px;flex-wrap:wrap;justify-content:center">
          <button class="btn bw bsm" onclick="editUserModal('${u.id}')" style="font-size:10px">✏️ Edit</button>
          <button class="btn bg bsm" onclick="openAdminCompose('${u.id}')" style="font-size:10px;background:#3a6bc4">📨 Message</button>
          ${u.id !== "admin" ? `<button class="btn bd bsm" onclick="deleteUser('${u.id}')" style="font-size:10px;color:#c0392b">🗑</button>` : ""}
        </td>
      </tr>`;
      })
      .join("")}
    </tbody>
  </table>
  </div>
  <div id="um_form" style="background:var(--gp);border:1px solid var(--bdr);border-radius:var(--r);padding:14px">
    <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd);margin-bottom:10px" id="um_formTitle">＋ Add New User</div>
    <input type="hidden" id="um_editId">
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:9px">
      <div class="fr"><label>Username *</label><input id="um_uname" placeholder="e.g. ahmad.karimi"></div>
      <div class="fr"><label>Full Name</label><input id="um_name" placeholder="e.g. Ahmad Karimi"></div>
      <div class="fr"><label>Password *</label><input type="password" id="um_pass" placeholder="Min 6 characters"></div>
      <div class="fr"><label>Confirm Password *</label><input type="password" id="um_conf" placeholder="Repeat password"></div>
      <div class="fr"><label>Role</label>
        <select id="um_role" onchange="document.getElementById('um_herd').style.display=this.value==='user'?'':'none'" style="font-size:13px">
          <option value="user">👤 User (Herd owner)</option>
          <option value="admin">🔐 Admin (Full access)</option>
        </select>
      </div>
      <div class="fr" id="um_herd"><label>Assign to Herd</label>
        <select id="um_owner" style="font-size:13px">
          <option value="">— No herd assigned —</option>
          <optgroup label="Registered Herds">${owners.map(o => `<option value="${o.id}">${o.herdName || o.name}</option>`).join("")}</optgroup>
          <optgroup label="Individual Owners">${indivOwners.map(o => `<option value="${o.id}">${o.name}</option>`).join("")}</optgroup>
        </select>
      </div>
    </div>
    <div style="display:flex;gap:7px;margin-top:10px;flex-wrap:wrap">
      <button class="btn bg" onclick="saveUser()" style="font-size:12px">💾 Save User</button>
      <button class="btn bd" onclick="resetUserForm()" style="font-size:12px">Cancel</button>
    </div>
    <div id="um_msg" style="font-size:11px;margin-top:7px"></div>
  </div>`;
}

function resetUserForm() {
  document.getElementById("um_editId").value = "";
  document.getElementById("um_formTitle").textContent = "＋ Add New User";
  ["um_uname", "um_name", "um_pass", "um_conf"].forEach(id => {
    const e = document.getElementById(id);
    if (e) e.value = "";
  });
  const r = document.getElementById("um_role");
  if (r) r.value = "user";
  const o = document.getElementById("um_owner");
  if (o) o.value = "";
  const h = document.getElementById("um_herd");
  if (h) h.style.display = "";
  const m = document.getElementById("um_msg");
  if (m) m.textContent = "";
}

function editUserModal(id) {
  const users = getUsers();
  const u = users.find(x => x.id === id);
  if (!u) return;
  document.getElementById("um_editId").value = id;
  document.getElementById("um_formTitle").textContent = "✏️ Edit User — " + u.username;
  const s = (el, v) => {
    const e = document.getElementById(el);
    if (e) e.value = v || "";
  };
  s("um_uname", u.username);
  s("um_name", u.name);
  s("um_pass", "");
  s("um_conf", "");
  const r = document.getElementById("um_role");
  if (r) r.value = u.role || "user";
  s("um_owner", u.ownerId || "");
  const h = document.getElementById("um_herd");
  if (h) h.style.display = u.role === "user" ? "" : "none";
  const m = document.getElementById("um_msg");
  if (m) m.textContent = "";
  document.getElementById("um_uname")?.scrollIntoView({ behavior: "smooth" });
}

function saveUser() {
  const editId = document.getElementById("um_editId").value;
  const uname = (document.getElementById("um_uname")?.value || "").trim().toLowerCase();
  const name = (document.getElementById("um_name")?.value || "").trim();
  const pass = (document.getElementById("um_pass")?.value || "").trim();
  const conf = (document.getElementById("um_conf")?.value || "").trim();
  const role = document.getElementById("um_role")?.value || "user";
  const ownId = document.getElementById("um_owner")?.value || "";
  const msg = document.getElementById("um_msg");
  if (!uname) {
    if (msg) {
      msg.textContent = "❌ Username required.";
      msg.style.color = "#c0392b";
    }
    return;
  }
  const users = getUsers();
  if (!editId && users.find(u => u.username.toLowerCase() === uname)) {
    if (msg) {
      msg.textContent = "❌ Username already exists.";
      msg.style.color = "#c0392b";
    }
    return;
  }
  if (!editId) {
    if (!pass) {
      if (msg) {
        msg.textContent = "❌ Password required.";
        msg.style.color = "#c0392b";
      }
      return;
    }
    if (pass.length < 6) {
      if (msg) {
        msg.textContent = "❌ Min 6 characters.";
        msg.style.color = "#c0392b";
      }
      return;
    }
    if (pass !== conf) {
      if (msg) {
        msg.textContent = "❌ Passwords do not match.";
        msg.style.color = "#c0392b";
      }
      return;
    }
  }
  if (editId) {
    const u = users.find(x => x.id === editId);
    if (!u) return;
    u.username = uname;
    u.name = name;
    u.role = role;
    u.ownerId = ownId || null;
    if (pass) {
      if (pass.length < 6) {
        if (msg) {
          msg.textContent = "❌ Password min 6 chars.";
          msg.style.color = "#c0392b";
        }
        return;
      }
      if (pass !== conf) {
        if (msg) {
          msg.textContent = "❌ Passwords do not match.";
          msg.style.color = "#c0392b";
        }
        return;
      }
      u.password = pass;
    }
  } else {
    users.push({
      id: "u" + Date.now(),
      username: uname,
      password: pass,
      role,
      ownerId: ownId || null,
      name,
    });
  }
  saveUsers(users);
  renderUserMgmt();
  resetUserForm();
  if (msg) {
    msg.textContent = "✅ User " + (editId ? "updated" : "created") + "!";
    msg.style.color = "#16a34a";
  }
}

function deleteUser(id) {
  if (id === "admin") {
    alert("Cannot delete the default admin account.");
    return;
  }
  const users = getUsers();
  const u = users.find(x => x.id === id);
  if (!confirm(`Delete user "${u?.username}"?`)) return;
  saveUsers(users.filter(x => x.id !== id));
  renderUserMgmt();
  showToast("User deleted");
}

/* ════════════════════════════════════════════════════════════
   KINSHIP RENDER
════════════════════════════════════════════════════════════ */
function kinSelAll() {
  animals.forEach(a => kinSel.add(a.id));
  renderKin();
}
function kinSelNone() {
  kinSel.clear();
  const km = document.getElementById("kinMatrix");
  const kc = document.getElementById("kinClusters");
  if (km) km.innerHTML = "";
  if (kc) kc.innerHTML = "";
  renderKin();
}
function renderKin() {
  const chips = document.getElementById("kinChips");
  if (!chips) return;
  chips.innerHTML = animals
    .map(a => {
      const av = a.photo
        ? `<img src="${a.photo}" alt="" style="width:20px;height:20px;border-radius:50%;object-fit:cover">`
        : a.breed === "Holstein"
          ? "🖤"
          : "🟠";
      return `<div class="kchip${kinSel.has(a.id) ? " on" : ""}" onclick="toggleKin('${a.id}')">
      ${av}<div><b>${a.name}</b><span style="display:block;color:var(--tl);font-size:9px">${a.sex === "Male" ? "🐂" : "🐄"} ${a.regId || ""}</span></div>
    </div>`;
    })
    .join("");
  if (kinSel.size >= 2) renderKinMatrix();
}
function toggleKin(id) {
  if (kinSel.has(id)) kinSel.delete(id);
  else kinSel.add(id);
  renderKin();
}
let _kinLoaded = "";
function renderKinMatrix() {
  // DB mode: pull the selected animals' ancestor subgraphs into the cache once,
  // then the existing wrightF()/byId() kinship maths runs unchanged.
  if (DB_MODE && kinSel.size >= 2) {
    const key = [...kinSel].sort().join(",");
    if (key !== _kinLoaded) {
      const el = document.getElementById("kinMatrix");
      if (el) el.innerHTML = '<div style="padding:14px;color:var(--tl);font-family:sans-serif;font-size:12px">Loading kinship…</div>';
      window.LpDB
        .ancestorsBatch([...kinSel], 6)
        .then(rows => { lpCacheMerge(rows); _kinLoaded = key; renderKinMatrix(); })
        .catch(() => { _kinLoaded = key; renderKinMatrix(); });
      return;
    }
  }
  const sel = [...kinSel].map(id => animals.find(a => a.id === id)).filter(Boolean);
  if (sel.length < 2) {
    const el = document.getElementById("kinMatrix");
    if (el) el.innerHTML = "";
    return;
  }
  const el = document.getElementById("kinMatrix");
  if (!el) return;
  let html = `<div class="card"><div class="ch"><h2>🔗 Kinship Matrix</h2></div><div class="cb" style="overflow-x:auto">
  <table style="border-collapse:collapse;font-family:sans-serif;font-size:11px;min-width:300px">
    <thead><tr><th style="padding:6px 10px;background:var(--gp)">Animal</th>
    ${sel.map(a => `<th style="padding:6px 10px;background:var(--gp);white-space:nowrap">${a.name.substring(0, 12)}</th>`).join("")}</tr></thead>
    <tbody>`;
  sel.forEach(a => {
    html += `<tr><td style="padding:6px 10px;font-weight:bold;border-bottom:1px solid var(--bdr)">${a.name.substring(0, 16)}</td>`;
    sel.forEach(b => {
      if (a.id === b.id) {
        html += `<td style="padding:6px 10px;text-align:center;background:#f0fdf4;border:1px solid var(--bdr)">—</td>`;
        return;
      }
      const res = wrightF(a, b, 4);
      const pct = res.F;
      const bg = pct > 12 ? "#fee2e2" : pct > 6 ? "#fef9c3" : pct > 0 ? "#dbeafe" : "#f9fafb";
      const col = pct > 12 ? "#991b1b" : pct > 6 ? "#854d0e" : pct > 0 ? "#1e40af" : "#9ca3af";
      html += `<td style="padding:6px 10px;text-align:center;background:${bg};color:${col};font-weight:${pct > 0 ? 700 : 400};border:1px solid var(--bdr)">${pct > 0 ? pct.toFixed(2) + "%" : "0%"}</td>`;
    });
    html += `</tr>`;
  });
  html += `</tbody></table></div></div>`;
  el.innerHTML = html;
  renderClusters(sel);
}
function renderClusters(sel) {
  const el = document.getElementById("kinClusters");
  if (!el || !sel || sel.length < 2) {
    if (el) el.innerHTML = "";
    return;
  }
  const pairs = [];
  sel.forEach((a, i) =>
    sel.slice(i + 1).forEach(b => {
      const res = wrightF(a, b, 4);
      if (res.F > 0)
        pairs.push({
          a: a.name,
          b: b.name,
          F: res.F,
          common: res.common.map(c => c.label).join(", "),
        });
    })
  );
  if (!pairs.length) {
    el.innerHTML = "";
    return;
  }
  el.innerHTML = `<div class="card"><div class="ch"><h2>🧬 Related Pairs &amp; Shared Ancestors</h2></div>
  <div class="cb" style="overflow-x:auto">
  <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:11px;min-width:400px">
    <thead><tr style="background:var(--gp)"><th style="padding:6px 9px;text-align:left">Animal A</th><th style="padding:6px 9px;text-align:left">Animal B</th><th style="padding:6px 9px">Kinship F%</th><th style="padding:6px 9px;text-align:left">Common Ancestors</th></tr></thead>
    <tbody>${pairs
      .sort((a, b) => b.F - a.F)
      .map(
        (
          p,
          i
        ) => `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
      <td style="padding:6px 9px;font-weight:bold">${p.a}</td>
      <td style="padding:6px 9px;font-weight:bold">${p.b}</td>
      <td style="text-align:center;padding:6px 9px;font-weight:700;color:${p.F > 12 ? "#991b1b" : p.F > 6 ? "#854d0e" : "#1e40af"}">${p.F.toFixed(2)}%</td>
      <td style="padding:6px 9px;font-size:10px;color:var(--tl)">${p.common || "—"}</td>
    </tr>`
      )
      .join("")}</tbody>
  </table></div></div>`;
}

document.getElementById("modal").addEventListener("click", function (e) {
  if (e.target === this) closeModal();
});

/* ── UX: Escape closes any open modal ── */
document.addEventListener("keydown", function (e) {
  if (e.key !== "Escape") return;
  const m = document.getElementById("modal");
  if (m && m.classList.contains("on")) {
    closeModal();
    return;
  }
  // Close request-sire dropdown if open
  const dd = document.getElementById("req_sireDD");
  if (dd && dd.style.display !== "none") dd.style.display = "none";
});

/* ── UX: Global search shortcut — press "/" or Ctrl/Cmd+K anywhere to jump
   straight to the herd search box (fixes "hard to find things"). ── */
document.addEventListener("keydown", function (e) {
  const tag = (e.target && e.target.tagName) || "";
  const typing = /^(INPUT|TEXTAREA|SELECT)$/.test(tag) || (e.target && e.target.isContentEditable);
  const isSlash = e.key === "/" && !typing;
  const isCmdK = (e.ctrlKey || e.metaKey) && (e.key === "k" || e.key === "K");
  if (!isSlash && !isCmdK) return;
  // Don't hijack while a modal or the login gate is open
  const modalOpen = document.getElementById("modal")?.classList.contains("on");
  const gate = document.getElementById("loginGate");
  if (modalOpen || (gate && gate.offsetParent !== null)) return;
  const box = document.getElementById("sbQ");
  if (!box) return;
  e.preventDefault();
  if (typeof goPage === "function") goPage("herd");
  box.focus();
  box.select();
});

/* ── UX: Autofocus login username field when login gate is visible ── */
(function autofocusLogin() {
  try {
    const gate = document.getElementById("loginGate");
    const u = document.getElementById("lg_user");
    if (gate && u && gate.offsetParent !== null) {
      setTimeout(() => u.focus(), 60);
    }
  } catch (e) {}
})();

document.addEventListener("click", function upSireOutsideHandler(e) {
  const dd = document.getElementById("req_sireDD");
  if (dd && !e.target.closest("#req_sireDD") && e.target.id !== "req_sireName")
    dd.style.display = "none";
});

/* ════════════════════════════════════════════════════════════════
   DEEP PEDIGREE — up to 11 generation tree view
   Added 2026-05-28. Companion to the 2–3 gen certificate.
   ════════════════════════════════════════════════════════════════ */

let dpState = { animalId: "", depth: 6, collapsed: new Set(), trail: [] };

function dpEscape(s) {
  return String(s == null ? "" : s).replace(/[&<>"']/g, ch => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#39;",
  })[ch]);
}

/**
 * Build an ancestor node tree from an animal.
 * Each node: { kind, name, regId, breed, dob, sex, linked, id, sire, dam, gen, path }
 *   kind = "self" | "sire" | "dam" | "unknown"
 *   path = "S","D","SS","SD",...  (unique key for collapse state)
 *
 * Walks linked records first; falls back to manually entered name fields
 * (sireName, ssName, sdName, dsName, ddName) so unlinked data still appears.
 */
function dpBuildTree(animal, maxDepth) {
  function nodeLinked(a, kind, gen, path) {
    return {
      kind,
      name: a.name || "",
      regId: a.regId || "",
      breed: a.breed || "",
      dob: a.dob || "",
      sex: a.sex || (kind === "sire" ? "Male" : kind === "dam" ? "Female" : ""),
      linked: true,
      id: a.id,
      gen,
      path,
      sire: null,
      dam: null,
    };
  }
  function nodeName(name, regId, kind, gen, path) {
    return {
      kind,
      name: name || "",
      regId: regId || "",
      breed: "",
      dob: "",
      sex: kind === "sire" ? "Male" : kind === "dam" ? "Female" : "",
      linked: false,
      id: "",
      gen,
      path,
      sire: null,
      dam: null,
    };
  }

  function walk(a, gen, path) {
    if (!a || gen > maxDepth) return { sire: null, dam: null };

    // ── Sire ──
    let sire = null;
    if (a.sireLinkedId) {
      const sa = byId(a.sireLinkedId);
      if (sa) sire = nodeLinked(sa, "sire", gen, path + "S");
    }
    if (!sire && (a.sireName || a.sireId)) {
      sire = nodeName(a.sireName, a.sireId, "sire", gen, path + "S");
      if (gen + 1 <= maxDepth) {
        if (a.ssName) sire.sire = nodeName(a.ssName, a.ssId, "sire", gen + 1, path + "SS");
        if (a.sdName) sire.dam = nodeName(a.sdName, a.sdId, "dam", gen + 1, path + "SD");
      }
    }
    if (sire && sire.linked && gen < maxDepth) {
      const linkedRec = byId(sire.id);
      const inner = walk(linkedRec, gen + 1, path + "S");
      sire.sire = inner.sire;
      sire.dam = inner.dam;
    }

    // ── Dam ──
    let dam = null;
    if (a.damLinkedId) {
      const da = byId(a.damLinkedId);
      if (da) dam = nodeLinked(da, "dam", gen, path + "D");
    }
    if (!dam && (a.damName || a.damId)) {
      dam = nodeName(a.damName, a.damId, "dam", gen, path + "D");
      if (gen + 1 <= maxDepth) {
        if (a.dsName) dam.sire = nodeName(a.dsName, a.dsId, "sire", gen + 1, path + "DS");
        if (a.ddName) dam.dam = nodeName(a.ddName, a.ddId, "dam", gen + 1, path + "DD");
      }
    }
    if (dam && dam.linked && gen < maxDepth) {
      const linkedRec = byId(dam.id);
      const inner = walk(linkedRec, gen + 1, path + "D");
      dam.sire = inner.sire;
      dam.dam = inner.dam;
    }

    return { sire, dam };
  }

  const root = {
    kind: "self",
    name: animal.name || "",
    regId: animal.regId || "",
    breed: animal.breed || "",
    dob: animal.dob || "",
    sex: animal.sex || "",
    linked: true,
    id: animal.id,
    gen: 0,
    path: "",
    sire: null,
    dam: null,
  };
  const inner = walk(animal, 1, "");
  root.sire = inner.sire;
  root.dam = inner.dam;
  return root;
}

function dpCountTree(root) {
  let known = 0;
  function w(n) {
    if (!n) return;
    if (n.kind !== "self") known++;
    w(n.sire);
    w(n.dam);
  }
  w(root.sire);
  w(root.dam);
  return { known };
}

function dpNodeCard(n) {
  if (!n) {
    return `<div class="dpn dpn-unknown" title="Ancestor not recorded">?</div>`;
  }
  const cls =
    n.kind === "self"
      ? "dpn-self"
      : n.kind === "sire" || n.sex === "Male"
        ? "dpn-sire"
        : "dpn-dam";
  // Clicking ANY animal in the tree opens its own family tree:
  //  • linked records re-root instantly by id
  //  • name-only ancestors are resolved to a registered animal by reg/name
  // Only truly unrecorded ancestors stay non-clickable (nothing to open).
  const isReal = n.kind !== "self" && (n.linked || !!n.name);
  const onClick = !isReal
    ? ""
    : n.linked
      ? `onclick="event.stopPropagation();dpReRoot(&quot;${n.id}&quot;);"`
      : `onclick="event.stopPropagation();dpReRootResolve(decodeURIComponent(&quot;${encodeURIComponent(n.name || "")}&quot;),decodeURIComponent(&quot;${encodeURIComponent(n.regId || "")}&quot;));"`;
  const linkBadge = isReal ? ` <span class="dpn-link" title="Click to open this animal's family tree">🌳</span>` : "";
  const regLine = n.regId ? `<div class="dpn-reg">${dpEscape(n.regId)}</div>` : "";
  const title = isReal ? ' title="Click to view this animal&apos;s own family tree"' : "";
  return `<div class="dpn ${cls}${isReal ? " dpn-lk" : ""}"${title} ${onClick}>
    <div class="dpn-name">${dpEscape(n.name || "—")}${linkBadge}</div>
    ${regLine}
  </div>`;
}

function dpRenderSubtree(node, depth, maxDepth) {
  if (!node) return `<div class="dp-branch">${dpNodeCard(null)}</div>`;
  const collapsed = dpState.collapsed.has(node.path);
  const hasKids = depth < maxDepth && (node.sire || node.dam);
  const toggle = hasKids
    ? `<button class="dp-toggle" onclick="dpToggle(&quot;${node.path}&quot;);event.stopPropagation();" title="${collapsed ? "Expand" : "Collapse"}">${collapsed ? "+" : "−"}</button>`
    : "";
  const card = `<div class="dp-card-wrap">${dpNodeCard(node)}${toggle}</div>`;
  if (!hasKids || collapsed) return `<div class="dp-branch">${card}</div>`;
  return `<div class="dp-branch">
    ${card}
    <div class="dp-children">
      <div class="dp-child dp-child-sire">${dpRenderSubtree(node.sire, depth + 1, maxDepth)}</div>
      <div class="dp-child dp-child-dam">${dpRenderSubtree(node.dam, depth + 1, maxDepth)}</div>
    </div>
  </div>`;
}

function dpToggle(path) {
  if (dpState.collapsed.has(path)) dpState.collapsed.delete(path);
  else dpState.collapsed.add(path);
  renderDeepPed();
}
function dpExpandAll() {
  dpState.collapsed.clear();
  renderDeepPed();
}
function dpCollapseAll() {
  const a = byId(dpState.animalId);
  if (!a) return;
  const root = dpBuildTree(a, dpState.depth);
  dpState.collapsed.clear();
  function w(n) {
    if (!n) return;
    if (n.path.length >= 2) dpState.collapsed.add(n.path);
    w(n.sire);
    w(n.dam);
  }
  w(root.sire);
  w(root.dam);
  renderDeepPed();
}
function dpSetAnimal(id) {
  dpState.animalId = id;
  dpState.collapsed.clear();
  // Start a fresh exploration trail at this animal.
  const a = byId(id);
  dpState.trail = [{ id: String(id), name: a ? a.name : id }];
  dpEnsureAndRender();
}
function dpSetDepth(v) {
  dpState.depth = Math.max(1, Math.min(22, parseInt(v, 10) || 1));
  dpEnsureAndRender();
}

/* Click an ancestor in the tree → re-root the whole view on that animal so its
   own family tree + inbreeding can be inspected. A breadcrumb tracks the path. */
function dpReRoot(id) {
  dpState.animalId = String(id);
  dpState.collapsed.clear();
  const a = byId(id);
  const name = a ? a.name : id;
  // Avoid duplicate consecutive crumbs.
  if (!dpState.trail.length || String(dpState.trail[dpState.trail.length - 1].id) !== String(id)) {
    dpState.trail.push({ id: String(id), name });
  }
  dpEnsureAndRender();
}
/* Re-root on a name-only ancestor by resolving it to a registered animal
   (exact reg id first, then exact name). Searches the cache, then the server
   in DB mode. If no record exists there is no tree to open. */
function dpReRootResolve(name, reg) {
  const nn = (name || "").trim().toLowerCase();
  const rr = (reg || "").trim().toLowerCase();
  const matchIn = list => {
    let f = null;
    if (rr) f = list.find(a => (a.regId || "").trim().toLowerCase() === rr);
    if (!f && nn) f = list.find(a => (a.name || "").trim().toLowerCase() === nn);
    return f;
  };
  const local = matchIn(animals);
  if (local) { dpReRoot(local.id); return; }
  if (DB_MODE) {
    window.LpDB
      .animalsPage({ q: reg || name, page: 1, perPage: 10, sort: "name" })
      .then(r => {
        lpCacheMerge(r.rows || []);
        const f = matchIn(r.rows || []);
        if (f) dpReRoot(f.id);
        else showToast("No registered record for “" + (name || reg) + "”");
      })
      .catch(() => showToast("No record found for this ancestor"));
    return;
  }
  showToast("“" + (name || reg) + "” isn't a registered animal yet");
}

/* Jump back to an earlier crumb in the exploration trail. */
function dpTrailTo(idx) {
  if (idx < 0 || idx >= dpState.trail.length) return;
  dpState.trail = dpState.trail.slice(0, idx + 1);
  dpState.animalId = dpState.trail[idx].id;
  dpState.collapsed.clear();
  dpEnsureAndRender();
}
/* Breadcrumb trail of explored animals (click to jump back). */
function dpTrailHTML() {
  if (!dpState.trail || dpState.trail.length < 2) return "";
  const crumbs = dpState.trail
    .map((c, i) => {
      const last = i === dpState.trail.length - 1;
      return `<span onclick="dpTrailTo(${i})" style="cursor:pointer;font-weight:${last ? 700 : 500};color:${last ? "var(--gd)" : "var(--gm)"}">${dpEscape(c.name || c.id)}</span>`;
    })
    .join(' <span style="color:var(--tl)">›</span> ');
  return `<div style="font-family:sans-serif;font-size:11px;background:var(--gp);border-radius:8px;padding:6px 12px;margin-bottom:8px;display:flex;align-items:center;gap:6px;flex-wrap:wrap">
    <span style="color:var(--tl)">🧭 Trail:</span> ${crumbs}
  </div>`;
}

/* Searchable subject picker (scales to thousands of cows). Local mode filters
   the cache; DB mode queries the server. */
let _dpSearchDebounce = null;
function dpSearch(q) {
  const res = document.getElementById("dp_searchRes");
  if (!res) return;
  q = (q || "").trim();
  if (!q) { res.style.display = "none"; res.innerHTML = ""; return; }
  const show = list => {
    if (!list.length) {
      res.innerHTML = '<div style="padding:8px 12px;color:var(--tl);font-size:12px;font-family:sans-serif">No matches</div>';
    } else {
      res.innerHTML = list
        .slice(0, 20)
        .map(
          a => `<div class="dp-sres" onclick="dpPick('${a.id}')" style="padding:7px 12px;cursor:pointer;font-family:sans-serif;font-size:12px;border-bottom:1px solid #f0f0f0">
            <b>${dpEscape(a.name || "—")}</b> <span style="color:var(--tl)">${a.regId ? "· " + dpEscape(a.regId) : ""} ${a.sex === "Male" ? "🐂" : "🐄"}</span>
          </div>`
        )
        .join("");
    }
    res.style.display = "block";
  };
  if (DB_MODE) {
    if (_dpSearchDebounce) clearTimeout(_dpSearchDebounce);
    _dpSearchDebounce = setTimeout(() => {
      window.LpDB.animalsPage({ q, page: 1, perPage: 20, sort: "name" })
        .then(r => { lpCacheMerge(r.rows || []); show(r.rows || []); })
        .catch(() => show([]));
    }, 220);
  } else {
    const ql = q.toLowerCase();
    show(
      animals.filter(
        a =>
          (a.name || "").toLowerCase().includes(ql) ||
          (a.regId || "").toLowerCase().includes(ql) ||
          (a.earTag || "").toLowerCase().includes(ql)
      )
    );
  }
}
function dpPick(id) {
  const res = document.getElementById("dp_searchRes");
  if (res) { res.style.display = "none"; res.innerHTML = ""; }
  const si = document.getElementById("dp_search");
  if (si) si.value = "";
  dpSetAnimal(id);
}
function dpPrint() {
  if (!dpState.animalId) {
    showToast("Pick an animal first");
    return;
  }
  const a = byId(dpState.animalId);
  if (!a) {
    showToast("Animal not found");
    return;
  }
  openDeepPedigreeCertificate(a, dpState.depth);
}

/**
 * Open the Deep Pedigree as an A3-landscape certificate in a popup window.
 * Mirrors openPedigreeCertificate(): same header / strip / signature blocks /
 * footer pulled from Pedigree Certificate Settings (Backup page).
 */
function openDeepPedigreeCertificate(a, depth) {
  const cs = getCertSettings();
  const gens = Math.max(1, Math.min(22, depth || 6));
  const today = new Date().toLocaleDateString("en-GB", {
    day: "2-digit",
    month: "long",
    year: "numeric",
  });
  const dob = a.dob
    ? new Date(a.dob + "T12:00").toLocaleDateString("en-GB", {
        day: "2-digit",
        month: "short",
        year: "numeric",
      })
    : "—";
  const isMale = a.sex === "Male";
  const breedDisp = a.breed === "Montbeliard" ? "Montbéliarde" : a.breed || "—";
  const certType = isMale ? "Dairy Bull Deep Pedigree" : "Dairy Cow Deep Pedigree";
  const certNo =
    "LPD-" +
    (a.regId || a.id || "")
      .replace(/[^A-Z0-9]/gi, "")
      .toUpperCase()
      .slice(0, 8) +
    "-" +
    new Date().getFullYear();

  // Logo (shared with the regular certificate)
  const logoUrl = typeof getLogoDataUrl === "function" ? getLogoDataUrl() : "";
  const certLogo = logoUrl
    ? `<img src="${logoUrl}" style="width:56px;height:56px;object-fit:contain;border-radius:50%;border:2px solid rgba(255,255,255,.4)">`
    : `<div style="width:56px;height:56px;background:rgba(255,255,255,.15);border-radius:50%;border:2px solid rgba(255,255,255,.4);display:flex;align-items:center;justify-content:center;font-size:26px">🌳</div>`;

  // Build the tree (depth gens, no collapse) and inline-render to HTML
  const tree = dpBuildTree(a, gens);
  const stats = dpCountTree(tree);
  const totalPossible = Math.pow(2, gens + 1) - 2;
  const coverage = totalPossible ? Math.round((stats.known * 100) / totalPossible) : 0;

  // Inline render WITHOUT collapse buttons (clean for print)
  function printCard(n) {
    if (!n) {
      return `<div class="dpn dpn-unknown">?</div>`;
    }
    const cls =
      n.kind === "self"
        ? "dpn-self"
        : n.kind === "sire" || n.sex === "Male"
          ? "dpn-sire"
          : "dpn-dam";
    const regLine = n.regId ? `<div class="dpn-reg">${dpEscape(n.regId)}</div>` : "";
    return `<div class="dpn ${cls}">
      <div class="dpn-name">${dpEscape(n.name || "—")}</div>
      ${regLine}
    </div>`;
  }
  function printSubtree(node, d) {
    if (!node) return `<div class="dp-branch">${printCard(null)}</div>`;
    const hasKids = d < gens && (node.sire || node.dam);
    const card = `<div class="dp-card-wrap">${printCard(node)}</div>`;
    if (!hasKids) return `<div class="dp-branch">${card}</div>`;
    return `<div class="dp-branch">
      ${card}
      <div class="dp-children">
        <div class="dp-child dp-child-sire">${printSubtree(node.sire, d + 1)}</div>
        <div class="dp-child dp-child-dam">${printSubtree(node.dam, d + 1)}</div>
      </div>
    </div>`;
  }

  // Signatures from cert settings (same logic as openPedigreeCertificate)
  const sigs = [];
  const numSigs = cs.numSigs || 3;
  if (numSigs >= 1 && cs.sig1Role) sigs.push({ role: cs.sig1Role, org: cs.sig1Org || "" });
  if (numSigs >= 2 && cs.sig2Role) sigs.push({ role: cs.sig2Role, org: cs.sig2Org || "" });
  if (numSigs >= 3 && cs.sig3Role) sigs.push({ role: cs.sig3Role, org: cs.sig3Org || "" });
  const sigCols = sigs.length === 1 ? "1fr" : sigs.length === 2 ? "1fr 1fr" : "1fr 1fr 1fr";
  const sigBlock = sigs.length
    ? `<div class="sh am" style="margin-top:8px">✍️ Certification &amp; Authorisation</div>
       <div class="sig-row" style="grid-template-columns:${sigCols}">
         ${sigs
           .map(
             s => `<div class="sig-box"><div class="sig-line"></div>
               <div class="sig-role">${dpEscape(s.role)}</div>
               ${s.org ? `<div class="sig-org">${dpEscape(s.org)}</div>` : ""}
             </div>`
           )
           .join("")}
       </div>`
    : "";

  // Page orientation: portrait for depths 1-3, landscape for 4-7, A3 for 8-11
  const pageRule =
    gens <= 3
      ? "@page { size:A4 portrait; margin:8mm 10mm }"
      : gens <= 7
        ? "@page { size:A4 landscape; margin:8mm 10mm }"
        : "@page { size:A3 landscape; margin:8mm 10mm }";

  const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>${dpEscape(certType)} — ${dpEscape(a.name || "Animal")}</title>
<style>
${pageRule}
*  { box-sizing:border-box; margin:0; padding:0; -webkit-print-color-adjust:exact; print-color-adjust:exact }
body { font-family:'Segoe UI',Arial,sans-serif; font-size:8.5pt; color:#111; background:#fff; margin:0 auto; padding:6px }

/* Header — identical to standard certificate */
.hdr  { background:#1a4a2e; color:#fff; padding:10px 14px; display:flex; align-items:center; gap:12px; border-radius:4px 4px 0 0 }
.hdr-txt h1 { font-size:10pt; font-weight:700; letter-spacing:.2px }
.hdr-txt h2 { font-size:13pt; font-weight:900; margin-top:1px }
.hdr-txt p  { font-size:7pt; opacity:.75; margin-top:2px }
.hdr-no { text-align:right; font-size:7pt; opacity:.75; margin-left:auto; flex-shrink:0 }

/* Animal strip */
.strip { background:#2d7a4f; color:#fff; padding:6px 14px; display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:6px }
.strip-name { font-size:14pt; font-weight:900 }
.strip-badges { display:flex; gap:6px; flex-wrap:wrap }
.badge { background:rgba(255,255,255,.22); border:1px solid rgba(255,255,255,.4); border-radius:3px; padding:2px 8px; font-size:7.5pt; font-weight:700 }

/* Section header */
.sh { background:#ecf5ec; border:1px solid #c8dcc8; border-top:none; padding:3px 10px; font-size:7.5pt; font-weight:700; color:#1a4a2e; display:flex; align-items:center; gap:5px }
.sh.am { background:#fffbeb; border-color:#fde68a; color:#92400e }

/* Stats strip */
.stats { display:grid; grid-template-columns:repeat(4,1fr); gap:0; border:1px solid #c8dcc8; border-top:none; background:#fafffe }
.stat { padding:5px 10px; border-right:1px solid #d4e4d4; text-align:center }
.stat:last-child { border-right:none }
.stat .lb { font-size:6pt; text-transform:uppercase; letter-spacing:.4px; color:#5a7a5a; font-weight:600 }
.stat .vl { font-size:11pt; font-weight:800; color:#1a4a2e; margin-top:1px }

/* Deep tree */
.tree-wrap { border:1px solid #c8dcc8; border-top:none; padding:10px; background:#fff; overflow:auto }
.dp-tree { display:inline-block; font-family:'Segoe UI',Arial,sans-serif; min-width:100% }
.dp-branch { display:flex; align-items:center; gap:18px; position:relative }
.dp-card-wrap { position:relative; display:flex; align-items:center }
.dp-children { display:flex; flex-direction:column; gap:4px; position:relative }
.dp-children::before { content:""; position:absolute; left:-9px; top:14px; bottom:14px; width:1.5px; background:#9caa9c }
.dp-child { position:relative }
.dp-child::before { content:""; position:absolute; left:-9px; top:50%; width:9px; height:1.5px; background:#9caa9c }
.dpn { min-width:110px; max-width:160px; padding:4px 8px; border-radius:4px; border:1.2px solid; background:#fff; font-size:7.5pt; line-height:1.25 }
.dpn-self    { border-color:#1a4a2e; background:#eaf6ea }
.dpn-sire    { border-color:#3a6bc4; background:#eef3ff }
.dpn-dam     { border-color:#8e44ad; background:#f5eeff }
.dpn-unknown { border-color:#d1d5db; border-style:dashed; background:#fafafa; color:#9ca3af; text-align:center; font-weight:bold; min-width:24px; padding:4px 6px }
.dpn-name { font-weight:700; color:#1a2e1a; white-space:nowrap; overflow:hidden; text-overflow:ellipsis }
.dpn-reg { font-size:6.5pt; color:#5a7a5a; margin-top:1px; font-family:monospace }

/* Auto-shrink boxes for very deep trees */
${
  gens >= 8
    ? `.dpn { min-width:80px; max-width:120px; padding:3px 6px; font-size:6.5pt }
       .dp-branch { gap:12px }`
    : gens >= 6
      ? `.dpn { min-width:95px; max-width:140px; padding:3px 7px; font-size:7pt }
         .dp-branch { gap:14px }`
      : ""
}

/* Signatures — identical to standard certificate */
.sig-row { display:grid; gap:14px; padding:12px 14px; border:1px solid #c8dcc8; border-top:none }
.sig-box { text-align:center }
.sig-line { border-top:1.5px solid #1a4a2e; margin-top:22px; margin-bottom:3px }
.sig-role { font-size:7.5pt; color:#1a4a2e; font-weight:700 }
.sig-org  { font-size:6.5pt; color:#888 }

/* Footer — identical to standard certificate */
.ftr { background:#ecf5ec; border:1px solid #c8dcc8; border-top:none; padding:4px 12px; display:flex; justify-content:space-between; border-radius:0 0 4px 4px }
.ftr p { font-size:6.5pt; color:#5a7a5a }

/* Toolbar (screen only) */
.no-print { position:fixed; top:12px; right:12px; z-index:999; display:flex; gap:7px }
.btn-p { padding:7px 14px; background:#1a4a2e; color:#fff; border:none; border-radius:6px; font-size:10.5pt; font-weight:700; cursor:pointer }
.btn-c { padding:7px 10px; background:#f3f4f6; color:#374151; border:1px solid #d1d5db; border-radius:6px; font-size:10.5pt; cursor:pointer }
@media print { .no-print { display:none !important } }
</style>
</head>
<body>

<div class="no-print">
  <button class="btn-p" onclick="window.print()">🖨 Print / Save PDF</button>
  <button class="btn-c" onclick="window.close()">✕</button>
</div>

<!-- HEADER (from Pedigree Certificate Settings) -->
<div class="hdr">
  ${certLogo}
  <div class="hdr-txt" style="flex:1">
    <h1>${dpEscape(cs.companyName)}</h1>
    <h2>Larspeenghar ${dpEscape(certType)}</h2>
    <p>${dpEscape(cs.certSubtitle)}</p>
  </div>
  <div class="hdr-no"><div style="font-weight:700">No: ${certNo}</div><div style="margin-top:2px">${today}</div></div>
</div>

<!-- ANIMAL STRIP -->
<div class="strip">
  <div>
    <div style="font-size:7pt;opacity:.8">DEEP PEDIGREE — ${isMale ? "DAIRY BULL" : "DAIRY COW/HEIFER"}</div>
    <div class="strip-name">${dpEscape(a.name || "(Unnamed)")}</div>
  </div>
  <div class="strip-badges">
    <span class="badge">${dpEscape(breedDisp)}</span>
    <span class="badge">${isMale ? "🐂 Bull" : "🐄 Cow"}</span>
    ${a.regId ? `<span class="badge">ID: ${dpEscape(a.regId)}</span>` : ""}
    <span class="badge">DOB: ${dob}</span>
    ${a.earTag ? `<span class="badge">🏷 ${dpEscape(a.earTag)}</span>` : ""}
  </div>
</div>

<!-- STATS -->
<div class="stats">
  <div class="stat"><div class="lb">Generations</div><div class="vl">${gens}</div></div>
  <div class="stat"><div class="lb">Known Ancestors</div><div class="vl">${stats.known}</div></div>
  <div class="stat"><div class="lb">Possible at Depth ${gens}</div><div class="vl">${totalPossible}</div></div>
  <div class="stat"><div class="lb">Completeness</div><div class="vl">${coverage}%</div></div>
</div>

<!-- DEEP PEDIGREE TREE -->
<div class="sh">🌳 ${gens}-Generation Pedigree Tree · Paternal line (blue) on top, Maternal line (purple) below each ancestor</div>
<div class="tree-wrap">
  <div class="dp-tree">${printSubtree(tree, 0)}</div>
</div>

<!-- INBREEDING ANALYSIS -->
${dpInbreedingPanel(a, gens, true)}

${sigBlock}

<!-- FOOTER (from Pedigree Certificate Settings) -->
<div class="ftr">
  <p>${dpEscape(cs.footerText)} · ${dpEscape(certType)} · ${certNo}</p>
  <p>Printed: ${today}</p>
</div>

</body>
</html>`;

  const winW = gens >= 8 ? 1400 : gens >= 5 ? 1200 : 1000;
  const win = window.open("", "_blank", "width=" + winW + ",height=820");
  if (!win) {
    alert("Please allow popups to open the deep pedigree certificate.");
    return;
  }
  win.document.write(html);
  win.document.close();
  setTimeout(() => win.focus(), 200);
}

/* Build the inbreeding analysis panel HTML for a subject animal.
   `forPrint` switches to the certificate's flat colour scheme. */
function dpInbreedingPanel(animal, depth, forPrint) {
  const res = subjectF(animal, depth);
  if (!res) {
    return forPrint
      ? ""
      : `<div class="dp-inbreed dp-inbreed-none">
           <b>🧬 Inbreeding</b> — not enough recorded ancestry to compute a coefficient.
           Link this animal's sire and dam to registered records to enable inbreeding analysis.
         </div>`;
  }
  const F = res.F;
  const c = fColor(F);
  const dep = inbreedingDepression(F);

  // Common ancestors contributing to inbreeding (appear on both sire & dam side)
  const commonRows = (res.common || [])
    .slice(0, 8)
    .map(
       co => `<tr>
        <td style="text-align:left">${dpEscape(co.label)}</td>
        <td style="text-align:right;font-weight:700">${co.contrib.toFixed(2)}%</td>
      </tr>`
    )
    .join("");

  const depRows = dep
    .map(d => {
      const sign = d.total > 0 ? "+" : "";
      const bad = d.dir === "down" ? d.total < 0 : d.total > 0;
      const col = forPrint ? (bad ? "#b91c1c" : "#15803d") : bad ? "var(--bd,#b91c1c)" : "#15803d";
      return `<tr>
        <td style="text-align:left">${d.label}</td>
        <td style="text-align:right;font-weight:700;color:${col}">${sign}${d.total} ${d.unit}</td>
      </tr>`;
    })
    .join("");

  const F0note =
    F === 0
      ? `<div style="font-size:11px;color:${forPrint ? "#15803d" : "var(--tl)"};margin-top:4px">
           No common ancestors found on both sides within ${depth} generations — outbred (F = 0%).
         </div>`
      : "";

  const wrapStyle = forPrint
    ? "border:1px solid #c8dcc8;border-top:none;padding:0"
    : "";

  return `
  <div class="dp-inbreed${forPrint ? " dp-inbreed-print" : ""}" style="${wrapStyle}">
    <div class="dp-inbreed-head" style="${forPrint ? "background:#ecf5ec;color:#1a4a2e;padding:4px 10px;font-weight:700;font-size:11px" : ""}">
      🧬 Inbreeding Analysis (over ${depth} generations)
    </div>
    <div class="dp-inbreed-body" style="${forPrint ? "display:flex;gap:14px;padding:10px;flex-wrap:wrap" : ""}">
      <div class="dp-inbreed-f" style="${forPrint ? "min-width:150px" : ""}">
        <div class="dp-fval" style="background:${c.bg};color:${c.color};border-radius:10px;padding:10px 14px;text-align:center">
          <div style="font-size:24px;font-weight:800;line-height:1">${F.toFixed(2)}%</div>
          <div style="font-size:11px;font-weight:700;margin-top:2px">Coefficient of Inbreeding (F)</div>
          <div style="font-size:11px;margin-top:2px">${c.label} risk</div>
        </div>
        ${F0note}
        ${
          commonRows
            ? `<table class="dp-itbl" style="width:100%;border-collapse:collapse;font-size:11px;margin-top:8px">
                 <thead><tr><th style="text-align:left;border-bottom:1px solid #ccc;padding:3px 0">Common ancestor</th><th style="text-align:right;border-bottom:1px solid #ccc;padding:3px 0">Contribution</th></tr></thead>
                 <tbody>${commonRows}</tbody>
               </table>`
            : ""
        }
      </div>
      <div class="dp-inbreed-dep" style="${forPrint ? "flex:1;min-width:220px" : ""}">
        <div style="font-size:11px;font-weight:700;margin-bottom:4px">Estimated inbreeding depression at F = ${F.toFixed(2)}%</div>
        <table class="dp-itbl" style="width:100%;border-collapse:collapse;font-size:11px">
          <tbody>${depRows}</tbody>
        </table>
        <div style="font-size:9px;color:${forPrint ? "#5a7a5a" : "var(--tl)"};margin-top:5px;line-height:1.4">
          Population-average estimates for Holstein dairy cattle (Smith, Cassell &amp; Pearson, 1998, J. Dairy Sci. 81:2729).
          Individual results vary with genetic merit and management.
        </div>
      </div>
    </div>
    ${dpDiversityStrip(animal, depth, forPrint)}
  </div>`;
}

/* Pedigree-diversity metrics strip (AVK, generation equivalents, ancestral
   inbreeding, completeness). Shown under the F panel and on the certificate. */
function dpDiversityStrip(animal, depth, forPrint) {
  const div = pedigreeDiversity(animal, depth);
  const fa = ancestralInbreeding(animal, depth);
  const cells = [
    {
      v: div.avk.toFixed(1) + "%",
      l: "Ancestor diversity (AVK)",
      hint: "Distinct ancestors ÷ traced positions. Higher = more genetically diverse.",
    },
    {
      v: div.genEquiv.toFixed(2),
      l: "Complete gen. equivalents",
      hint: "Depth-weighted measure of how complete the pedigree is.",
    },
    {
      v: div.completeness.toFixed(0) + "%",
      l: "Pedigree completeness",
      hint: `${div.tracedSlots} of ${div.possible} possible ancestor positions known.`,
    },
    {
      v: fa !== null ? fa.toFixed(2) + "%" : "—",
      l: "Ancestral inbreeding (Fa, est.)",
      hint: "Estimated share of the genome exposed to inbreeding in earlier generations.",
    },
    {
      v: div.distinctAnc + "",
      l: "Distinct ancestors",
      hint: "Unique individuals found across the pedigree.",
    },
  ];
  const border = forPrint ? "1px solid #c8dcc8" : "1px solid var(--bdr)";
  const headBg = forPrint ? "#ecf5ec" : "var(--gp)";
  const headCol = forPrint ? "#1a4a2e" : "var(--gd)";
  const repeated =
    div.repeated && div.repeated.length
      ? `<div style="padding:6px 12px;font-size:10px;color:${forPrint ? "#5a7a5a" : "var(--tl)"};border-top:${border}">
           <b>Repeated ancestors</b> (appear on multiple pedigree paths — the inbreeding drivers):
           ${div.repeated.slice(0, 6).map(r => `${dpEscape(r.label)} ×${r.count}`).join(" · ")}
         </div>`
      : "";
  return `
  <div style="border-top:${border}">
    <div style="background:${headBg};color:${headCol};padding:5px 12px;font-family:sans-serif;font-size:11px;font-weight:700">
      📐 Pedigree Diversity (over ${div.depth} generations)
    </div>
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));font-family:sans-serif">
      ${cells
        .map(
          c => `<div style="padding:8px 12px;border-right:${border};text-align:center" title="${c.hint}">
            <div style="font-size:17px;font-weight:800;color:${forPrint ? "#1a4a2e" : "var(--gd)"};line-height:1.1">${c.v}</div>
            <div style="font-size:9px;color:${forPrint ? "#5a7a5a" : "var(--tl)"};margin-top:2px">${c.l}</div>
          </div>`
        )
        .join("")}
    </div>
    ${repeated}
  </div>`;
}

/* DB mode: fetch the animal's ancestor subgraph (up to depth gens) into the
   cache from the server, THEN render. The existing byId-based tree builder,
   inbreeding and diversity panels work unchanged once ancestors are cached. */
function dpEnsureAndRender() {
  if (DB_MODE) {
    if (!dpState.animalId && curId) dpState.animalId = curId;
    if (!dpState.animalId) { renderDeepPed(); ensureSubtabBar("deepped"); return; }
    window.LpDB
      .pedigree(dpState.animalId, dpState.depth)
      .then(rows => {
        lpCacheMerge(rows);
        renderDeepPed();
        ensureSubtabBar("deepped");
        // Persist the computed F + diversity so the herd report can sort at scale.
        try {
          const a = byId(dpState.animalId);
          if (a && typeof subjectF === "function") {
            const r = subjectF(a, dpState.depth);
            const d = typeof pedigreeDiversity === "function" ? pedigreeDiversity(a, dpState.depth) : null;
            if (r) window.LpDB.setF(dpState.animalId, r.F, d ? d.avk : null).catch(() => {});
          }
        } catch (e) {}
      })
      .catch(() => { renderDeepPed(); ensureSubtabBar("deepped"); });
    return;
  }
  renderDeepPed();
  ensureSubtabBar("deepped");
}

/* ── Generation-by-generation technical analysis ──
   For each generation 1..depth: how many ancestor positions are known vs
   possible (2^g), the % completeness, and the inbreeding (F) contributed by
   common ancestors whose loops close at that generation. This is the
   "inbreeding tracking between each generation" view. */
function generationAnalysis(a, maxDepth) {
  const depth = Math.max(1, Math.min(22, maxDepth || IB_DEPTH));
  const known = new Array(depth + 2).fill(0);
  const norm = n => (n ? n.trim().toUpperCase().replace(/\s+/g, " ") : "");
  function walk(node, gen) {
    if (!node || gen > depth) return;
    const sLinked = node.sireLinkedId ? byId(node.sireLinkedId) : null;
    if (node.sireLinkedId || node.sireName) {
      known[gen]++;
      if (sLinked && gen < depth) walk(sLinked, gen + 1);
      else if (!sLinked && gen + 1 <= depth) {
        if (node.ssName) known[gen + 1]++;
        if (node.sdName) known[gen + 1]++;
      }
    }
    const dLinked = node.damLinkedId ? byId(node.damLinkedId) : null;
    if (node.damLinkedId || node.damName) {
      known[gen]++;
      if (dLinked && gen < depth) walk(dLinked, gen + 1);
      else if (!dLinked && gen + 1 <= depth) {
        if (node.dsName) known[gen + 1]++;
        if (node.ddName) known[gen + 1]++;
      }
    }
  }
  walk(a, 1);

  // Inbreeding contribution bucketed by the generation a common ancestor sits.
  const fByGen = new Array(depth + 2).fill(0);
  const res = typeof subjectF === "function" ? subjectF(a, depth) : null;
  if (res && res.common) {
    res.common.forEach(co => {
      (co.sireGens || []).forEach(L1 =>
        (co.damGens || []).forEach(L2 => {
          const contrib = Math.pow(0.5, L1 + L2 + 1) * 100;
          const g = Math.min(L1, L2) + 1;
          if (g >= 1 && g <= depth) fByGen[g] += contrib;
        })
      );
    });
  }
  const rows = [];
  let cum = 0;
  for (let g = 1; g <= depth; g++) {
    const possible = Math.pow(2, g);
    cum += fByGen[g];
    rows.push({
      gen: g,
      known: known[g],
      possible,
      completeness: possible ? Math.round((known[g] * 1000) / possible) / 10 : 0,
      fContrib: Math.round(fByGen[g] * 1000) / 1000,
      cumF: Math.round(cum * 100) / 100,
    });
  }
  return rows;
}

/* Render the generation-by-generation analysis table. */
function dpGenAnalysisPanel(animal, depth) {
  const rows = generationAnalysis(animal, depth);
  const maxF = Math.max(0.0001, ...rows.map(r => r.fContrib));
  const body = rows
    .map(r => {
      const cBar = Math.min(100, r.completeness);
      const fBar = Math.round((r.fContrib / maxF) * 100);
      const fCol = r.fContrib > 0 ? fColor(r.cumF).color : "var(--tl)";
      return `<tr style="border-top:1px solid var(--bdr)">
        <td style="padding:5px 9px;font-weight:700;text-align:center">${r.gen}</td>
        <td style="padding:5px 9px;text-align:right">${r.known.toLocaleString()}</td>
        <td style="padding:5px 9px;text-align:right;color:var(--tl)">${r.possible.toLocaleString()}</td>
        <td style="padding:5px 9px;min-width:90px">
          <div style="background:#eef2ee;border-radius:4px;height:8px;overflow:hidden"><div style="width:${cBar}%;height:100%;background:var(--gm)"></div></div>
          <div style="font-size:9px;color:var(--tl);margin-top:1px">${r.completeness}%</div>
        </td>
        <td style="padding:5px 9px;min-width:90px">
          <div style="background:#fdecec;border-radius:4px;height:8px;overflow:hidden"><div style="width:${fBar}%;height:100%;background:#c0392b"></div></div>
          <div style="font-size:9px;color:#c0392b;margin-top:1px">${r.fContrib ? "+" + r.fContrib.toFixed(3) + "%" : "—"}</div>
        </td>
        <td style="padding:5px 9px;text-align:right;font-weight:700;color:${fCol}">${r.cumF.toFixed(2)}%</td>
      </tr>`;
    })
    .join("");
  return `
  <div class="card" style="margin-top:10px">
    <div class="ch"><h2>📈 Generation-by-Generation Analysis</h2>
      <span style="font-family:sans-serif;font-size:10px;color:var(--tl)">completeness &amp; inbreeding contributed at each generation</span>
    </div>
    <div class="cb" style="overflow-x:auto;padding:0">
      <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:11px">
        <thead><tr style="background:var(--gp)">
          <th style="padding:6px 9px">Gen</th>
          <th style="padding:6px 9px;text-align:right">Known</th>
          <th style="padding:6px 9px;text-align:right">Possible</th>
          <th style="padding:6px 9px;text-align:left">Completeness</th>
          <th style="padding:6px 9px;text-align:left">F contributed</th>
          <th style="padding:6px 9px;text-align:right">Cumulative F</th>
        </tr></thead>
        <tbody>${body}</tbody>
      </table>
    </div>
  </div>`;
}

function renderDeepPed() {
  const el = document.getElementById("pg-deepped");
  if (!el) return;

  if (!dpState.animalId && DB_MODE && curId) dpState.animalId = curId;
  if (!dpState.animalId && animals.length) dpState.animalId = animals[0].id;
  const animal = byId(dpState.animalId);

  const animalOpts = animals
    .map(a => {
      const reg = a.regId ? " (" + dpEscape(a.regId) + ")" : "";
      const sel = a.id === dpState.animalId ? " selected" : "";
      return `<option value="${a.id}"${sel}>${dpEscape(a.name)}${reg}</option>`;
    })
    .join("");

  const depthOpts = Array.from({ length: 22 }, (_, i) => i + 1)
    .map(n => {
      const sel = n === dpState.depth ? " selected" : "";
      const cap = Math.pow(2, n);
      return `<option value="${n}"${sel}>${n} generation${n > 1 ? "s" : ""} · up to ${cap} ancestors</option>`;
    })
    .join("");

  let body;
  if (!animal) {
    body = `<div class="empty">No animal selected. Add an animal on the Home page first.</div>`;
  } else {
    const tree = dpBuildTree(animal, dpState.depth);
    const stats = dpCountTree(tree);
    const totalPossible = Math.pow(2, dpState.depth + 1) - 2;
    const coverage = totalPossible ? Math.round((stats.known * 100) / totalPossible) : 0;

    body = `
      <div class="dp-stats">
        <div class="sc"><div class="sv">${dpState.depth}</div><div class="sl">Generations</div></div>
        <div class="sc"><div class="sv">${stats.known}</div><div class="sl">Known ancestors</div></div>
        <div class="sc"><div class="sv">${totalPossible}</div><div class="sl">Possible at depth ${dpState.depth}</div></div>
        <div class="sc"><div class="sv">${coverage}%</div><div class="sl">Pedigree completeness</div></div>
      </div>
      ${dpInbreedingPanel(animal, dpState.depth, false)}
      <div class="dp-legend">
        <span><span class="dp-sw dp-sw-self"></span> Subject animal</span>
        <span><span class="dp-sw dp-sw-sire"></span> Paternal line (♂)</span>
        <span><span class="dp-sw dp-sw-dam"></span> Maternal line (♀)</span>
        <span><span class="dp-sw dp-sw-unknown"></span> Unknown</span>
        <span>🌳 click any ancestor to open <b>its</b> family tree</span>
      </div>
      <div class="dp-scroll">
        <div class="dp-tree">
          ${dpRenderSubtree(tree, 0, dpState.depth)}
        </div>
      </div>
      ${dpGenAnalysisPanel(animal, dpState.depth)}`;
  }

  el.innerHTML = `
    <div class="card dp-card">
      <div class="ch">
        <h2>🌳 Deep Pedigree Tree</h2>
        <div style="display:flex;gap:6px;flex-wrap:wrap;align-items:center">
          <button class="btn bd bsm" onclick="dpExpandAll()">Expand all</button>
          <button class="btn bd bsm" onclick="dpCollapseAll()">Collapse to 1 gen</button>
          <button class="btn bg bsm" onclick="dpPrint()">🖨 Print</button>
        </div>
      </div>
      <div class="cb">
        <div class="dp-controls">
          <label class="dp-ctrl" style="position:relative;flex:1;min-width:200px">
            <span>Find animal</span>
            <input id="dp_search" type="text" autocomplete="off" placeholder="🔍 Search name, ID or ear tag…"
              oninput="dpSearch(this.value)" onfocus="dpSearch(this.value)"
              style="width:100%;padding:7px 10px;border:1px solid var(--bdr);border-radius:8px;font-family:sans-serif;font-size:13px">
            <div id="dp_searchRes" style="display:none;position:absolute;top:100%;left:0;right:0;z-index:30;background:#fff;border:1px solid var(--bdr);border-radius:8px;box-shadow:var(--shl);max-height:280px;overflow:auto;margin-top:3px"></div>
          </label>
          <label class="dp-ctrl">
            <span>Depth</span>
            <select onchange="dpSetDepth(this.value)">${depthOpts}</select>
          </label>
        </div>
        ${dpTrailHTML()}
        ${animal
          ? `<div class="dp-subject" style="margin-bottom:8px">
              <b style="font-size:15px">${dpEscape(animal.name)}</b>
              <span style="color:var(--tl);font-size:11px">
                ${animal.regId ? "· " + dpEscape(animal.regId) : ""}
                ${animal.breed ? "· " + dpEscape(animal.breed) : ""}
                ${animal.dob ? "· DOB " + fmtD(animal.dob) : ""}
                ${animal.sex ? "· " + (animal.sex === "Male" ? "🐂 Bull" : "🐄 Cow") : ""}
              </span>
            </div>`
          : ""}
        ${body}
      </div>
    </div>`;
}

/* ════════════════════════════════════════════════════════════════
   LANDING PAGE & TRANSLATIONS — Admin editor
   Added 2026-06-18. Lets the admin customise the public landing page
   text and all three languages (English / Pashto / Dari).
   Overrides are stored in localStorage key "lp_landing_i18n".
   ════════════════════════════════════════════════════════════════ */

function lpSaveI18nOverrides(overrides) {
  try {
    localStorage.setItem("lp_landing_i18n", JSON.stringify(overrides));
  } catch (e) {
    console.warn("save i18n failed:", e);
  }
  if (window.LpSync && window.LpSync.hasBackend && typeof fetch === "function") {
    fetch("api.php?action=save&store=settings_i18n", {
      method: "POST",
      credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: overrides }),
    }).catch(() => {});
  }
}

function lpCurrentI18n() {
  return typeof window.lpGetI18n === "function" ? window.lpGetI18n() : { en: {}, ps: {}, fa: {} };
}
function lpOverrides() {
  try {
    return JSON.parse(localStorage.getItem("lp_landing_i18n") || "{}") || {};
  } catch (e) {
    return {};
  }
}

let lpAdminLang = "en";

function renderLandingAdmin() {
  const el = document.getElementById("pg-landing");
  if (!el) return;
  if (typeof window.LP_I18N_DEFAULTS === "undefined") {
    el.innerHTML =
      '<div class="card"><div class="cb"><div class="empty">translations.js failed to load — cannot edit landing text.</div></div></div>';
    return;
  }

  el.innerHTML = `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#0f3a23,#2f7d52);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">🌐 Landing Page</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">The public page visitors see before logging in</span>
    </div>
    <div class="cb">
      <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:12px">
        <button class="btn bg" onclick="lpShowLanding()">🔗 Open Landing Page</button>
        <button class="btn bd" onclick="lpResetI18n()">↺ Reset all text to defaults</button>
      </div>
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);background:#f0fdf4;border:1px solid #a7f3d0;border-radius:var(--r);padding:10px 13px;line-height:1.6">
        The landing page is now built into the app — it is the first thing visitors see at
        <b>yourdomain.com</b>. Its "Login / Enter System" buttons reveal the sign-in screen.
        The header <b>logo</b> is the company logo you upload under <b>💾 Backup → Company Logo</b>.
        Visitors can switch between <b>English</b>, <b>پښتو (Pashto)</b> and <b>دری (Dari)</b>
        — their choice is remembered. Click "Open Landing Page" above to preview it now.
      </div>
    </div>
  </div>

  <!-- ══ LANDING PHOTOS (auto-rotating showcase) ══ -->
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#7c2d12,#b45309);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">🖼 Landing Photos</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">An auto-rotating showcase on the landing page</span>
    </div>
    <div class="cb">
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);margin-bottom:10px;line-height:1.6">
        Add photos of your farm, animals or facilities. They rotate automatically on the public
        landing page (every few seconds). Add as many as you like — they're stored with your data
        and included in backups.
      </div>
      <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:12px">
        <label class="btn bg" style="cursor:pointer">📁 Add Photos
          <input type="file" accept="image/*" multiple style="display:none" onchange="lpAddLandingPhotos(this)">
        </label>
        <button class="btn bd" onclick="lpClearLandingPhotos()">🗑 Remove All</button>
        <span id="lp_photos_msg" style="font-family:sans-serif;font-size:11px;color:var(--tl)"></span>
      </div>
      <div id="lp_photos_grid"></div>
    </div>
  </div>

  <div class="card">
    <div class="cb" style="font-family:sans-serif;font-size:12px;color:var(--tm)">
      ✍️ To translate the app's text into English / Pashto / Dari, open the
      <b>🗣 Translations</b> tab above.
    </div>
  </div>`;

  lpRenderLandingPhotos();
}

/* ── Translations editor — its own Settings tab (Settings → Translations) ── */
function renderTranslations() {
  const el = document.getElementById("pg-translations");
  if (!el) return;
  if (typeof window.LP_I18N_DEFAULTS === "undefined") {
    el.innerHTML = '<div class="card"><div class="cb"><div class="empty">translations.js failed to load.</div></div></div>';
    return;
  }
  const langs = window.LP_LANGS || [
    { code: "en", native: "English" }, { code: "ps", native: "پښتو" }, { code: "fa", native: "دری" },
  ];
  const eff = lpCurrentI18n();
  const labels = window.LP_I18N_LABELS || {};
  const groups = [
    { name: "🏷 Brand & Navigation", keys: ["brand", "tagline", "nav_features", "nav_how", "nav_login"] },
    { name: "🎯 Hero", keys: ["hero_badge", "hero_title", "hero_sub", "hero_cta", "hero_cta2"] },
    { name: "✨ Features", keys: ["features_title", "features_sub", "feat1_title", "feat1_desc", "feat2_title", "feat2_desc", "feat3_title", "feat3_desc", "feat4_title", "feat4_desc", "feat5_title", "feat5_desc", "feat6_title", "feat6_desc", "feat7_title", "feat7_desc", "feat8_title", "feat8_desc"] },
    { name: "🪜 How it works", keys: ["how_title", "step1_title", "step1_desc", "step2_title", "step2_desc", "step3_title", "step3_desc"] },
    { name: "📊 Stats", keys: ["stat1_value", "stat1_label", "stat2_value", "stat2_label", "stat3_value", "stat3_label", "stat4_value", "stat4_label"] },
    { name: "📣 Call to action & Footer", keys: ["cta_title", "cta_sub", "cta_btn", "learn_title", "learn_sub", "learn_btn", "footer_tag", "footer_copy"] },
    { name: "👤 Portal — Navigation & Dashboard", keys: ["up_portal_name", "up_role_owner", "up_role_user", "up_signout", "up_nav", "up_help", "up_lang", "up_dashboard", "up_newrequest", "up_inbox", "up_sent", "up_account", "up_dash_welcome", "up_dash_subtitle", "up_dash_quick", "up_stat_unread", "up_stat_pending", "up_stat_replied", "up_stat_completed", "up_new", "up_recent", "up_viewall", "up_how_title", "up_step1_t", "up_step1_d", "up_step2_t", "up_step2_d", "up_step3_t", "up_step3_d", "up_step4_t", "up_step4_d"] },
    { name: "📝 Portal — Request Form", keys: ["up_req_title", "up_req_sub", "up_req_submit", "up_clearform", "up_optional", "up_sec_sire", "up_f_sirename", "up_f_airecord", "up_f_sirebreed", "up_f_aidate", "up_f_aitech", "up_sec_cow", "up_f_sex", "up_f_animalname", "up_f_dob", "up_f_eartag", "up_f_regno", "up_f_cowbreed", "up_f_photo", "up_sec_dam", "up_dam_optional", "up_dam_hint", "up_f_damname", "up_f_damdob", "up_f_damreg", "up_f_dameartag", "up_f_dambreed", "up_f_dammilk", "up_sec_owner", "up_f_ownername", "up_f_phone", "up_f_email", "up_f_province", "up_f_district", "up_f_address", "up_f_notes", "up_sel_breed", "up_sel_sex"] },
    { name: "📥 Portal — Inbox, Sent & Account", keys: ["up_inbox_title", "up_inbox_empty", "up_inbox_hint", "up_sent_title", "up_sent_empty", "up_sent_first", "up_st_pending", "up_st_replied", "up_st_completed", "up_st_rejected", "up_acc_title", "up_logged_as", "up_username_lbl", "up_curpass", "up_newpass", "up_confpass", "up_updatepass"] },
  ];
  const rtl = lpAdminLang === "ps" || lpAdminLang === "fa";
  const langTabs = langs
    .map(l => `<button class="btn bsm ${l.code === lpAdminLang ? "bg" : "bd"}" onclick="lpAdminSetLang('${l.code}')">${l.native}</button>`)
    .join("");
  const fieldRows = groups
    .map(grp => {
      const rows = grp.keys
        .map(k => {
          const label = labels[k] || k;
          const val = eff[lpAdminLang] && eff[lpAdminLang][k] != null ? eff[lpAdminLang][k] : "";
          const isLong = /_sub$|tagline|footer_copy/.test(k) || /feat\d_desc|step\d_desc|hero_sub|features_sub/.test(k);
          const input = isLong
            ? `<textarea id="i18n_${k}" rows="2" dir="${rtl ? "rtl" : "ltr"}" style="width:100%;font-family:sans-serif;font-size:12px;padding:7px 9px;border:1px solid var(--bdr);border-radius:6px">${dpEscape(val)}</textarea>`
            : `<input id="i18n_${k}" dir="${rtl ? "rtl" : "ltr"}" value="${dpEscape(val)}" style="width:100%;font-family:sans-serif;font-size:12px;padding:7px 9px;border:1px solid var(--bdr);border-radius:6px">`;
          return `<div style="display:grid;grid-template-columns:170px 1fr;gap:10px;align-items:start;padding:6px 0;border-bottom:1px solid #f1f1f1">
            <label style="font-family:sans-serif;font-size:11px;color:var(--tm);font-weight:600;padding-top:6px">${label}<br><span style="color:var(--tl);font-weight:400;font-size:9px">${k}</span></label>${input}</div>`;
        })
        .join("");
      return `<details open style="margin-bottom:10px;border:1px solid var(--bdr);border-radius:var(--r);overflow:hidden">
        <summary style="cursor:pointer;padding:9px 13px;background:var(--gp);font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd)">${grp.name}</summary>
        <div style="padding:6px 13px 12px">${rows}</div></details>`;
    })
    .join("");
  el.innerHTML = `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1e3a5f,#3a6bc4);-webkit-print-color-adjust:exact;print-color-adjust:exact">
      <h2 style="color:#fff">🗣 Translations</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">Translate the landing page &amp; owner portal into all three languages</span>
    </div>
    <div class="cb">
      <div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:14px">
        <span style="font-family:sans-serif;font-size:12px;color:var(--tm);font-weight:600">Editing language:</span>
        ${langTabs}
        <span id="i18n_msg" style="font-family:sans-serif;font-size:11px;margin-inline-start:8px"></span>
      </div>
      ${fieldRows}
      <div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:14px;position:sticky;bottom:0;background:#fff;padding:10px 0;border-top:1px solid var(--bdr)">
        <button class="btn bg" onclick="lpSaveI18nForm()">💾 Save ${langs.find(l => l.code === lpAdminLang)?.native || ""} translations</button>
        <button class="btn bd" onclick="renderTranslations()">Cancel changes</button>
        <button class="btn bd" onclick="lpResetI18n()">↺ Reset all to defaults</button>
      </div>
    </div>
  </div>`;
}

/* ── Landing photo management (admin) ── */
function lpRenderLandingPhotos() {
  const grid = document.getElementById("lp_photos_grid");
  if (!grid) return;
  const photos = lpGetLandingPhotos();
  if (!photos.length) {
    grid.innerHTML = '<div style="font-family:sans-serif;font-size:12px;color:var(--tl)">No photos yet. Click “Add Photos”.</div>';
    return;
  }
  grid.innerHTML =
    '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(120px,1fr));gap:10px">' +
    photos
      .map(
        (src, i) => `<div style="position:relative;border-radius:8px;overflow:hidden;border:1px solid var(--bdr);aspect-ratio:4/3;background:#f3f3f3">
          <img src="${src}" style="width:100%;height:100%;object-fit:cover">
          <button class="ib" title="Remove" onclick="lpRemoveLandingPhoto(${i})"
            style="position:absolute;top:4px;right:4px;background:rgba(0,0,0,.55);color:#fff;width:24px;height:24px;border-radius:6px">🗑</button>
          <span style="position:absolute;bottom:4px;left:6px;background:rgba(0,0,0,.5);color:#fff;font-size:10px;padding:1px 6px;border-radius:6px;font-family:sans-serif">${i + 1}</span>
        </div>`
      )
      .join("") +
    "</div>";
}

/* Read, downscale and append uploaded images to the landing showcase. */
function lpAddLandingPhotos(input) {
  const files = input && input.files ? Array.from(input.files) : [];
  if (!files.length) return;
  const msg = document.getElementById("lp_photos_msg");
  let done = 0;
  const photos = lpGetLandingPhotos();
  const imgs = files.filter(f => /^image\//.test(f.type));
  if (!imgs.length) { if (msg) msg.textContent = "No image files selected."; return; }
  if (msg) msg.textContent = "Processing…";

  imgs.forEach(file => {
    const reader = new FileReader();
    reader.onload = e => {
      const img = new Image();
      img.onload = () => {
        // Downscale to max 1280px wide to keep storage reasonable.
        const MAX = 1280;
        let w = img.width, h = img.height;
        if (w > MAX) { h = Math.round((h * MAX) / w); w = MAX; }
        const c = document.createElement("canvas");
        c.width = w; c.height = h;
        c.getContext("2d").drawImage(img, 0, 0, w, h);
        try { photos.push(c.toDataURL("image/jpeg", 0.82)); } catch (err) {}
        if (++done === imgs.length) finish();
      };
      img.onerror = () => { if (++done === imgs.length) finish(); };
      img.src = e.target.result;
    };
    reader.onerror = () => { if (++done === imgs.length) finish(); };
    reader.readAsDataURL(file);
  });

  function finish() {
    lpSaveLandingPhotos(photos);
    lpPushLandingPhotos();
    lpRenderLandingPhotos();
    input.value = "";
    if (msg) { msg.textContent = "✅ Added"; setTimeout(() => (msg.textContent = ""), 2000); }
    showToast("Landing photos updated ✓");
  }
}
function lpRemoveLandingPhoto(i) {
  const photos = lpGetLandingPhotos();
  photos.splice(i, 1);
  lpSaveLandingPhotos(photos);
  lpPushLandingPhotos();
  lpRenderLandingPhotos();
}
function lpClearLandingPhotos() {
  if (!lpGetLandingPhotos().length) return;
  if (!confirm("Remove ALL landing photos?")) return;
  lpSaveLandingPhotos([]);
  lpPushLandingPhotos();
  lpRenderLandingPhotos();
  showToast("Landing photos cleared ✓");
}
/* Mirror photos to the server (settings store) when the DB backend is live. */
function lpPushLandingPhotos() {
  if (window.LpDB && window.LpDB.active) {
    window.LpDB.settingSave("landing_photos", lpGetLandingPhotos()).catch(() => {});
  } else if (window.LpSync && window.LpSync.hasBackend) {
    fetch("api.php?action=save&store=settings_landing_photos", {
      method: "POST",
      credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: lpGetLandingPhotos() }),
    }).catch(() => {});
  }
}

function lpAdminSetLang(code) {
  lpCaptureForm();
  lpAdminLang = code;
  renderTranslations();
}

function lpCaptureForm() {
  if (typeof window.LP_I18N_DEFAULTS === "undefined") return;
  const ov = lpOverrides();
  if (!ov[lpAdminLang]) ov[lpAdminLang] = {};
  const keys = Object.keys(window.LP_I18N_DEFAULTS.en);
  keys.forEach(k => {
    const e = document.getElementById("i18n_" + k);
    if (!e) return;
    const val = e.value;
    const def = (window.LP_I18N_DEFAULTS[lpAdminLang] || {})[k];
    if (val !== def) ov[lpAdminLang][k] = val;
    else if (ov[lpAdminLang][k] !== undefined) delete ov[lpAdminLang][k];
  });
  lpSaveI18nOverrides(ov);
  return ov;
}

function lpSaveI18nForm() {
  lpCaptureForm();
  const msg = document.getElementById("i18n_msg");
  if (msg) {
    msg.textContent = "✅ Saved!";
    msg.style.color = "#16a34a";
    setTimeout(() => (msg.textContent = ""), 2500);
  }
  showToast("Translations saved ✓");
}

function lpResetI18n() {
  if (!confirm("Reset ALL landing-page text back to the built-in defaults in every language?")) return;
  try {
    localStorage.removeItem("lp_landing_i18n");
  } catch (e) {}
  if (window.LpSync && window.LpSync.hasBackend) {
    fetch("api.php?action=save&store=settings_i18n", {
      method: "POST",
      credentials: "same-origin",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: {} }),
    }).catch(() => {});
  }
  renderTranslations();
  showToast("Translations reset to defaults ✓");
}

/* ════════════════════════════════════════════════════════════════
   COMBINED LANDING VIEW — controller (was landing.html)
   The landing is now an in-app overlay (#landingView) shown first.
   "Enter System" reveals the login gate; logged-in users skip it.
   ════════════════════════════════════════════════════════════════ */
const LV_FEATURE_ICONS = ["📋", "📜", "🌳", "🧬", "💞", "📊", "👥", "💾"];
const LV_STEP_KEYS = [["step1_title", "step1_desc"], ["step2_title", "step2_desc"], ["step3_title", "step3_desc"]];
const LV_STAT_KEYS = [["stat1_value", "stat1_label"], ["stat2_value", "stat2_label"], ["stat3_value", "stat3_label"], ["stat4_value", "stat4_label"]];

let lvLang = typeof lpGetLang === "function" ? lpGetLang() : "en";
let lvI18n = typeof lpGetI18n === "function" ? lpGetI18n() : { en: {}, ps: {}, fa: {} };

function lvT(key) {
  return (lvI18n[lvLang] && lvI18n[lvLang][key]) || (lvI18n.en && lvI18n.en[key]) || key;
}

function lpRenderLanding() {
  const root = document.getElementById("landingView");
  if (!root) return;
  lvI18n = typeof lpGetI18n === "function" ? lpGetI18n() : lvI18n;

  const fg = document.getElementById("lv_featgrid");
  if (fg) {
    let h = "";
    for (let i = 1; i <= 8; i++) {
      h += `<div class="lv-fcard lv-reveal"><div class="lv-ficon">${LV_FEATURE_ICONS[i - 1]}</div>
        <h3 data-i18n="feat${i}_title"></h3><p data-i18n="feat${i}_desc"></p></div>`;
    }
    fg.innerHTML = h;
  }
  const sg = document.getElementById("lv_stepgrid");
  if (sg) {
    sg.innerHTML = LV_STEP_KEYS.map(
      (s, i) =>
        `<div class="lv-step lv-reveal"><div class="lv-num">${i + 1}</div>
          <h3 data-i18n="${s[0]}"></h3><p data-i18n="${s[1]}"></p></div>`
    ).join("");
  }
  const sb = document.getElementById("lv_statsband");
  if (sb) {
    sb.innerHTML = LV_STAT_KEYS.map(
      s => `<div class="lv-reveal"><div class="lv-sv" data-i18n="${s[0]}"></div><div class="lv-sl" data-i18n="${s[1]}"></div></div>`
    ).join("");
  }
  lvBuildLangs();
  lvUpdateLogo();
  lvBuildGallery();
  lvApplyI18n();
  lvInitReveal();
}

/* Show the company logo (uploaded in Settings → Backup) in the landing header,
   falling back to the cow emoji when none is set. */
function lvUpdateLogo() {
  const el = document.getElementById("lv_logo");
  if (!el) return;
  const url = typeof getLogoDataUrl === "function" ? getLogoDataUrl() : "";
  el.innerHTML = url
    ? `<img src="${url}" alt="logo" style="width:100%;height:100%;object-fit:contain;border-radius:50%">`
    : "🐄";
}

/* ── Landing photo showcase (auto-rotating) ──
   Photos are managed by the admin in Settings → Landing and stored in
   localStorage key "lp_landing_photos" (array of image data-URLs). */
let _lvGalleryTimer = null;
let _lvGalleryIdx = 0;
let _lvRatios = [];
function lpGetLandingPhotos() {
  try {
    const a = JSON.parse(localStorage.getItem("lp_landing_photos") || "[]");
    return Array.isArray(a) ? a : [];
  } catch (e) {
    return [];
  }
}
function lpSaveLandingPhotos(arr) {
  try {
    localStorage.setItem("lp_landing_photos", JSON.stringify(arr || []));
  } catch (e) {
    showToast("⚠️ Storage full — remove some photos.");
  }
}
function lvBuildGallery() {
  const block = document.getElementById("lv-gallery-block");
  const gal = document.getElementById("lv_gallery");
  if (!block || !gal) return;
  if (_lvGalleryTimer) { clearInterval(_lvGalleryTimer); _lvGalleryTimer = null; }
  const photos = lpGetLandingPhotos();
  if (!photos.length) {
    block.style.display = "none";
    gal.innerHTML = "";
    return;
  }
  block.style.display = "";
  _lvGalleryIdx = 0;
  _lvRatios = new Array(photos.length).fill(null);
  gal.innerHTML =
    photos
      .map((src, i) => `<div class="lv-slide${i === 0 ? " on" : ""}" style="--img:url('${src}')"></div>`)
      .join("") +
    (photos.length > 1
      ? `<div class="lv-dots">${photos.map((_, i) => `<span class="lv-dot-i${i === 0 ? " on" : ""}" onclick="lvGalleryGo(${i})"></span>`).join("")}</div>`
      : "");
  // Measure each photo's shape so the frame can match it exactly (fills the
  // frame with no cropping and no empty side bars).
  photos.forEach((src, i) => {
    const im = new Image();
    im.onload = () => {
      _lvRatios[i] = im.naturalWidth / Math.max(1, im.naturalHeight);
      if (i === _lvGalleryIdx) lvApplyRatio(i);
    };
    im.src = src;
  });
  if (photos.length > 1) {
    _lvGalleryTimer = setInterval(() => lvGalleryGo(_lvGalleryIdx + 1), 4500);
  }
}
/* Size the gallery frame to the active photo's aspect ratio (clamped to a sane
   range) so the image fills it completely without cropping or letterboxing. */
function lvApplyRatio(i) {
  const gal = document.getElementById("lv_gallery");
  if (!gal) return;
  let r = _lvRatios[i];
  if (!r || !isFinite(r)) return;
  r = Math.max(0.75, Math.min(1.85, r)); // portrait … wide
  gal.style.aspectRatio = r + " / 1";
}
function lvGalleryGo(idx) {
  const gal = document.getElementById("lv_gallery");
  if (!gal) return;
  const slides = gal.querySelectorAll(".lv-slide");
  const dots = gal.querySelectorAll(".lv-dot-i");
  if (!slides.length) return;
  _lvGalleryIdx = ((idx % slides.length) + slides.length) % slides.length;
  slides.forEach((s, i) => s.classList.toggle("on", i === _lvGalleryIdx));
  dots.forEach((d, i) => d.classList.toggle("on", i === _lvGalleryIdx));
  lvApplyRatio(_lvGalleryIdx);
}

function lvBuildLangs() {
  const el = document.getElementById("lv_langs");
  if (!el) return;
  const langs = window.LP_LANGS || [
    { code: "en", native: "English" },
    { code: "ps", native: "پښتو" },
    { code: "fa", native: "دری" },
  ];
  el.innerHTML = langs
    .map(l => `<button class="lv-langbtn${l.code === lvLang ? " on" : ""}" onclick="lvSetLang('${l.code}')">${l.native}</button>`)
    .join("");
}

function lvApplyI18n() {
  const root = document.getElementById("landingView");
  if (!root) return;
  root.querySelectorAll("[data-i18n]").forEach(el => {
    el.textContent = lvT(el.getAttribute("data-i18n"));
  });
  const langDef = (window.LP_LANGS || []).find(l => l.code === lvLang);
  root.setAttribute("dir", langDef && langDef.rtl ? "rtl" : "ltr");
}

function lvSetLang(l) {
  lvLang = l;
  if (typeof lpSetLang === "function") lpSetLang(l);
  lvBuildLangs();
  lvApplyI18n();
}

function lvScrollTo(id) {
  const el = document.getElementById(id);
  const root = document.getElementById("landingView");
  if (el && root) root.scrollTo({ top: el.offsetTop - 60, behavior: "smooth" });
}
function lvScrollTop() {
  const root = document.getElementById("landingView");
  if (root) root.scrollTo({ top: 0, behavior: "smooth" });
}

function lvInitReveal() {
  const root = document.getElementById("landingView");
  if (!root || typeof IntersectionObserver === "undefined") return;
  const io = new IntersectionObserver(
    entries => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.classList.add("in");
          io.unobserve(e.target);
        }
      });
    },
    { root: root, threshold: 0.12 }
  );
  root.querySelectorAll(".lv-reveal").forEach((el, i) => {
    el.style.transitionDelay = Math.min(i % 6, 5) * 60 + "ms";
    io.observe(el);
  });
}

function lvInitScroll() {
  const root = document.getElementById("landingView");
  const hdr = document.getElementById("lv_hdr");
  if (root && hdr) {
    root.addEventListener("scroll", () => {
      hdr.classList.toggle("scrolled", root.scrollTop > 10);
    });
  }
}

/* Show / hide the landing overlay. */
function lpHideLanding() {
  const el = document.getElementById("landingView");
  if (el) el.style.display = "none";
}
function lpShowLanding() {
  const el = document.getElementById("landingView");
  if (!el) return;
  el.style.display = "block";
  lpRenderLanding();
  el.scrollTop = 0;
}

/* "Enter System" / "Login" from the landing. */
function lpEnterApp() {
  lpHideLanding();
  const sess = typeof getSession === "function" ? getSession() : null;
  if (!sess) {
    const g = document.getElementById("loginGate");
    if (g) g.style.display = "flex";
    setTimeout(() => {
      const u = document.getElementById("lg_user");
      if (u) u.focus();
    }, 60);
  }
}

/* Boot: render the landing for first-time visitors; logged-in users skip it. */
document.addEventListener("DOMContentLoaded", function () {
  try {
    lvInitScroll();
    let hasSession = false;
    try {
      hasSession = !!JSON.parse(sessionStorage.getItem("lp_session_v1") || "null");
    } catch (e) {}
    if (hasSession) lpHideLanding();
    else lpRenderLanding();
  } catch (e) {
    console.warn("landing init failed:", e);
  }
});

/* ── Detect the scalable MySQL backend (api_db.php) and, if a server session
   is already active, resume straight into the app. Runs before the user
   interacts; on a DB deployment this is what enables paged loading. ── */
document.addEventListener("DOMContentLoaded", async function lpDbBoot() {
  if (!window.LpDB) return;
  try {
    await window.LpDB.probe();
    DB_MODE = !!window.LpDB.active;
    if (!DB_MODE) return;
    const u = window.LpDB.user;
    if (!u) return; // not logged in — landing/login will handle it
    // Resume an existing DB session.
    const sess = setSession({
      id: u.id, username: u.username, role: u.role,
      ownerId: u.owner_id, name: u.name,
    });
    if (typeof lpHideLanding === "function") lpHideLanding();
    const gate = document.getElementById("loginGate");
    if (gate) gate.style.display = "none";
    if (sess.role === "admin") {
      const hdr = document.querySelector("header");
      const shell = document.querySelector(".shell");
      if (hdr) hdr.style.display = "";
      if (shell) shell.style.display = "";
      startApp();
    } else {
      showUserPortal(sess);
    }
  } catch (e) {
    console.warn("DB boot failed:", e);
  }
});

/* ════════════════════════════════════════════════════════════════
   INBREEDING — worked example for farmers (visual, stepped)
   A guided walkthrough of Wright's coefficient using a half-sibling
   mating (the classic 12.5% case), with a pedigree diagram, plain
   language, and a reference table of common matings.
   ════════════════════════════════════════════════════════════════ */

let ibExStep = 0;
const IB_EX_STEPS = 6; // 0..5

function showInbreedingExample() {
  let ov = document.getElementById("ibExOverlay");
  if (!ov) {
    ov = document.createElement("div");
    ov.id = "ibExOverlay";
    ov.className = "ibx-overlay";
    ov.addEventListener("click", e => {
      if (e.target === ov) ibExClose();
    });
    document.body.appendChild(ov);
  }
  ov.style.display = "flex";
  ibExStep = 0;
  ibExRender();
  document.addEventListener("keydown", ibExKey);
}
function ibExClose() {
  const ov = document.getElementById("ibExOverlay");
  if (ov) ov.style.display = "none";
  document.removeEventListener("keydown", ibExKey);
}
function ibExKey(e) {
  if (e.key === "Escape") ibExClose();
  else if (e.key === "ArrowRight") ibExNext();
  else if (e.key === "ArrowLeft") ibExPrev();
}
function ibExNext() {
  if (ibExStep < IB_EX_STEPS - 1) {
    ibExStep++;
    ibExRender();
  }
}
function ibExPrev() {
  if (ibExStep > 0) {
    ibExStep--;
    ibExRender();
  }
}

/* Pedigree diagram. `hi` selects which parts to highlight per step:
   hi.common = highlight the shared ancestor G + the inbreeding loop. */
function ibExDiagram(hi) {
  hi = hi || {};
  const loop = hi.common ? 1 : 0;
  const dim = loop ? 0.25 : 1; // fade non-loop lines when showing the loop
  const box = (x, y, label, sub, cls, glow) =>
    `<g>
      <rect x="${x}" y="${y}" width="118" height="48" rx="9"
        fill="${cls.bg}" stroke="${cls.bd}" stroke-width="${glow ? 3 : 1.5}"
        ${glow ? 'filter="url(#ibglow)"' : ""}/>
      <text x="${x + 59}" y="${y + 21}" text-anchor="middle" font-size="13" font-weight="700" fill="${cls.tx}">${label}</text>
      <text x="${x + 59}" y="${y + 37}" text-anchor="middle" font-size="9.5" fill="${cls.tx}" opacity=".8">${sub}</text>
    </g>`;
  const SIRE = { bg: "#eef3ff", bd: "#3a6bc4", tx: "#1e3a8a" };
  const DAM = { bg: "#f5eeff", bd: "#8e44ad", tx: "#6b21a8" };
  const ANC = { bg: loop ? "#fee2e2" : "#fff7ed", bd: loop ? "#c0392b" : "#e67e22", tx: loop ? "#991b1b" : "#9a3412" };
  const SELF = { bg: "#eaf6ea", bd: "#1a4a2e", tx: "#14532d" };
  // line helper
  const line = (x1, y1, x2, y2, on) =>
    `<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="${on ? "#c0392b" : "#9caa9c"}" stroke-width="${on ? 3 : 1.5}" opacity="${on ? 1 : loop ? dim : 1}"/>`;
  return `<svg viewBox="0 0 470 300" style="width:100%;max-width:520px;height:auto" font-family="sans-serif">
    <defs><filter id="ibglow" x="-30%" y="-30%" width="160%" height="160%">
      <feDropShadow dx="0" dy="0" stdDeviation="5" flood-color="#c0392b" flood-opacity="0.6"/></filter></defs>
    <!-- connectors (drawn first) -->
    ${line(150, 130, 79, 68, false)}    <!-- X→M1 -->
    ${line(150, 130, 230, 68, loop)}    <!-- X→G (loop) -->
    ${line(310, 130, 230, 68, loop)}    <!-- Y→G (loop) -->
    ${line(310, 130, 391, 68, false)}   <!-- Y→M2 -->
    ${line(230, 240, 150, 178, loop)}   <!-- Z→X (loop) -->
    ${line(230, 240, 310, 178, loop)}   <!-- Z→Y (loop) -->
    <!-- boxes -->
    ${box(20, 20, "Dam M1", "mother of X", DAM, false)}
    ${box(171, 20, "Grandsire G", "shared father", ANC, loop)}
    ${box(332, 20, "Dam M2", "mother of Y", DAM, false)}
    ${box(91, 130, "Bull X", "the father", SIRE, false)}
    ${box(251, 130, "Cow Y", "the mother", DAM, false)}
    ${box(171, 240, "Calf Z", "the result", SELF, false)}
  </svg>`;
}

function ibExRender() {
  const ov = document.getElementById("ibExOverlay");
  if (!ov) return;
  const steps = [
    {
      t: "What is inbreeding?",
      icon: "🐄",
      body: `<p>Inbreeding happens when a calf's <b>father and mother are related</b> — they share one or more ancestors.</p>
        <p>The <b>inbreeding coefficient (F)</b> is a single number that tells you how related they are, as a percentage. Higher F = more inbred.</p>
        <p>Let's work through a real example together, step by step.</p>`,
      diag: ibExDiagram({}),
    },
    {
      t: "Step 1 — Meet the animals",
      icon: "👀",
      body: `<p>We want to breed <b style="color:#1e3a8a">Bull X</b> with <b style="color:#6b21a8">Cow Y</b> to get <b style="color:#14532d">Calf Z</b>.</p>
        <p>But look carefully: Bull X and Cow Y are <b>half-siblings</b> — they have the <b>same father</b>, <b style="color:#9a3412">Grandsire G</b>, but different mothers (M1 and M2).</p>`,
      diag: ibExDiagram({}),
    },
    {
      t: "Step 2 — Find the shared ancestor",
      icon: "🔎",
      body: `<p><b style="color:#991b1b">Grandsire G</b> appears on <b>both sides</b> of the calf's family — once through the father, once through the mother.</p>
        <p>That shared ancestor is the <b>reason for the inbreeding</b>. The red loop shows how G connects the father and mother.</p>`,
      diag: ibExDiagram({ common: true }),
    },
    {
      t: "Step 3 — Count the steps",
      icon: "🧮",
      body: `<p>We count the generations from each parent up to the shared ancestor G:</p>
        <ul style="margin:8px 0 8px 18px;line-height:1.9">
          <li>Father's side: <b>Bull X → Grandsire G = 1 step</b></li>
          <li>Mother's side: <b>Cow Y → Grandsire G = 1 step</b></li>
        </ul>
        <p>Every step in the family line <b>halves</b> the shared genes.</p>`,
      diag: ibExDiagram({ common: true }),
    },
    {
      t: "Step 4 — Apply the formula",
      icon: "✏️",
      body: `<p>Wright's formula for one shared ancestor:</p>
        <div class="ibx-formula">F = ( ½ ) <sup>n₁ + n₂ + 1</sup></div>
        <p>where n₁ and n₂ are the steps we counted (1 and 1):</p>
        <div class="ibx-formula">F = ( ½ ) <sup>1 + 1 + 1</sup> = ( ½ )<sup>3</sup> = <b>1 ⁄ 8</b></div>
        <p style="text-align:center;font-size:13px;color:var(--tm)">(The “+1” links the father and mother through the shared ancestor.)</p>`,
      diag: ibExDiagram({ common: true }),
    },
    {
      t: "Step 5 — The result",
      icon: "✅",
      body: `<div style="text-align:center;margin-bottom:10px">${fBadge(12.5)}</div>
        <p><b>F = 1 ⁄ 8 = 12.5%.</b> About 1 in every 8 of the calf's gene pairs come from the same source.</p>
        <p>This is a <b>moderate-to-high</b> level. Higher inbreeding can mean <b>less milk, weaker calves, and fertility problems</b>. Breeders usually try to keep F <b>below 6.25%</b>.</p>
        <div class="ibx-ref">
          <div class="ibx-ref-h">Common matings — at a glance</div>
          ${[
            ["Father × daughter / Mother × son", "25%", "#991b1b"],
            ["Full brother × sister", "25%", "#991b1b"],
            ["Half-brother × half-sister (this example)", "12.5%", "#9a3412"],
            ["Grandparent × grandchild", "12.5%", "#9a3412"],
            ["Uncle/Aunt × niece/nephew", "12.5%", "#9a3412"],
            ["First cousins", "6.25%", "#854d0e"],
            ["Unrelated animals", "0%", "#166534"],
          ]
            .map(
              ([m, f, c]) =>
                `<div class="ibx-ref-row"><span>${m}</span><b style="color:${c}">${f}</b></div>`
            )
            .join("")}
        </div>
        <p style="font-size:12px;color:var(--tm);margin-top:10px">💡 Use <b>Breeding → Mating Advisor</b> and <b>Deep Pedigree</b> to see these exact numbers for <b>your own animals</b>.</p>`,
      diag: ibExDiagram({}),
    },
  ];
  const s = steps[ibExStep];
  const dots = steps
    .map((_, i) => `<span class="ibx-dot${i === ibExStep ? " on" : ""}" onclick="ibExGo(${i})"></span>`)
    .join("");
  ov.innerHTML = `
    <div class="ibx-modal">
      <div class="ibx-head">
        <div><span style="font-size:20px;margin-inline-end:6px">${s.icon}</span><b>${s.t}</b></div>
        <button class="ibx-x" onclick="ibExClose()" title="Close">✕</button>
      </div>
      <div class="ibx-body">
        <div class="ibx-diag">${s.diag}</div>
        <div class="ibx-text">${s.body}</div>
      </div>
      <div class="ibx-foot">
        <button class="btn bd bsm" onclick="ibExPrev()" ${ibExStep === 0 ? "disabled" : ""}>← Back</button>
        <div class="ibx-dots">${dots}</div>
        ${
          ibExStep < IB_EX_STEPS - 1
            ? `<button class="btn bg bsm" onclick="ibExNext()">Next →</button>`
            : `<button class="btn bg bsm" onclick="ibExClose()">Done ✓</button>`
        }
      </div>
    </div>`;
}
function ibExGo(i) {
  ibExStep = Math.max(0, Math.min(IB_EX_STEPS - 1, i));
  ibExRender();
}

/* ════════════════════════════════════════════════════════════════
   GENETIC DEFECTS / RECESSIVE CARRIER TRACKING
   Record each animal's status for known recessive conditions and warn
   on at-risk matings (both parents carrying the same recessive).
   Status codes: "" unknown · F tested-free · C carrier · A affected
   ════════════════════════════════════════════════════════════════ */

/* Known recessive conditions. `breeds` limits which animals show each row
   ("Any" shows for all). Edit this list to suit your herd / region. */
const GENETIC_DEFECTS_DEFAULT = [
  { code: "BLAD", name: "Bovine Leukocyte Adhesion Deficiency", breeds: ["Holstein"] },
  { code: "CVM", name: "Complex Vertebral Malformation", breeds: ["Holstein"] },
  { code: "BY", name: "Brachyspina", breeds: ["Holstein"] },
  { code: "DUMPS", name: "Uridine Monophosphate Synthase Deficiency", breeds: ["Holstein"] },
  { code: "CD", name: "Cholesterol Deficiency", breeds: ["Holstein"] },
  { code: "CN", name: "Citrullinemia", breeds: ["Holstein"] },
  { code: "FXI", name: "Factor XI Deficiency", breeds: ["Holstein"] },
  { code: "MF", name: "Mulefoot (Syndactyly)", breeds: ["Any"] },
  { code: "HH", name: "Fertility Haplotype (HH/MH)", breeds: ["Holstein", "Montbeliard"] },
];
/* Admin-editable list (Settings → Backup → Genetic Conditions), stored in
   localStorage "lp_genetic_defects". Falls back to the defaults above. */
function loadGeneticDefects() {
  try {
    const o = JSON.parse(localStorage.getItem("lp_genetic_defects") || "null");
    if (Array.isArray(o) && o.length) return o;
  } catch (e) {}
  return GENETIC_DEFECTS_DEFAULT.map(d => ({ ...d, breeds: [...d.breeds] }));
}
function saveGeneticDefects(list) {
  GENETIC_DEFECTS = list;
  try {
    localStorage.setItem("lp_genetic_defects", JSON.stringify(list));
  } catch (e) {}
}
let GENETIC_DEFECTS = loadGeneticDefects();

const DEFECT_STATUS = [
  { v: "", label: "Unknown", short: "—", color: "#9ca3af" },
  { v: "F", label: "Tested Free", short: "Free", color: "#16a34a" },
  { v: "C", label: "Carrier", short: "Carrier", color: "#b45309" },
  { v: "A", label: "Affected", short: "Affected", color: "#c0392b" },
];

function defectsForBreed(breed) {
  return GENETIC_DEFECTS.filter(d => d.breeds.includes("Any") || d.breeds.includes(breed) || !breed);
}
function defectStatusMeta(v) {
  return DEFECT_STATUS.find(s => s.v === (v || "")) || DEFECT_STATUS[0];
}

/* Build the per-condition selects inside the form's Genetic Health section. */
function lpBuildDefectForm(defects) {
  const host = document.getElementById("genSecBody");
  if (!host) return;
  defects = defects || {};
  const breed = typeof curBreed !== "undefined" ? curBreed : "Holstein";
  const list = defectsForBreed(breed);
  host.innerHTML =
    `<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:8px">` +
    list
      .map(d => {
        const cur = defects[d.code] || "";
        const opts = DEFECT_STATUS.map(
          s => `<option value="${s.v}"${s.v === cur ? " selected" : ""}>${s.label}</option>`
        ).join("");
        return `<div class="fr">
          <label style="font-size:11px">${d.code} <span style="color:var(--tl);font-weight:400">${d.name}</span></label>
          <select id="f_def_${d.code}" style="font-size:12px">${opts}</select>
        </div>`;
      })
      .join("") +
    `</div>`;
}

/* Read the defect selects back into a {code: status} object (omits unknowns). */
function lpCaptureDefects() {
  const out = {};
  GENETIC_DEFECTS.forEach(d => {
    const e = document.getElementById("f_def_" + d.code);
    if (e && e.value) out[d.code] = e.value;
  });
  return out;
}

/* Compact carrier badges for an animal (used on the herd page). */
function defectBadges(a) {
  const def = (a && a.defects) || {};
  const items = Object.keys(def).filter(k => def[k]);
  if (!items.length) return "";
  return (
    `<div style="display:flex;flex-wrap:wrap;gap:5px;margin-top:6px">` +
    items
      .map(k => {
        const m = defectStatusMeta(def[k]);
        return `<span style="font-family:sans-serif;font-size:10px;font-weight:700;padding:2px 8px;border-radius:10px;background:${m.color}22;color:${m.color};border:1px solid ${m.color}55">${k}: ${m.short}</span>`;
      })
      .join("") +
    `</div>`
  );
}

/* At-risk mating analysis. Returns conditions where BOTH parents can pass the
   recessive (carrier or affected on each side) → affected calves possible. */
function matingDefectRisk(sire, dam) {
  if (!sire || !dam) return [];
  const sd = sire.defects || {};
  const dd = dam.defects || {};
  const risks = [];
  GENETIC_DEFECTS.forEach(d => {
    const s = sd[d.code] || "";
    const m = dd[d.code] || "";
    const sBad = s === "C" || s === "A";
    const mBad = m === "C" || m === "A";
    if (sBad && mBad) {
      // probability a calf is affected (homozygous recessive)
      const p = st => (st === "A" ? 1 : 0.5); // fraction of gametes carrying the allele
      const affected = Math.round(p(s) * p(m) * 100);
      risks.push({
        code: d.code,
        name: d.name,
        sireStatus: s,
        damStatus: m,
        affectedPct: affected,
        level: affected >= 50 ? "severe" : "high",
      });
    } else if ((sBad && m === "") || (mBad && s === "")) {
      // one parent at risk, the other untested → cannot rule out
      risks.push({
        code: d.code,
        name: d.name,
        sireStatus: s,
        damStatus: m,
        affectedPct: null,
        level: "caution",
      });
    }
  });
  return risks;
}

/* Warning panel HTML for a sire × dam pairing (empty string if no risk). */
function defectWarningHTML(sire, dam) {
  const risks = matingDefectRisk(sire, dam);
  if (!risks.length) return "";
  const real = risks.filter(r => r.level !== "caution");
  const caution = risks.filter(r => r.level === "caution");
  const rows = real
    .map(r => {
      const col = r.level === "severe" ? "#7f1d1d" : "#b45309";
      const sm = defectStatusMeta(r.sireStatus).short;
      const dm = defectStatusMeta(r.damStatus).short;
      return `<div style="display:flex;justify-content:space-between;gap:8px;padding:4px 0;border-top:1px solid #fde0d0;font-family:sans-serif;font-size:12px">
        <span><b>${r.code}</b> — ${r.name}<br><span style="color:var(--tl);font-size:10px">Sire: ${sm} · Dam: ${dm}</span></span>
        <b style="color:${col};white-space:nowrap">${r.affectedPct}% affected</b>
      </div>`;
    })
    .join("");
  const cautionLine = caution.length
    ? `<div style="font-family:sans-serif;font-size:11px;color:#92400e;margin-top:6px">⚠ Untested risk: ${caution.map(c => c.code).join(", ")} — one parent carries it, the other isn't tested. Test before mating to be sure.</div>`
    : "";
  if (!real.length && !caution.length) return "";
  return `
  <div style="border:2px solid #fca5a5;background:#fef2f2;border-radius:var(--rl);padding:12px 14px;margin-bottom:12px">
    <div style="font-family:sans-serif;font-weight:800;color:#991b1b;font-size:13px;margin-bottom:4px">⛔ Genetic defect risk in this mating</div>
    ${real.length ? `<div style="font-family:sans-serif;font-size:11px;color:#7f1d1d;margin-bottom:4px">Both parents can pass these recessive condition(s) to the calf:</div>${rows}` : ""}
    ${cautionLine}
  </div>`;
}

/* ════════════════════════════════════════════════════════════════
   BREEDING-SPECIALIST TOOLS
   2) Herd Genetics report — generation interval, ΔF, effective pop size Ne
   3) Optimal Mate Allocation — assign sires to cows to minimise inbreeding
   4) Selection Index — rank animals by weighted genetic merit
   ════════════════════════════════════════════════════════════════ */

/* fractional year from an ISO date */
function _yrFrac(d) {
  const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(d || "");
  if (!m) return null;
  return +m[1] + (+m[2] - 1) / 12 + (+m[3] - 1) / 365;
}

/* ── Generation interval: avg age of a parent when its offspring was born ── */
function generationInterval() {
  let sum = 0, n = 0, sireSum = 0, sireN = 0, damSum = 0, damN = 0;
  animals.forEach(a => {
    const ay = _yrFrac(a.dob);
    if (ay == null) return;
    const sp = a.sireLinkedId ? byId(a.sireLinkedId) : null;
    const dp = a.damLinkedId ? byId(a.damLinkedId) : null;
    [[sp, "s"], [dp, "d"]].forEach(([p, side]) => {
      const py = p ? _yrFrac(p.dob) : null;
      if (py == null) return;
      const d = ay - py;
      if (d > 0.5 && d < 25) {
        sum += d; n++;
        if (side === "s") { sireSum += d; sireN++; } else { damSum += d; damN++; }
      }
    });
  });
  return {
    gi: n ? sum / n : null,
    pairs: n,
    sireGI: sireN ? sireSum / sireN : null,
    damGI: damN ? damSum / damN : null,
  };
}

/* ── Rate of inbreeding ΔF and effective population size Ne ──
   ΔF/year from the least-squares slope of mean F over birth years;
   ΔF/generation = ΔF/year × generation interval; Ne = 1 / (2·ΔF). */
function inbreedingRate() {
  const trend = typeof herdInbreedingTrend === "function" ? herdInbreedingTrend() : [];
  const giObj = generationInterval();
  const gi = giObj.gi;
  if (trend.length < 2) return { trend, gi, slopePerYear: null, deltaFperGen: null, ne: null };
  let n = trend.length, sx = 0, sy = 0, sxy = 0, sxx = 0;
  trend.forEach(t => {
    const x = +t.year, y = t.avgF;
    sx += x; sy += y; sxy += x * y; sxx += x * x;
  });
  const denom = n * sxx - sx * sx;
  const slope = denom ? (n * sxy - sx * sy) / denom : 0; // %F per year
  const deltaFperYear = slope / 100;
  const deltaFperGen = gi ? deltaFperYear * gi : null;
  let ne = null;
  if (deltaFperGen && deltaFperGen > 0.00001) ne = 1 / (2 * deltaFperGen);
  return { trend, gi, slopePerYear: slope, deltaFperYear, deltaFperGen, ne };
}

let _hgStats = null; // server-computed herd stats (DB mode)
function renderHerdGenetics() {
  const el = document.getElementById("pg-herdgen");
  if (!el) return;
  // DB mode: compute the herd metrics on the SERVER (works at 3M rows) instead
  // of iterating the in-memory cache.
  if (DB_MODE && _hgStats === null) {
    el.innerHTML = '<div class="card"><div class="cb"><div style="padding:14px;color:var(--tl);font-size:12px;font-family:sans-serif">Loading herd genetics…</div></div></div>';
    window.LpDB.herdStats().then(s => { _hgStats = s || {}; renderHerdGenetics(); }).catch(() => { _hgStats = {}; renderHerdGenetics(); });
    return;
  }
  let gi, ir, avgF, withFCount;
  if (DB_MODE) {
    const s = _hgStats || {};
    gi = { gi: s.gi != null ? s.gi : null, pairs: "" };
    ir = { ne: s.ne != null ? s.ne : null, deltaFperGen: s.deltaFperGen != null ? s.deltaFperGen : null, slopePerYear: s.slopePerYear != null ? s.slopePerYear : null };
    avgF = s.avgF != null ? s.avgF : null;
    withFCount = s.withF || 0;
  } else {
    gi = generationInterval();
    ir = inbreedingRate();
    const fVals = animals.map(a => animalF(a)).filter(r => r && r.F > 0).map(r => r.F);
    avgF = fVals.length ? fVals.reduce((s, v) => s + v, 0) / fVals.length : null;
    withFCount = fVals.length;
  }

  const card = (v, l, sub, col) =>
    `<div class="sc"><div class="sv" style="color:${col || "var(--gd)"}">${v}</div><div class="sl">${l}</div>${sub ? `<div style="font-size:9px;color:var(--tl);margin-top:2px">${sub}</div>` : ""}</div>`;

  const neVal = ir.ne != null ? Math.round(ir.ne) : null;
  const neCol = neVal == null ? "var(--tl)" : neVal < 50 ? "#c0392b" : neVal < 100 ? "#b45309" : "#16a34a";
  const dfGen = ir.deltaFperGen != null ? (ir.deltaFperGen * 100).toFixed(3) + "%" : "—";

  const interp =
    neVal == null
      ? "Not enough birth-year + pedigree data yet to estimate the rate of inbreeding. Add DOBs and link parents."
      : neVal < 50
        ? "⚠️ Effective population size is LOW — the gene pool is narrowing. Introduce unrelated genetics and avoid overusing a few sires."
        : neVal < 100
          ? "Moderate. Watch sire usage and keep introducing outcross genetics to protect diversity."
          : "Healthy effective population size — diversity is being maintained well.";

  el.innerHTML = `
  ${subTabBarHTML ? "" : ""}
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#0f3a23,#2f7d52)"><h2 style="color:#fff">📊 Herd Genetics Report</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">Population-level breeding management metrics</span></div>
    <div class="cb">
      <div class="dp-stats" style="grid-template-columns:repeat(auto-fit,minmax(150px,1fr))">
        ${card(gi.gi != null ? gi.gi.toFixed(1) + " yr" : "—", "Generation interval", gi.pairs ? gi.pairs + " parent–offspring" : "from linked pedigrees", "var(--gd)")}
        ${card(dfGen, "Inbreeding rate (ΔF / gen)", ir.slopePerYear != null ? (ir.slopePerYear).toFixed(3) + "%/yr" : "", ir.deltaFperGen > 0 ? "#c0392b" : "#16a34a")}
        ${card(neVal != null ? neVal : "—", "Effective pop. size (Ne)", "1 / (2·ΔF)", neCol)}
        ${card(avgF != null ? avgF.toFixed(2) + "%" : "—", "Herd average inbreeding", withFCount.toLocaleString() + " animals", "var(--gd)")}
      </div>
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);background:#f0fdf4;border:1px solid #a7f3d0;border-radius:var(--r);padding:11px 14px;margin:12px 0;line-height:1.6">
        <b>What this means:</b> ${interp}
      </div>
      <div style="font-family:sans-serif;font-size:11px;color:var(--tl);line-height:1.7">
        <b>Generation interval</b> = average age of parents when their calves are born (lower = faster genetic progress).<br>
        <b>ΔF / generation</b> = how fast inbreeding rises each generation.
        <b>Ne</b> ≈ how many “equivalent” breeding animals your herd genetically behaves like — higher is healthier (aim for ≥ 100).
      </div>
      ${!DB_MODE && typeof herdTrendChartHTML === "function" ? herdTrendChartHTML() : ""}
    </div>
  </div>

  <!-- Batch precompute (mainly for server mode / big herds) -->
  <div class="card">
    <div class="ch"><h2>♻️ Precompute Herd Inbreeding</h2></div>
    <div class="cb">
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);margin-bottom:10px;line-height:1.6">
        Calculate &amp; store every animal's inbreeding coefficient so the herd report and ranking stay instant at scale.
        Run this after importing data or major pedigree changes.
      </div>
      <div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center">
        <button class="btn bg" id="gdRecomputeBtn" onclick="recomputeHerdF()">▶ Recompute now</button>
        <span id="gdRecomputeMsg" style="font-family:sans-serif;font-size:12px;color:var(--tl)"></span>
      </div>
    </div>
  </div>

  <!-- Editable list of tracked genetic conditions -->
  <div class="card">
    <div class="ch"><h2>⚙️ Tracked Genetic Conditions</h2>
      <span style="font-family:sans-serif;font-size:10px;color:var(--tl)">edit the recessive defects this herd screens for</span></div>
    <div class="cb">
      <div style="display:grid;grid-template-columns:90px 1fr 130px 30px;gap:8px;font-family:sans-serif;font-size:10px;color:var(--tl);font-weight:700;margin-bottom:5px">
        <span>CODE</span><span>CONDITION NAME</span><span>BREEDS</span><span></span>
      </div>
      <div id="gdEditor"></div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:10px">
        <button class="btn bd bsm" onclick="gdAdd()">＋ Add condition</button>
        <button class="btn bg bsm" onclick="gdSave()">💾 Save list</button>
        <button class="btn bd bsm" onclick="gdReset()">↺ Reset to defaults</button>
      </div>
    </div>
  </div>`;
  gdRenderEditor();
}

/* ── Genetic-conditions editor ── */
function gdRenderEditor() {
  const host = document.getElementById("gdEditor");
  if (!host) return;
  host.innerHTML = GENETIC_DEFECTS.map(
    (d, i) => `<div style="display:grid;grid-template-columns:90px 1fr 130px 30px;gap:8px;align-items:center;margin-bottom:6px">
      <input id="gd_code_${i}" value="${dpEscape(d.code)}" placeholder="Code" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
      <input id="gd_name_${i}" value="${dpEscape(d.name)}" placeholder="Condition name" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
      <input id="gd_breeds_${i}" value="${dpEscape((d.breeds || []).join(", "))}" placeholder="Any" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
      <button class="ib" onclick="gdRemove(${i})" title="Remove" style="color:#c0392b">🗑</button>
    </div>`
  ).join("");
}
function gdRemove(i) { GENETIC_DEFECTS.splice(i, 1); gdRenderEditor(); }
function gdAdd() { GENETIC_DEFECTS.push({ code: "", name: "", breeds: ["Any"] }); gdRenderEditor(); }
function gdSave() {
  const list = [];
  GENETIC_DEFECTS.forEach((_, i) => {
    const code = (document.getElementById("gd_code_" + i)?.value || "").trim();
    const name = (document.getElementById("gd_name_" + i)?.value || "").trim();
    const breeds = (document.getElementById("gd_breeds_" + i)?.value || "Any")
      .split(",").map(s => s.trim()).filter(Boolean);
    if (code) list.push({ code, name, breeds: breeds.length ? breeds : ["Any"] });
  });
  saveGeneticDefects(list);
  gdRenderEditor();
  showToast("Genetic conditions saved ✓");
}
function gdReset() {
  if (!confirm("Reset the genetic-conditions list to defaults?")) return;
  try { localStorage.removeItem("lp_genetic_defects"); } catch (e) {}
  GENETIC_DEFECTS = loadGeneticDefects();
  gdRenderEditor();
  showToast("Reset to defaults ✓");
}

/* ── Batch precompute of every animal's inbreeding F ──
   Local mode computes from the in-memory herd. DB mode pages through the
   server, computing F from each animal's pedigree subgraph and storing it
   via set_f (for the instant herd report). Big herds: run occasionally. */
let _recomputeStop = false;
async function recomputeHerdF() {
  const msg = document.getElementById("gdRecomputeMsg");
  const btn = document.getElementById("gdRecomputeBtn");
  const setMsg = t => { if (msg) msg.textContent = t; };

  if (!DB_MODE) {
    let n = 0;
    animals.forEach(a => {
      const r = animalF(a);
      a.inbreeding_f = r && r.F ? Math.round(r.F * 100) / 100 : 0;
      n++;
    });
    try { persist(); } catch (e) {}
    setMsg(`✅ Computed inbreeding for ${n} animals.`);
    showToast("Herd inbreeding recomputed ✓");
    return;
  }

  // DB mode — page through the server.
  if (btn && btn.dataset.running === "1") { _recomputeStop = true; return; }
  _recomputeStop = false;
  if (btn) { btn.dataset.running = "1"; btn.textContent = "⏹ Stop"; }
  let page = 1, done = 0, total = 0;
  try {
    do {
      const res = await window.LpDB.animalsPage({ page, perPage: 50, sort: "created" });
      total = res.total || 0;
      const rows = res.rows || [];
      if (!rows.length) break;
      for (const a of rows) {
        if (_recomputeStop) break;
        try {
          const anc = await window.LpDB.pedigree(a.id, IB_DEPTH);
          lpCacheMerge(anc);
          const full = byId(a.id) || a;
          const r = subjectF(full, IB_DEPTH);
          const d = pedigreeDiversity(full, IB_DEPTH);
          await window.LpDB.setF(a.id, r ? r.F : 0, d ? d.avk : null);
        } catch (e) {}
        done++;
        if (done % 10 === 0) setMsg(`Processing… ${done.toLocaleString()} / ${total.toLocaleString()}`);
      }
      page++;
    } while (!_recomputeStop && (page - 1) * 50 < total);
    setMsg(_recomputeStop ? `Stopped at ${done.toLocaleString()} / ${total.toLocaleString()}.` : `✅ Done — ${done.toLocaleString()} animals.`);
  } catch (e) {
    setMsg("Error: " + (e.message || e));
  } finally {
    if (btn) { btn.dataset.running = "0"; btn.textContent = "▶ Recompute now"; }
  }
}

/* ── Optimal mate allocation ──────────────────────────────────────── */
/* Selection-based so it scales: you pick the cows and sires to plan, rather
   than allocating across the whole (possibly millions) herd. */
let mpState = { cowIds: [], sireIds: [], maxPerSire: 0, result: null };
let _mpSearchT = null;

function renderMatePlan() {
  const el = document.getElementById("pg-mateplan");
  if (!el) return;
  const picker = (kind, label, icon, extra) => `
    <div style="border:1px solid var(--bdr);border-radius:var(--rl);padding:12px">
      <div style="font-family:sans-serif;font-size:12px;font-weight:700;color:var(--gd);margin-bottom:8px;display:flex;justify-content:space-between;align-items:center;gap:6px">
        <span>${icon} ${label} (<span id="mp_${kind}Count">0</span>)</span>${extra || ""}
      </div>
      <div style="position:relative">
        <input id="mp_${kind}Search" autocomplete="off" placeholder="🔍 Search ${kind === "cow" ? "cows" : "sires"} by name / ID…"
          oninput="mpSearch('${kind}',this.value)" onfocus="mpSearch('${kind}',this.value)"
          style="width:100%;padding:7px 10px;border:1px solid var(--bdr);border-radius:8px;font-family:sans-serif;font-size:12px">
        <div id="mp_${kind}Res" style="display:none;position:absolute;top:100%;left:0;right:0;z-index:30;background:#fff;border:1px solid var(--bdr);border-radius:8px;box-shadow:var(--shl);max-height:240px;overflow:auto;margin-top:3px"></div>
      </div>
      <div id="mp_${kind}Chips" style="margin-top:8px"></div>
    </div>`;

  el.innerHTML = `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#1e3a5f,#3a6bc4)"><h2 style="color:#fff">📋 Optimal Mate Plan</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">Pick cows &amp; sires → best low-inbreeding, defect-safe pairings</span></div>
    <div class="cb">
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);margin-bottom:12px;line-height:1.6">
        Choose the <b>cows you want to breed</b> and the <b>sires available</b> to you. The planner pairs each cow with
        her best sire — lowest predicted offspring inbreeding, avoiding shared genetic defects.
      </div>
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px;margin-bottom:14px">
        ${picker("cow", "Cows to mate", "🐄", "")}
        ${picker("sire", "Available sires", "🐂", `<button class="btn bd bsm" onclick="mpAddAllSires()">＋ all sires</button>`)}
      </div>
      <div style="display:flex;gap:12px;flex-wrap:wrap;align-items:center;margin-bottom:14px">
        <label class="dp-ctrl"><span>Max matings per sire</span>
          <input id="mp_max" type="number" min="0" value="${mpState.maxPerSire || ""}" placeholder="unlimited" style="width:120px;padding:7px 9px;border:1px solid var(--bdr);border-radius:8px">
        </label>
        <button class="btn bg" onclick="mpGenerate()">⚙️ Generate plan</button>
        <button class="btn bd bsm" onclick="mpState.cowIds=[];mpState.sireIds=[];mpState.result=null;renderMatePlan()">Clear</button>
      </div>
      <div id="mp_result"></div>
    </div>
  </div>`;
  mpRenderChips("cow");
  mpRenderChips("sire");
  if (mpState.result) mpRenderResult();
}

/* Search cows/sires for the picker — local filters the cache, DB queries the server. */
function mpSearch(kind, q) {
  const res = document.getElementById("mp_" + kind + "Res");
  if (!res) return;
  q = (q || "").trim();
  if (!q) { res.style.display = "none"; res.innerHTML = ""; return; }
  const sex = kind === "cow" ? "Female" : "Male";
  const render = list => {
    res.innerHTML =
      list
        .slice(0, 15)
        .map(
          a => `<div onclick="mpAdd('${kind}','${a.id}')" style="padding:7px 10px;cursor:pointer;font-family:sans-serif;font-size:12px;border-bottom:1px solid #f0f0f0">
            <b>${dpEscape(a.name || "—")}</b> <span style="color:var(--tl)">${a.regId ? "· " + dpEscape(a.regId) : ""}</span></div>`
        )
        .join("") || '<div style="padding:8px 10px;color:var(--tl);font-size:12px;font-family:sans-serif">No matches</div>';
    res.style.display = "block";
  };
  if (DB_MODE) {
    if (_mpSearchT) clearTimeout(_mpSearchT);
    _mpSearchT = setTimeout(() => {
      window.LpDB
        .animalsPage({ q, sex, page: 1, perPage: 15 })
        .then(r => {
          lpCacheMerge(r.rows || []);
          let rows = r.rows || [];
          if (kind === "sire") rows = rows.filter(a => a.role === "Sire" || !a.role);
          render(rows);
        })
        .catch(() => render([]));
    }, 220);
  } else {
    const ql = q.toLowerCase();
    let list = animals.filter(
      a =>
        a.sex === sex &&
        ((a.name || "").toLowerCase().includes(ql) ||
          (a.regId || "").toLowerCase().includes(ql) ||
          (a.earTag || "").toLowerCase().includes(ql))
    );
    if (kind === "sire") list = list.filter(a => a.role === "Sire" || !a.role);
    render(list);
  }
}
function mpAdd(kind, id) {
  const arr = kind === "cow" ? mpState.cowIds : mpState.sireIds;
  if (!arr.includes(String(id))) arr.push(String(id));
  const res = document.getElementById("mp_" + kind + "Res");
  if (res) { res.style.display = "none"; res.innerHTML = ""; }
  const inp = document.getElementById("mp_" + kind + "Search");
  if (inp) inp.value = "";
  mpRenderChips(kind);
}
function mpRemove(kind, id) {
  if (kind === "cow") mpState.cowIds = mpState.cowIds.filter(x => x !== String(id));
  else mpState.sireIds = mpState.sireIds.filter(x => x !== String(id));
  mpRenderChips(kind);
}
function mpRenderChips(kind) {
  const host = document.getElementById("mp_" + kind + "Chips");
  const cnt = document.getElementById("mp_" + kind + "Count");
  const arr = kind === "cow" ? mpState.cowIds : mpState.sireIds;
  if (cnt) cnt.textContent = arr.length;
  if (!host) return;
  host.innerHTML =
    arr
      .map(id => {
        const a = byId(id);
        const nm = a ? a.name : id;
        return `<span style="display:inline-flex;align-items:center;gap:5px;background:var(--gp);border:1px solid var(--bdr);border-radius:12px;padding:3px 9px;font-size:11px;margin:2px;font-family:sans-serif">${dpEscape(nm)} <span style="cursor:pointer;color:#c0392b;font-weight:700" onclick="mpRemove('${kind}','${id}')">✕</span></span>`;
      })
      .join("") || '<span style="color:var(--tl);font-size:11px;font-family:sans-serif">None selected</span>';
}
function mpAddAllSires() {
  const add = rows => {
    rows
      .filter(a => a.sex === "Male" && (a.role === "Sire" || !a.role))
      .forEach(a => { if (!mpState.sireIds.includes(String(a.id))) mpState.sireIds.push(String(a.id)); });
    mpRenderChips("sire");
  };
  if (DB_MODE) {
    window.LpDB
      .animalsPage({ sex: "Male", page: 1, perPage: 200 })
      .then(r => { lpCacheMerge(r.rows || []); add(r.rows || []); })
      .catch(() => {});
  } else {
    add(animals);
  }
}

function allocateMatings(cowIds, sireIds, maxPerSire) {
  const cows = cowIds.map(byId).filter(Boolean);
  const sires = sireIds.map(byId).filter(Boolean);
  const usage = {};
  const cap = maxPerSire && maxPerSire > 0 ? maxPerSire : Infinity;
  const out = [];
  // Cows with higher own inbreeding first (they benefit most from a careful match)
  const ordered = cows.slice().sort((a, b) => {
    const fa = animalF(a), fb = animalF(b);
    return (fb && fb.F ? fb.F : 0) - (fa && fa.F ? fa.F : 0);
  });
  ordered.forEach(cow => {
    let best = null;
    sires.forEach(s => {
      if ((usage[s.id] || 0) >= cap) return;
      const F = wrightF(s, cow, IB_DEPTH).F;
      const defects = matingDefectRisk(s, cow).filter(r => r.level !== "caution" && r.affectedPct > 0);
      const score = F + (defects.length ? 1000 : 0);
      if (!best || score < best.score) best = { s, F, defects, score };
    });
    if (best) {
      usage[best.s.id] = (usage[best.s.id] || 0) + 1;
      out.push({ cow, sire: best.s, F: best.F, defects: best.defects });
    } else {
      out.push({ cow, sire: null, F: null, defects: [] });
    }
  });
  return out;
}

async function mpGenerate() {
  mpState.maxPerSire = parseInt(document.getElementById("mp_max")?.value, 10) || 0;
  if (!mpState.cowIds.length || !mpState.sireIds.length) {
    showToast("Pick at least one cow and one sire");
    return;
  }
  if (mpState.cowIds.length > 500 && !confirm(mpState.cowIds.length + " cows selected — this may take a moment. Continue?")) return;
  const host = document.getElementById("mp_result");
  if (host) host.innerHTML = '<div style="padding:14px;color:var(--tl);font-size:12px;font-family:sans-serif">Calculating best matings…</div>';
  // DB mode: pull the selected animals' ancestor subgraphs so wrightF works.
  if (DB_MODE) {
    const ids = [...new Set([...mpState.cowIds, ...mpState.sireIds])];
    for (let i = 0; i < ids.length; i += 50) {
      try {
        const rows = await window.LpDB.ancestorsBatch(ids.slice(i, i + 50), IB_DEPTH);
        lpCacheMerge(rows);
      } catch (e) {}
    }
  }
  mpState.result = allocateMatings(mpState.cowIds, mpState.sireIds, mpState.maxPerSire);
  mpRenderResult();
}
function mpRenderResult() {
  const host = document.getElementById("mp_result");
  if (!host) return;
  const plan = mpState.result || [];
  const matched = plan.filter(p => p.sire);
  const avgF = matched.length ? matched.reduce((s, p) => s + p.F, 0) / matched.length : null;
  const rows = plan
    .map(p => {
      const fc = p.F != null ? fColor(p.F) : { color: "#aaa", bg: "#f9f9f9", label: "—" };
      const def = p.defects.length
        ? `<span style="color:#991b1b;font-weight:700">⛔ ${p.defects.map(d => d.code).join(",")}</span>`
        : '<span style="color:#16a34a">✓ clear</span>';
      return `<tr style="border-top:1px solid var(--bdr)">
        <td style="padding:6px 9px;font-weight:bold"><span style="cursor:pointer;color:var(--gm)" onclick="selAnimal('${p.cow.id}')">${dpEscape(p.cow.name)}</span></td>
        <td style="padding:6px 9px">${p.sire ? `<span style="cursor:pointer;color:var(--gm)" onclick="selAnimal('${p.sire.id}')">${dpEscape(p.sire.name)}</span>` : '<span style="color:#c0392b">No suitable sire</span>'}</td>
        <td style="text-align:center;padding:6px 9px">${p.F != null ? fBadge(p.F) : "—"}</td>
        <td style="text-align:center;padding:6px 9px;font-size:11px">${def}</td>
      </tr>`;
    })
    .join("");
  host.innerHTML = `
    <div style="display:flex;gap:10px;flex-wrap:wrap;margin-bottom:12px">
      <div class="sc"><div class="sv">${matched.length}/${plan.length}</div><div class="sl">Cows matched</div></div>
      <div class="sc"><div class="sv" style="color:${avgF != null && avgF > 6.25 ? "#c0392b" : "#16a34a"}">${avgF != null ? avgF.toFixed(2) + "%" : "—"}</div><div class="sl">Avg planned inbreeding</div></div>
    </div>
    <div style="overflow-x:auto">
    <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
      <thead><tr style="background:var(--gp)"><th style="text-align:left;padding:7px 9px">Cow</th><th style="text-align:left;padding:7px 9px">Recommended sire</th><th style="padding:7px 9px">Expected F</th><th style="padding:7px 9px">Defects</th></tr></thead>
      <tbody>${rows || '<tr><td colspan="4" style="padding:12px;text-align:center;color:var(--tl)">No cows/sires available.</td></tr>'}</tbody>
    </table></div>`;
}

/* ── Selection index ──────────────────────────────────────────────── */
let siWeights = { milk: 30, fat: 25, prot: 30, scs: 15 };

function selectionIndex(weights) {
  weights = weights || siWeights;
  const list = animals
    .filter(a => a.milk)
    .map(a => {
      const c = calc(a.milk, a.fat, a.prot);
      return { a, milk: +a.milk || 0, fatKg: c.fatKg || 0, protKg: c.protKg || 0, scs: a.scs != null && a.scs !== "" ? +a.scs : null };
    });
  if (!list.length) return [];
  const mean = arr => arr.reduce((s, v) => s + v, 0) / arr.length;
  const sd = (arr, m) => Math.sqrt(arr.reduce((s, x) => s + (x - m) ** 2, 0) / arr.length) || 1;
  const st = {};
  ["milk", "fatKg", "protKg"].forEach(k => {
    const vals = list.map(x => x[k]);
    const m = mean(vals);
    st[k] = { m, s: sd(vals, m) };
  });
  const scsVals = list.filter(x => x.scs != null).map(x => x.scs);
  const scsM = scsVals.length ? mean(scsVals) : null;
  const scsS = scsVals.length ? sd(scsVals, scsM) : 1;
  const W = { milk: weights.milk / 100, fat: weights.fat / 100, prot: weights.prot / 100, scs: weights.scs / 100 };
  list.forEach(x => {
    const zMilk = (x.milk - st.milk.m) / st.milk.s;
    const zFat = (x.fatKg - st.fatKg.m) / st.fatKg.s;
    const zProt = (x.protKg - st.protKg.m) / st.protKg.s;
    const zScs = x.scs != null && scsM != null ? -((x.scs - scsM) / scsS) : 0;
    const z = W.milk * zMilk + W.fat * zFat + W.prot * zProt + W.scs * zScs;
    x.z = z;
    x.index = Math.round(100 + z * 15); // breeding-value-style scale (mean≈100)
  });
  return list.sort((a, b) => b.z - a.z);
}

function renderSelIndex() {
  const el = document.getElementById("pg-selindex");
  if (!el) return;
  const ranked = selectionIndex(siWeights);
  const wInput = (k, label) =>
    `<label class="dp-ctrl"><span>${label} (${siWeights[k]}%)</span>
      <input type="range" min="0" max="100" value="${siWeights[k]}" oninput="siSetWeight('${k}',this.value)" style="width:130px">
    </label>`;
  const rows = ranked
    .slice(0, 50)
    .map((x, i) => {
      const medal = i === 0 ? "🥇" : i === 1 ? "🥈" : i === 2 ? "🥉" : i + 1;
      return `<tr style="border-top:1px solid var(--bdr);background:${i % 2 ? "#fafafa" : "#fff"}">
        <td style="padding:6px 9px;text-align:center;font-weight:bold">${medal}</td>
        <td style="padding:6px 9px;font-weight:bold"><span style="cursor:pointer;color:var(--gm)" onclick="selAnimal('${x.a.id}')">${dpEscape(x.a.name)}</span><div style="font-size:9px;color:var(--tl)">${dpEscape(x.a.regId || "—")}</div></td>
        <td style="text-align:right;padding:6px 9px">${x.milk.toLocaleString()}</td>
        <td style="text-align:right;padding:6px 9px">${x.fatKg.toFixed(1)}</td>
        <td style="text-align:right;padding:6px 9px">${x.protKg.toFixed(1)}</td>
        <td style="text-align:right;padding:6px 9px">${x.scs != null ? x.scs : "—"}</td>
        <td style="text-align:right;padding:6px 9px;font-weight:800;color:${x.index >= 100 ? "#16a34a" : "#b45309"}">${x.index}</td>
      </tr>`;
    })
    .join("");
  el.innerHTML = `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#7c3aed,#a855f7)"><h2 style="color:#fff">⭐ Selection Index</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">Rank animals by weighted genetic merit</span></div>
    <div class="cb">
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);margin-bottom:12px;line-height:1.6">
        A relative merit score (herd average ≈ <b>100</b>) from recorded production. Adjust the trait weights to match your breeding goal.
      </div>
      <div style="display:flex;gap:16px;flex-wrap:wrap;margin-bottom:14px;align-items:center">
        ${wInput("milk", "Milk")} ${wInput("fat", "Fat")} ${wInput("prot", "Protein")} ${wInput("scs", "Cell count (SCS)")}
        <button class="btn bd bsm" onclick="siReset()">↺ Reset weights</button>
      </div>
      ${
        ranked.length
          ? `<div style="overflow-x:auto"><table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:clamp(10px,1.1vw,12px)">
              <thead><tr style="background:var(--gp)">
                <th style="padding:6px 9px">Rank</th><th style="text-align:left;padding:6px 9px">Animal</th>
                <th style="text-align:right;padding:6px 9px">Milk kg</th><th style="text-align:right;padding:6px 9px">Fat kg</th>
                <th style="text-align:right;padding:6px 9px">Prot kg</th><th style="text-align:right;padding:6px 9px">SCS</th>
                <th style="text-align:right;padding:6px 9px">Index</th></tr></thead>
              <tbody>${rows}</tbody></table></div>`
          : `<div class="empty">No animals with recorded production yet. Add milk/fat/protein to animals to rank them.</div>`
      }
    </div>
  </div>`;
}
function siSetWeight(k, v) {
  siWeights[k] = parseInt(v, 10) || 0;
  renderSelIndex();
}
function siReset() {
  siWeights = { milk: 30, fat: 25, prot: 30, scs: 15 };
  renderSelIndex();
}

/* ════════════════════════════════════════════════════════════════
   AUTOMATIC TAG NUMBERING
   When an animal is registered (given a pedigree) it gets an official Tag
   Number built from its REGION code + BREED code + a running SERIAL, e.g.
   Eastern region (code 1) + Montbéliarde (MB) + serial 1  →  "1MB0000001".
   Serials run independently per region+breed up to the millions.
   Everything (region codes, provinces, breed codes, padding) is adjustable
   in Settings → Tag Numbers, stored in localStorage "lp_tag_config".
   ════════════════════════════════════════════════════════════════ */

const TAG_CFG_DEFAULT = {
  enabled: true,
  padding: 7, // serial digits → up to 9,999,999 per region+breed
  start: 1,
  breeds: { Holstein: "HO", Montbeliard: "MB" },
  regions: [
    { code: "1", name: "Eastern", provinces: ["Nangarhar", "Laghman", "Kunar", "Nuristan"] },
    { code: "2", name: "Capital", provinces: ["Kabul", "Parwan", "Kapisa", "Panjshir", "Wardak", "Logar"] },
    { code: "3", name: "Northern", provinces: ["Balkh", "Jawzjan", "Sar-e Pol", "Samangan", "Faryab"] },
    { code: "4", name: "North-Eastern", provinces: ["Kunduz", "Takhar", "Baghlan", "Badakhshan"] },
    { code: "5", name: "Western", provinces: ["Herat", "Badghis", "Ghor", "Farah"] },
    { code: "6", name: "Southern", provinces: ["Kandahar", "Helmand", "Zabul", "Uruzgan", "Nimroz"] },
    { code: "7", name: "Central & South-East", provinces: ["Ghazni", "Paktia", "Paktika", "Khost", "Bamyan", "Daikundi"] },
  ],
};

function getTagConfig() {
  try {
    const o = JSON.parse(localStorage.getItem("lp_tag_config") || "null");
    if (o && typeof o === "object") return Object.assign({}, TAG_CFG_DEFAULT, o);
  } catch (e) {}
  return JSON.parse(JSON.stringify(TAG_CFG_DEFAULT));
}
function saveTagConfig(cfg) {
  try {
    localStorage.setItem("lp_tag_config", JSON.stringify(cfg));
  } catch (e) {}
  // Mirror to the server settings store when a backend is present.
  if (window.LpDB && window.LpDB.active) window.LpDB.settingSave("tag_config", cfg).catch(() => {});
  else if (window.LpSync && window.LpSync.hasBackend)
    fetch("api.php?action=save&store=settings_tag_config", {
      method: "POST", credentials: "same-origin", headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ data: cfg }),
    }).catch(() => {});
}

const _norm = s => (s || "").trim().toLowerCase();

/* Region code for a province (from the config); "" if unmapped. */
function regionCodeForProvince(province, cfg) {
  cfg = cfg || getTagConfig();
  const p = _norm(province);
  if (!p) return "";
  const r = (cfg.regions || []).find(rg => (rg.provinces || []).some(x => _norm(x) === p));
  return r ? String(r.code) : "";
}
/* Breed code from the config; "" if the breed isn't configured. */
function breedCodeFor(breed, cfg) {
  cfg = cfg || getTagConfig();
  return (cfg.breeds || {})[breed] || "";
}
/* Resolve {regionCode, breedCode, key} for an animal-like object. */
function tagPartsFor(a, cfg) {
  cfg = cfg || getTagConfig();
  const regionCode = regionCodeForProvince(a.province, cfg);
  const breedCode = breedCodeFor(a.breed, cfg);
  return { regionCode, breedCode, key: regionCode && breedCode ? regionCode + breedCode : "" };
}
function formatTag(regionCode, breedCode, serial, padding) {
  return regionCode + breedCode + String(serial).padStart(padding || 7, "0");
}

/* Next serial for a region+breed key — LOCAL counter (localStorage). */
function nextTagLocal(key, start) {
  let ctr = {};
  try { ctr = JSON.parse(localStorage.getItem("lp_tag_counters") || "{}"); } catch (e) {}
  const n = ctr[key] != null ? ctr[key] : (start || 1);
  ctr[key] = n + 1;
  try { localStorage.setItem("lp_tag_counters", JSON.stringify(ctr)); } catch (e) {}
  return n;
}

/* Assign a tag to a new animal's data object (local mode, synchronous).
   Returns the tag string or "" if it cannot be built. */
function assignTagLocal(data, cfg) {
  cfg = cfg || getTagConfig();
  if (!cfg.enabled) return "";
  const parts = tagPartsFor(data, cfg);
  if (!parts.key) return "";
  const n = nextTagLocal(parts.key, cfg.start);
  return formatTag(parts.regionCode, parts.breedCode, n, cfg.padding);
}

/* A human note explaining why a tag could not be auto-assigned. */
function tagBlockedReason(data, cfg) {
  cfg = cfg || getTagConfig();
  if (!cfg.enabled) return "Auto tag numbering is turned off.";
  const parts = tagPartsFor(data, cfg);
  if (!parts.regionCode) return "Province “" + (data.province || "—") + "” is not assigned to any region.";
  if (!parts.breedCode) return "Breed “" + (data.breed || "—") + "” has no tag code set.";
  return "";
}

/* ── Tag-numbering settings editor (Settings → Tag Numbers) ── */
let _tagEdit = null;

function renderTagConfig() {
  const el = document.getElementById("pg-tagcfg");
  if (!el) return;
  if (!_tagEdit) _tagEdit = getTagConfig();
  const cfg = _tagEdit;

  const breedRows = Object.keys(cfg.breeds || {})
    .map(
      (b, i) => `<div style="display:grid;grid-template-columns:1fr 90px 30px;gap:8px;align-items:center;margin-bottom:6px">
        <input id="tc_breed_name_${i}" value="${dpEscape(b)}" placeholder="Breed" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
        <input id="tc_breed_code_${i}" value="${dpEscape(cfg.breeds[b])}" placeholder="Code" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px;text-transform:uppercase">
        <button class="ib" onclick="tcRemoveBreed(${i})" title="Remove" style="color:#c0392b">🗑</button>
      </div>`
    )
    .join("");

  const regionRows = (cfg.regions || [])
    .map(
      (r, i) => `<div style="display:grid;grid-template-columns:70px 150px 1fr 30px;gap:8px;align-items:center;margin-bottom:6px">
        <input id="tc_reg_code_${i}" value="${dpEscape(r.code)}" placeholder="Code" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
        <input id="tc_reg_name_${i}" value="${dpEscape(r.name)}" placeholder="Region name" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
        <input id="tc_reg_prov_${i}" value="${dpEscape((r.provinces || []).join(", "))}" placeholder="Provinces (comma separated)" style="font-size:12px;padding:5px 7px;border:1px solid var(--bdr);border-radius:6px">
        <button class="ib" onclick="tcRemoveRegion(${i})" title="Remove" style="color:#c0392b">🗑</button>
      </div>`
    )
    .join("");

  // counters (next serial per region+breed)
  let counters = {};
  try { counters = JSON.parse(localStorage.getItem("lp_tag_counters") || "{}"); } catch (e) {}
  const ctrRows = Object.keys(counters).length
    ? Object.keys(counters).sort().map(k => `<tr><td style="padding:4px 9px;font-weight:700">${k}</td><td style="padding:4px 9px;text-align:right">${(counters[k]).toLocaleString()}</td></tr>`).join("")
    : '<tr><td colspan="2" style="padding:8px 9px;color:var(--tl)">No tags issued yet.</td></tr>';

  // a live example using the first region + first breed
  const exReg = (cfg.regions && cfg.regions[0]) ? cfg.regions[0].code : "1";
  const exBreed = Object.values(cfg.breeds || {})[0] || "MB";
  const example = formatTag(exReg, exBreed, cfg.start || 1, cfg.padding || 7);

  el.innerHTML = `
  <div class="card">
    <div class="ch" style="background:linear-gradient(135deg,#7c2d12,#b45309)"><h2 style="color:#fff">🏷 Ear Tag Numbering</h2>
      <span style="font-family:sans-serif;font-size:10px;color:rgba(255,255,255,.8)">Auto ear tag = region + breed + serial</span></div>
    <div class="cb">
      <div style="font-family:sans-serif;font-size:12px;color:var(--tm);background:#fffbeb;border:1px solid #fde68a;border-radius:var(--r);padding:10px 13px;margin-bottom:14px;line-height:1.6">
        Every new pedigree gets an Ear Tag automatically (when left blank): <b>region code</b> + <b>breed code</b> + a running serial.
        Example: <b>${dpEscape(example)}</b> (then ${dpEscape(formatTag(exReg, exBreed, (cfg.start || 1) + 1, cfg.padding || 7))}, …). Serials count up independently for each region+breed.
      </div>

      <div style="display:flex;gap:16px;flex-wrap:wrap;align-items:center;margin-bottom:14px">
        <label class="dp-ctrl" style="flex-direction:row;align-items:center;gap:7px"><input type="checkbox" id="tc_enabled" ${cfg.enabled ? "checked" : ""}><span>Auto-assign tags on registration</span></label>
        <label class="dp-ctrl"><span>Serial digits</span><input id="tc_padding" type="number" min="1" max="12" value="${cfg.padding || 7}" style="width:90px;padding:6px 8px;border:1px solid var(--bdr);border-radius:6px"></label>
        <label class="dp-ctrl"><span>Start at</span><input id="tc_start" type="number" min="1" value="${cfg.start || 1}" style="width:110px;padding:6px 8px;border:1px solid var(--bdr);border-radius:6px"></label>
      </div>

      <div class="st">🐄 Breed codes</div>
      <div style="margin:8px 0">
        ${breedRows}
        <button class="btn bd bsm" onclick="tcAddBreed()">＋ Add breed</button>
      </div>

      <div class="st" style="margin-top:14px">📍 Regions &amp; provinces</div>
      <div style="margin:8px 0">
        <div style="display:grid;grid-template-columns:70px 150px 1fr 30px;gap:8px;font-family:sans-serif;font-size:10px;color:var(--tl);font-weight:700;margin-bottom:5px">
          <span>CODE</span><span>REGION</span><span>PROVINCES</span><span></span>
        </div>
        ${regionRows}
        <button class="btn bd bsm" onclick="tcAddRegion()">＋ Add region</button>
      </div>

      <div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:16px">
        <button class="btn bg" onclick="tcSave()">💾 Save tag settings</button>
        <button class="btn bd" onclick="tcReset()">↺ Reset to defaults</button>
        <span id="tc_msg" style="font-family:sans-serif;font-size:12px;color:var(--tl);align-self:center"></span>
      </div>
    </div>
  </div>

  <div class="card">
    <div class="ch"><h2>🔢 Issued serials</h2><span style="font-family:sans-serif;font-size:10px;color:var(--tl)">next number per region+breed</span></div>
    <div class="cb" style="padding:0;overflow-x:auto">
      <table style="width:100%;border-collapse:collapse;font-family:sans-serif;font-size:12px">
        <thead><tr style="background:var(--gp)"><th style="text-align:left;padding:6px 9px">Region+Breed</th><th style="text-align:right;padding:6px 9px">Next serial</th></tr></thead>
        <tbody>${ctrRows}</tbody>
      </table>
    </div>
  </div>`;
}

/* Read the form back into the working copy. */
function tcCapture() {
  if (!_tagEdit) _tagEdit = getTagConfig();
  _tagEdit.enabled = !!document.getElementById("tc_enabled")?.checked;
  _tagEdit.padding = Math.max(1, Math.min(12, parseInt(document.getElementById("tc_padding")?.value, 10) || 7));
  _tagEdit.start = Math.max(1, parseInt(document.getElementById("tc_start")?.value, 10) || 1);
  // breeds
  const breeds = {};
  Object.keys(_tagEdit.breeds || {}).forEach((_, i) => {
    const name = (document.getElementById("tc_breed_name_" + i)?.value || "").trim();
    const code = (document.getElementById("tc_breed_code_" + i)?.value || "").trim().toUpperCase();
    if (name && code) breeds[name] = code;
  });
  _tagEdit.breeds = breeds;
  // regions
  const regions = [];
  (_tagEdit.regions || []).forEach((_, i) => {
    const code = (document.getElementById("tc_reg_code_" + i)?.value || "").trim();
    const name = (document.getElementById("tc_reg_name_" + i)?.value || "").trim();
    const prov = (document.getElementById("tc_reg_prov_" + i)?.value || "")
      .split(",").map(s => s.trim()).filter(Boolean);
    if (code) regions.push({ code, name, provinces: prov });
  });
  _tagEdit.regions = regions;
  return _tagEdit;
}
function tcAddBreed() { tcCapture(); _tagEdit.breeds["New breed"] = ""; renderTagConfig(); }
function tcRemoveBreed(i) { tcCapture(); const keys = Object.keys(_tagEdit.breeds); const k = keys[i]; delete _tagEdit.breeds[k]; renderTagConfig(); }
function tcAddRegion() { tcCapture(); _tagEdit.regions.push({ code: "", name: "", provinces: [] }); renderTagConfig(); }
function tcRemoveRegion(i) { tcCapture(); _tagEdit.regions.splice(i, 1); renderTagConfig(); }
function tcReset() {
  if (!confirm("Reset tag-numbering settings to defaults? (issued serials are kept)")) return;
  try { localStorage.removeItem("lp_tag_config"); } catch (e) {}
  _tagEdit = getTagConfig();
  renderTagConfig();
  showToast("Tag settings reset ✓");
}
function tcSave() {
  tcCapture();
  saveTagConfig(_tagEdit);
  const m = document.getElementById("tc_msg");
  if (m) { m.textContent = "✅ Saved"; setTimeout(() => (m.textContent = ""), 2000); }
  showToast("Tag settings saved ✓");
}
