// Family-facing pages, rendered when URL hash starts with #familia
// Accessible via window.FamiliaRoutes.render({hash}) -> returns React node or null

const { useState: useStateF, useEffect: useEffectF } = React;

// BookCover, shows real cover from REAL_COVERS if available & loaded; falls back to
// an abstract cover using book.palette + title. Google Books sometimes returns a
// 128px placeholder "no image available", we detect that and treat as failure.
function BookCover({ book, size = "sm" }) {
  const REAL = window.REAL_COVERS || {};
  const LOCAL = window.LOCAL_COVERS || {};
  const ISBN = window.ISBN_COVERS || {};
  const isbn = (book && book.isbn ? String(book.isbn) : "").trim();
  const realUrl = (isbn && ISBN[isbn]) || LOCAL[book.id] || REAL[book.id] || (book && book.imageUrl) || null;
  const [failed, setFailed] = React.useState(false);
  const dims = size === "sm" ? { w: 60, h: 85, ft: 8.5, pad: 6 }
            : size === "md" ? { w: 80, h: 115, ft: 10, pad: 8 }
            : size === "lg" ? { w: 260, h: 380, ft: 20, pad: 18 }
            : { w: 110, h: 160, ft: 12, pad: 10 };
  const palette = book.palette || ["#2c4ffc", "#c7de40", "#fff"];

  if (realUrl && !failed) {
    return (
      <img
        src={realUrl}
        alt={book.title}
        onLoad={(e) => {
          // Google Books returns a 128×164 "no cover" placeholder, detect & fallback
          if (e.target.naturalWidth <= 128 && e.target.naturalHeight <= 200) setFailed(true);
        }}
        onError={() => setFailed(true)}
        style={{
          width: dims.w, height: dims.h,
          objectFit: "cover",
          borderRadius: 4,
          boxShadow: "0 4px 10px rgba(0,0,0,0.15)",
          flexShrink: 0,
          background: "#f0f0f0",
          display: "block",
        }}
      />
    );
  }

  // Abstract fallback, palette-based
  const [c0, c1, c2] = palette;
  const shortTitle = (book.title || "").length > 32 ? (book.title || "").slice(0, 30) + "…" : (book.title || "");
  return (
    <div style={{
      width: dims.w, height: dims.h,
      borderRadius: 4,
      background: `linear-gradient(135deg, ${c0}, ${c1 || c0})`,
      boxShadow: "0 4px 10px rgba(0,0,0,0.15)",
      flexShrink: 0,
      display: "flex",
      flexDirection: "column",
      justifyContent: "center",
      padding: dims.pad,
      overflow: "hidden",
      position: "relative",
    }}>
      <div style={{
        fontSize: dims.ft,
        fontWeight: 700,
        color: c2 || "#fff",
        lineHeight: 1.15,
        textShadow: "0 1px 2px rgba(0,0,0,0.2)",
      }}>{shortTitle}</div>
    </div>
  );
}
window.BookCover = BookCover;

function slugifyF(s) {
  return (s || "").toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "")
    .replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
}

function moneyF(n) { return "R$ " + (Number(n) || 0).toFixed(2).replace(".", ","); }

// Pull the live state from the app (data + CATALOG are injected)
function resolveBooksForSeries(data, seriesIdx) {
  const CATALOG = window.CATALOG || [];
  const activeSeries = window.resolveSeriesList ? window.resolveSeriesList(data) : (window.SERIES_LIST || []);
  // O catálogo estático (CATALOG) só conhece nomes canônicos de série ("6º
  // ano" etc) — usa o canônico correspondente à série ativa pra completar a
  // lista quando falta livro pinado, não o rótulo customizado pela escola.
  const canonicalSeriesName = window.inferCanonicalSeriesName
    ? window.inferCanonicalSeriesName(activeSeries[seriesIdx])
    : activeSeries[seriesIdx];
  const pinned = data.pinned || {};
  const pinnedIds = pinned[seriesIdx] || [];
  const pinnedBooks = pinnedIds.map(id => CATALOG.find(b => b.id === id)).filter(Boolean);

  const hasEN = data.scolexEN !== "none";
  const needPT = data.qtyPerYearPT ?? (data.qtyPerYear ?? 3);
  const needEN = hasEN ? (data.qtyPerYearEN ?? 2) : 0;
  const havePT = pinnedBooks.filter(b => b.lang === "pt").length;
  const haveEN = pinnedBooks.filter(b => b.lang === "en").length;

  // Priorize livros com capa real (REAL_COVERS) na frente, afeta pinned e extras
  const REAL = window.REAL_COVERS || {};
  const LOCAL = window.LOCAL_COVERS || {};
  const hasCover = (id) => !!(LOCAL[id] || REAL[id]);
  const sortByCover = (a, b) => (hasCover(b.id) - hasCover(a.id));
  const pinnedSorted = [...pinnedBooks].sort(sortByCover);
  const extraPT = CATALOG.filter(b => b.series === canonicalSeriesName && b.lang === "pt" && !pinnedIds.includes(b.id)).sort(sortByCover).slice(0, Math.max(0, needPT - havePT));
  const extraEN = hasEN ? CATALOG.filter(b => b.series === canonicalSeriesName && b.lang === "en" && !pinnedIds.includes(b.id)).sort(sortByCover).slice(0, Math.max(0, needEN - haveEN)) : [];

  return [...pinnedSorted, ...extraPT, ...extraEN];
}

// ============= SHARED FRAME =============
function FamilyFrame({ data, children, showBack, onBack }) {
  // Mesma prioridade do LandingBrandingCard (steps-final.jsx): logo/accent
  // persistidos em account.school primeiro, data.school* como fallback.
  // Antes essa página só lia data.schoolLogoUrl, que só existe na sessão em
  // que o upload aconteceu — reabrindo o link (ou logando de novo) sem
  // re-upar a logo, ela sumia mesmo já salva no backend da escola (bug
  // reportado pela Joana, reunião 26/08/2026: "a logo já tá aqui, não sei
  // por que não apareceu lá").
  const { account } = useApp();
  const accent = (account && account.school && account.school.accent) || data.schoolAccent || "#2f97a1";
  const logoUrl = (account && account.school && account.school.logoUrl) || data.schoolLogoUrl;
  const schoolName = data.schoolName || "Sua escola";
  return (
    <div style={{
      minHeight: "100vh",
      background: "#fafbf9",
      fontFamily: "Poppins, sans-serif",
      color: "#111",
    }}>
      {/* Top school bar */}
      <div style={{
        background: "#fff",
        borderBottom: `3px solid ${accent}`,
        padding: "18px 48px",
        display: "flex", alignItems: "center", gap: 20, justifyContent: "space-between",
        position: "sticky", top: 0, zIndex: 10,
      }} className="no-print-sticky">
        <div style={{ display: "flex", alignItems: "center", gap: 18 }}>
          {logoUrl ? (
            <img src={logoUrl} alt={schoolName} style={{ height: 48, maxWidth: 160, objectFit: "contain" }}/>
          ) : (
            <div style={{
              width: 48, height: 48, borderRadius: 10,
              background: accent, color: "#fff",
              display: "flex", alignItems: "center", justifyContent: "center",
              fontWeight: 800, fontSize: 20, letterSpacing: 1,
            }}>{(schoolName.match(/\b\w/g) || []).slice(0, 2).join("").toUpperCase()}</div>
          )}
          <div>
            <div style={{ fontSize: 18, fontWeight: 700, color: "#111", lineHeight: 1.1 }}>{schoolName}</div>
            <div style={{ fontSize: 12, color: "#666", marginTop: 3 }}>Lista de literatura · 2026</div>
          </div>
        </div>

        {showBack && (
          <button onClick={onBack} style={{
            padding: "10px 18px", borderRadius: 10,
            background: "transparent", border: `1.5px solid ${accent}`,
            color: accent, fontWeight: 600, fontSize: 14, whiteSpace: "nowrap",
            display: "flex", alignItems: "center", gap: 8, cursor: "pointer",
          }}>
            ← Voltar para todas as séries
          </button>
        )}
      </div>

      {children}

      {/* Powered by */}
      <div className="no-print" style={{
        padding: "28px 48px 48px", textAlign: "center",
        color: "#999", fontSize: 12,
      }}>
        Powered by <strong style={{ color: "#2f97a1" }}>Scolist</strong> · uma lista feita com curadoria da escola
      </div>
    </div>
  );
}

// Export Excel with 3 sheets: Literatura · Didáticos · Papelaria — for coordinators
function exportCoordinatorCSV(data) {
  // Séries de verdade da escola (Passo 1), não a lista fixa de 12.
  const activeSeries = window.resolveSeriesList ? window.resolveSeriesList(data) : (window.SERIES_LIST || []);
  const DIDATICOS_CAT = window.DIDATICOS || [];
  const PAPELARIA_CAT = window.PAPELARIA_CATALOG || [];
  const PAPELARIA_PARTNERS = window.PAPELARIA_PARTNERS || {};
  const fname = `lista-completa-${slugifyF(data.schoolName || "escola")}-2026`;

  // Fallback: se XLSX não tiver carregado, exporta CSV só de literatura como antes
  if (!window.XLSX) {
    const rows = [["Série", "Título", "Autor", "Editora", "Idioma", "Nível", "Páginas", "Preço"]];
    activeSeries.forEach((s, i) => {
      const bks = resolveBooksForSeries(data, i);
      bks.forEach(b => {
        rows.push([s, b.title || "", b.author || "", b.editora || "",
          b.lang === "pt" ? "Português" : "Inglês",
          b.level || "", b.pages || "",
          (b.price != null ? Number(b.price).toFixed(2) : "")]);
      });
    });
    const csv = rows.map(r => r.map(c => {
      const s = String(c).replace(/"/g, '""');
      return /[",;\n]/.test(s) ? `"${s}"` : s;
    }).join(";")).join("\n");
    const blob = new Blob(["\uFEFF" + csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = `${fname}.csv`;
    document.body.appendChild(a); a.click();
    setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 500);
    return;
  }

  const XLSX = window.XLSX;
  const wb = XLSX.utils.book_new();

  // --- Aba 1: Literatura ---
  const { label: periodLabel, abbrev: periodAbbrev } = window.periodInfo ? window.periodInfo(data) : { label: "bimestre", abbrev: "bim" };
  const periodHeader = periodLabel.charAt(0).toUpperCase() + periodLabel.slice(1);
  const litRows = [["Série", "Título", "Autor", "Editora", "Idioma", "Nível", "Páginas", periodHeader, "Preço"]];
  const bimestres = data.bimestres || {};
  activeSeries.forEach((s, i) => {
    const bks = resolveBooksForSeries(data, i);
    const seriesBim = bimestres[i] || {};
    bks.forEach(b => {
      const bn = seriesBim[b.id] ? `${seriesBim[b.id]}º ${periodAbbrev}` : "";
      litRows.push([
        s, b.title || "", b.author || "", b.editora || "",
        b.lang === "pt" ? "Português" : (b.lang === "en" ? "Inglês" : (b.lang || "")),
        b.level || "", b.pages || "", bn,
        (b.price != null ? Number(b.price) : ""),
      ]);
    });
  });
  if (litRows.length === 1) litRows.push(["—", "(sem livros selecionados)", "", "", "", "", "", "", ""]);
  const wsLit = XLSX.utils.aoa_to_sheet(litRows);
  for (let R = 2; R <= litRows.length; R++) {
    const c = wsLit["I" + R]; if (c && typeof c.v === "number") c.z = '"R$" #,##0.00';
  }
  wsLit["!cols"] = [{wch:14},{wch:38},{wch:22},{wch:18},{wch:11},{wch:8},{wch:9},{wch:11},{wch:12}];
  XLSX.utils.book_append_sheet(wb, wsLit, "Literatura");

  // --- Aba 2: Didáticos ---
  const didRows = [["Série", "Título", "Editora", "Disciplina", "Idioma", "Formato", "Tipo", "Preço"]];
  const didaticosAdopted = data.didaticosAdopted || {};
  activeSeries.forEach((s, i) => {
    // priceBySeries no catálogo estático usa o nome CANÔNICO da série, não o
    // rótulo customizado pela escola (ver inferCanonicalSeriesName, shared.jsx).
    const canonicalName = window.inferCanonicalSeriesName ? window.inferCanonicalSeriesName(s) : s;
    const list = didaticosAdopted[i] || [];
    list.forEach(a => {
      const it = DIDATICOS_CAT.find(d => d.id === a.itemId);
      if (!it) return;
      const price = it.priceBySeries?.[canonicalName] || 0;
      didRows.push([
        s, it.title || "", it.publisher || it.editora || "",
        it.subject || "", (it.lang || "").toUpperCase(),
        it.format || "", it.kind || "",
        price || "",
      ]);
    });
  });
  if (didRows.length === 1) didRows.push(["—", "(nenhum didático adotado)", "", "", "", "", "", ""]);
  const wsDid = XLSX.utils.aoa_to_sheet(didRows);
  for (let R = 2; R <= didRows.length; R++) {
    const c = wsDid["H" + R]; if (c && typeof c.v === "number") c.z = '"R$" #,##0.00';
  }
  wsDid["!cols"] = [{wch:14},{wch:36},{wch:22},{wch:14},{wch:7},{wch:11},{wch:10},{wch:12}];
  XLSX.utils.book_append_sheet(wb, wsDid, "Didáticos");

  // --- Aba 3: Papelaria ---
  const papRows = [["Série", "Item", "Marca/Parceiro", "Quantidade", "Preço unitário", "Total"]];
  const papelariaCart = data.papelaria || {};
  activeSeries.forEach((s, i) => {
    const list = papelariaCart[i] || [];
    list.forEach(c => {
      const it = PAPELARIA_CAT.find(x => x.id === c.itemId);
      if (!it) return;
      const br = it.brands?.[c.brandIdx];
      const partner = br ? PAPELARIA_PARTNERS[br.partnerId] : null;
      const unit = br ? br.price : 0;
      const qty = c.qty || 1;
      papRows.push([
        s, it.genericName || it.name || "",
        partner?.name || br?.partnerId || "",
        qty, unit, unit * qty,
      ]);
    });
    // Materiais customizados (escola própria)
    (data.customMaterials || []).forEach(m => {
      if (m.seriesApplied && !m.seriesApplied.includes(i)) return;
      papRows.push([s, m.name || "(material da escola)", m.brand || "—", m.qty || 1, m.price || "", (m.price || 0) * (m.qty || 1)]);
    });
  });
  if (papRows.length === 1) papRows.push(["—", "(nenhum item de papelaria)", "", "", "", ""]);
  const wsPap = XLSX.utils.aoa_to_sheet(papRows);
  for (let R = 2; R <= papRows.length; R++) {
    ["E", "F"].forEach(col => {
      const cell = wsPap[col + R]; if (cell && typeof cell.v === "number") cell.z = '"R$" #,##0.00';
    });
  }
  wsPap["!cols"] = [{wch:14},{wch:36},{wch:22},{wch:11},{wch:14},{wch:14}];
  XLSX.utils.book_append_sheet(wb, wsPap, "Papelaria");

  XLSX.writeFile(wb, `${fname}.xlsx`);
}

// Exportação alternativa pedida pela Joana (item 20,
// TAREFAS-JOANA-2026-08-21.md): uma aba por turma (em vez de uma aba por
// categoria), colunas simples pra mandar direto pra compra/conferência.
// Papelaria e outros materiais entram com ISBN vazio (não têm).
function exportListaPorTurma(data) {
  // Séries de verdade da escola (Passo 1), não a lista fixa de 12.
  const activeSeries = window.resolveSeriesList ? window.resolveSeriesList(data) : (window.SERIES_LIST || []);
  const DIDATICOS_CAT = window.DIDATICOS || [];
  const PAPELARIA_CAT = window.PAPELARIA_CATALOG || [];
  const PAPELARIA_PARTNERS = window.PAPELARIA_PARTNERS || {};
  const fname = `lista-por-turma-${slugifyF(data.schoolName || "escola")}-2026`;
  const header = ["Série", "Categoria", "ISBN", "Título", "Preço", "Opcional"];

  const rowsForSeries = (s, i) => {
    const rows = [];
    // Só livros de literatura podem ser marcados opcionais (Passo 13,
    // Confirmação final) — didáticos, papelaria e outros materiais ficam
    // sempre em branco nessa coluna.
    const optionalIds = new Set((data.optionalBooks || {})[i] || []);

    resolveBooksForSeries(data, i).forEach((b) => {
      rows.push([s, "Literatura", b.isbn || "", b.title || "", b.price != null ? Number(b.price) : "", optionalIds.has(b.id) ? "Sim" : ""]);
    });

    ((data.didaticosAdopted || {})[i] || []).forEach((a) => {
      const it = DIDATICOS_CAT.find((d) => d.id === a.itemId);
      if (!it) return;
      // priceBySeries no catálogo estático usa o nome CANÔNICO da série, não
      // o rótulo customizado (ver inferCanonicalSeriesName, shared.jsx).
      const canonicalName = window.inferCanonicalSeriesName ? window.inferCanonicalSeriesName(s) : s;
      const price = it.priceBySeries?.[canonicalName] || 0;
      rows.push([s, "Didáticos", it.isbn || "", it.title || "", price || "", ""]);
    });

    ((data.papelaria || {})[i] || []).forEach((c) => {
      const it = PAPELARIA_CAT.find((x) => x.id === c.itemId);
      if (!it) return;
      const br = it.brands?.[c.brandIdx];
      const unit = br ? br.price : 0;
      const qty = c.qty || 1;
      const partner = br ? PAPELARIA_PARTNERS[br.partnerId] : null;
      const label = partner ? `${it.genericName} (${partner.name})` : (it.genericName || it.name || "");
      rows.push([s, "Papelaria", "", label, unit * qty, ""]);
    });

    (data.customMaterials || []).forEach((m) => {
      if (m.seriesApplied && !m.seriesApplied.includes(i)) return;
      rows.push([s, "Outros", "", m.name || "(material da escola)", (m.price || 0) * (m.qty || 1), ""]);
    });

    return rows;
  };

  // Fallback: se XLSX não tiver carregado, exporta tudo num CSV só (sem abas)
  if (!window.XLSX) {
    const rows = [header];
    activeSeries.forEach((s, i) => rows.push(...rowsForSeries(s, i)));
    const csv = rows.map(r => r.map(c => {
      const str = String(c).replace(/"/g, '""');
      return /[",;\n]/.test(str) ? `"${str}"` : str;
    }).join(";")).join("\n");
    const blob = new Blob(["﻿" + csv], { type: "text/csv;charset=utf-8" });
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url; a.download = `${fname}.csv`;
    document.body.appendChild(a); a.click();
    setTimeout(() => { document.body.removeChild(a); URL.revokeObjectURL(url); }, 500);
    return;
  }

  const XLSX = window.XLSX;
  const wb = XLSX.utils.book_new();

  // Nomes de aba do Excel são limitados a 31 caracteres, não podem conter
  // \ / ? * [ ] : , não podem ficar vazios, nem se repetir — qualquer um
  // desses casos faz o Excel considerar o arquivo corrompido ao abrir.
  // Séries têm nome livre (renomeável desde o ajuste de segmento manual),
  // então nada disso é garantido só pelo nome da série.
  const usedSheetNames = new Set();
  const safeSheetName = (raw, fallbackIdx) => {
    let name = String(raw || "").replace(/[/\\?*[\]:]/g, "").trim().slice(0, 31) || `Turma ${fallbackIdx + 1}`;
    let candidate = name;
    let suffix = 2;
    while (usedSheetNames.has(candidate)) {
      candidate = `${name.slice(0, 28)} (${suffix})`;
      suffix++;
    }
    usedSheetNames.add(candidate);
    return candidate;
  };

  activeSeries.forEach((s, i) => {
    const rows = [header, ...rowsForSeries(s, i)];
    if (rows.length === 1) rows.push(["—", "(sem itens)", "", "", "", ""]);
    const ws = XLSX.utils.aoa_to_sheet(rows);
    for (let R = 2; R <= rows.length; R++) {
      const c = ws["E" + R]; if (c && typeof c.v === "number") c.z = '"R$" #,##0.00';
    }
    ws["!cols"] = [{ wch: 10 }, { wch: 12 }, { wch: 16 }, { wch: 42 }, { wch: 12 }, { wch: 10 }];
    XLSX.utils.book_append_sheet(wb, ws, safeSheetName(s, i));
  });

  XLSX.writeFile(wb, `${fname}.xlsx`);
}

// ============= HUB (series da escola) =============
function HubFamilia({ data, go }) {
  // Séries de verdade da escola (Passo 1), não a lista fixa de 12.
  const activeSeries = window.resolveSeriesList ? window.resolveSeriesList(data) : (window.SERIES_LIST || []);
  const accent = data.schoolAccent || "#2f97a1";

  return (
    <FamilyFrame data={data}>
      {/* Hero */}
      <div style={{
        padding: "56px 48px 40px",
        background: `linear-gradient(180deg, ${accent}0d 0%, transparent 100%)`,
      }}>
        <div style={{ maxWidth: 1120, margin: "0 auto" }}>
          <div style={{ fontSize: 13, fontWeight: 700, color: accent, textTransform: "uppercase", letterSpacing: 1.5, marginBottom: 14 }}>
            Lista de Literatura 2026
          </div>
          <h1 style={{ fontSize: 40, fontWeight: 800, lineHeight: 1.15, margin: 0, maxWidth: 820, letterSpacing: -0.5 }}>
            Os livros que seu filho vai ler este ano
          </h1>
          <p style={{ fontSize: 17, color: "#555", marginTop: 14, maxWidth: 720, lineHeight: 1.55 }}>
            Selecione a série abaixo para ver os títulos escolhidos pela equipe pedagógica, ou baixe a lista em PDF.
          </p>
        </div>
      </div>

      {/* Series grid */}
      <div style={{ padding: "0 48px 40px", maxWidth: 1120, margin: "0 auto", width: "100%", boxSizing: "border-box" }}>
        <div style={{
          display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(280px, 1fr))", gap: 18,
        }}>
          {activeSeries.map((s, i) => {
            const bks = resolveBooksForSeries(data, i);
            const covers = bks.slice(0, 3);
            return (
              <button key={s} onClick={() => go(`familia/serie/${i}`)} style={{
                background: "#fff", border: "1px solid #e4ebe4",
                borderRadius: 18, padding: "22px 22px 20px", textAlign: "left",
                cursor: "pointer", transition: "all 0.18s",
                display: "flex", flexDirection: "column", gap: 16,
                minHeight: 240,
              }}
              onMouseEnter={e => { e.currentTarget.style.transform = "translateY(-4px)"; e.currentTarget.style.boxShadow = "0 14px 30px rgba(0,0,0,0.08)"; e.currentTarget.style.borderColor = accent; }}
              onMouseLeave={e => { e.currentTarget.style.transform = "none"; e.currentTarget.style.boxShadow = "none"; e.currentTarget.style.borderColor = "#e4ebe4"; }}
              >
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
                  <div>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "#999", textTransform: "uppercase", letterSpacing: 1 }}>Série</div>
                    <div style={{ fontSize: 24, fontWeight: 800, color: "#111", marginTop: 2 }}>{s}</div>
                  </div>
                  <div style={{
                    padding: "5px 10px", borderRadius: 999,
                    background: accent, color: "#fff",
                    fontSize: 11, fontWeight: 700,
                  }}>{bks.length} livro{bks.length !== 1 ? "s" : ""}</div>
                </div>

                {/* Cover fan, scaled down to fit narrow cards */}
                <div style={{ flex: 1, display: "flex", alignItems: "flex-end", justifyContent: "center", gap: 6, minHeight: 110, overflow: "hidden" }}>
                  {covers.length === 0 ? (
                    <div style={{ color: "#bbb", fontSize: 13, fontStyle: "italic" }}>Lista em curadoria</div>
                  ) : (
                    <div style={{ display: "flex", gap: 4, transform: "scale(0.72)", transformOrigin: "bottom center" }}>
                      {covers.map((b, k) => (
                        <div key={b.id} style={{ transform: `translateY(${k % 2 === 1 ? 4 : 0}px) rotate(${(k-1)*2}deg)`, flexShrink: 0 }}>
                          <window.BookCover book={b} size="sm" />
                        </div>
                      ))}
                    </div>
                  )}
                </div>

                <div style={{
                  display: "flex", justifyContent: "space-between", alignItems: "center",
                  borderTop: "1px solid #f0f0f0", paddingTop: 12, marginTop: 4,
                }}>
                  <span style={{ fontSize: 13, color: "#666" }}>Ver lista da série</span>
                  <span style={{ color: accent, fontSize: 16, fontWeight: 700 }}>→</span>
                </div>
              </button>
            );
          })}
        </div>
      </div>
    </FamilyFrame>
  );
}

// ============= SÉRIE (printable) =============
function SerieFamilia({ data, seriesIdx, go }) {
  // Séries de verdade da escola (Passo 1), não a lista fixa de 12.
  const activeSeries = window.resolveSeriesList ? window.resolveSeriesList(data) : (window.SERIES_LIST || []);
  const seriesName = activeSeries[seriesIdx] || ",";
  // priceBySeries no catálogo estático usa o nome CANÔNICO da série, não o
  // rótulo customizado pela escola (ver inferCanonicalSeriesName, shared.jsx).
  const canonicalSeriesName = window.inferCanonicalSeriesName ? window.inferCanonicalSeriesName(seriesName) : seriesName;
  const accent = data.schoolAccent || "#2f97a1";
  const books = resolveBooksForSeries(data, seriesIdx);
  const { label: periodLabel } = window.periodInfo ? window.periodInfo(data) : { label: "bimestre" };

  const totalPrice = books.reduce((s, b) => s + (Number(b.price) || 0), 0);
  const ptBooks = books.filter(b => b.lang === "pt");
  const enBooks = books.filter(b => b.lang === "en");

  // Didáticos adotados pra essa série
  const DIDATICOS = window.DIDATICOS || [];
  const didAdoptions = (data.didaticosAdopted || {})[seriesIdx] || [];
  const didItems = didAdoptions.map(a => {
    const d = DIDATICOS.find(x => x.id === a.itemId);
    if (!d) return null;
    return {
      ...d,
      price: d.priceBySeries?.[canonicalSeriesName] || 0,
    };
  }).filter(Boolean);
  const didTotal = didItems.reduce((s, d) => s + d.price, 0);

  // Papelaria montada pra essa série
  const PAPELARIA = window.PAPELARIA_CATALOG || [];
  const PARTNERS = window.PAPELARIA_PARTNERS || {};
  const papCart = (data.papelaria || {})[seriesIdx] || [];
  const papItems = papCart.map(c => {
    const it = PAPELARIA.find(x => x.id === c.itemId);
    if (!it) return null;
    const br = it.brands?.[c.brandIdx];
    if (!br) return null;
    const partner = PARTNERS[br.partnerId];
    return {
      genericName: it.genericName,
      brand: br.brand,
      partner: partner?.name || br.partnerId,
      price: br.price,
      qty: c.qty,
      total: br.price * c.qty,
      icon: it.icon,
    };
  }).filter(Boolean);
  const papTotal = papItems.reduce((s, x) => s + x.total, 0);

  const grandTotal = totalPrice + didTotal + papTotal;
  const itemCount = books.length + didItems.length + papItems.length;

  const schoolSlug = slugifyF(data.schoolName || "escola");

  const printPDF = () => window.print();

  return (
    <FamilyFrame data={data} showBack onBack={() => go("familia")}>
      <style>{`
        @media print {
          .no-print, .no-print-sticky { display: none !important; }
          body { background: #fff !important; }
          .print-page { padding: 24px !important; }
          .book-row { page-break-inside: avoid; break-inside: avoid; }
        }
      `}</style>

      <div className="print-page" style={{ padding: "32px 48px 48px", maxWidth: 980, margin: "0 auto", width: "100%", boxSizing: "border-box" }}>
        {/* Series header */}
        <div style={{
          padding: "28px 32px",
          background: `linear-gradient(135deg, ${accent} 0%, ${accent}dd 100%)`,
          color: "#fff", borderRadius: 16,
        }}>
          <div style={{ fontSize: 12, fontWeight: 700, textTransform: "uppercase", letterSpacing: 1.5, opacity: 0.9 }}>
            Lista 2026
          </div>
          <div style={{ fontSize: 32, fontWeight: 800, marginTop: 4, lineHeight: 1.1 }}>{seriesName}</div>
          <div style={{ fontSize: 14, marginTop: 6, opacity: 0.9 }}>
            {itemCount} {itemCount === 1 ? "item" : "itens"}
            {books.length > 0 && ` · ${books.length} livro${books.length !== 1 ? "s" : ""}`}
            {didItems.length > 0 && ` · ${didItems.length} didático${didItems.length !== 1 ? "s" : ""}`}
            {papItems.length > 0 && ` · ${papItems.length} item${papItems.length !== 1 ? "ns" : ""} de papelaria`}
          </div>
          {grandTotal > 0 && (
            <div style={{ marginTop: 14, paddingTop: 14, borderTop: "1px solid rgba(255,255,255,0.25)", display: "flex", justifyContent: "space-between", alignItems: "baseline", flexWrap: "wrap", gap: 12 }}>
              <span style={{ fontSize: 13, opacity: 0.92 }}>Total estimado · preço de capa</span>
              <span style={{ fontSize: 28, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>
                R$ {grandTotal.toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
              </span>
            </div>
          )}
        </div>

        {/* Action bar */}
        <div className="no-print" style={{ display: "flex", gap: 10, marginTop: 20, justifyContent: "flex-end", flexWrap: "wrap" }}>
          <button onClick={printPDF} style={{
            padding: "12px 22px", borderRadius: 10,
            background: accent, color: "#fff", fontWeight: 600, fontSize: 14,
            border: "none", cursor: "pointer",
            display: "flex", alignItems: "center", gap: 8, whiteSpace: "nowrap",
            boxShadow: "0 4px 12px rgba(47,151,161,0.25)",
          }}>
            ⬇ Baixar PDF
          </button>
        </div>

        {/* Section: Literatura */}
        {(books.length > 0 || (didItems.length === 0 && papItems.length === 0)) && (
          <SectionHeader accent={accent} icon="📚" label="Literatura" count={books.length} subtotal={totalPrice} />
        )}

        {/* Book list */}
        <div style={{ marginTop: 14, display: "flex", flexDirection: "column", gap: 18 }}>
          {books.length === 0 && didItems.length === 0 && papItems.length === 0 && (
            <div style={{ padding: 40, textAlign: "center", color: "#888", background: "#fff", borderRadius: 14, border: "1px dashed #ddd" }}>
              Lista desta série ainda em curadoria pela escola.
            </div>
          )}
          {books.map((b, i) => (
            <div key={b.id} className="book-row" style={{
              background: "#fff",
              border: "1px solid #e4ebe4", borderRadius: 14,
              padding: 20, display: "grid",
              gridTemplateColumns: "120px 1fr", gap: 22, alignItems: "center",
            }}>
              <div style={{ display: "flex", justifyContent: "center" }}>
                <window.BookCover book={b} size="sm" />
              </div>
              <div>
                <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 6, flexWrap: "wrap" }}>
                  <span style={{
                    fontSize: 10, fontWeight: 700, padding: "3px 8px", borderRadius: 4,
                    background: b.lang === "pt" ? accent : "#2c4ffc", color: "#fff",
                  }}>{b.lang === "pt" ? "Português" : "Inglês"}</span>
                  {b.level && <span style={{ fontSize: 10, fontWeight: 700, padding: "3px 8px", borderRadius: 4, background: "#f0fbfb", color: "#23747c", border: "1px solid #b7e8ea" }}>Nível {b.level}</span>}
                  {(() => {
                    const bim = ((data.bimestres || {})[seriesIdx] || {})[b.id];
                    if (!bim) return null;
                    return (
                      <span style={{
                        fontSize: 10, fontWeight: 700, padding: "3px 8px", borderRadius: 4,
                        background: "#fff7e6", color: "#a06a14", border: "1px solid #f0d28a",
                      }}>{`${bim}º ${periodLabel}`}</span>
                    );
                  })()}
                  {((data.optionalBooks || {})[seriesIdx] || []).includes(b.id) && (
                    <span style={{
                      fontSize: 10, fontWeight: 700, padding: "3px 8px", borderRadius: 4,
                      background: "#fff3e0", color: "#9a6416", border: "1px solid #e8b96a",
                    }}>Opcional</span>
                  )}
                  <span style={{ fontSize: 11, color: "#999" }}>#{i + 1}</span>
                </div>
                <div style={{ fontSize: 19, fontWeight: 700, color: "#111", lineHeight: 1.2 }}>{b.title}</div>
                <div style={{ fontSize: 14, color: "#555", marginTop: 4 }}>{b.author}</div>
                <div style={{ fontSize: 12, color: "#888", marginTop: 6 }}>
                  {b.editora}{b.pages ? ` · ${b.pages} páginas` : ""}
                </div>
                {b.summary && <div style={{ fontSize: 13, color: "#444", marginTop: 10, lineHeight: 1.5, maxWidth: 560 }}>{b.summary}</div>}
              </div>
            </div>
          ))}
        </div>

        {/* Section: Didáticos */}
        {didItems.length > 0 && (
          <>
            <SectionHeader accent={accent} icon="📘" label="Livros didáticos" count={didItems.length} subtotal={didTotal} />
            <div style={{ marginTop: 14, display: "flex", flexDirection: "column", gap: 12 }}>
              {didItems.map((d) => (
                <div key={d.id} className="book-row" style={{
                  background: "#fff", border: "1px solid #e4ebe4", borderRadius: 14,
                  padding: "16px 20px", display: "grid",
                  gridTemplateColumns: "1fr auto", gap: 16, alignItems: "center",
                }}>
                  <div>
                    <div style={{ display: "flex", gap: 8, alignItems: "center", marginBottom: 6, flexWrap: "wrap" }}>
                      <span style={{ fontSize: 10, fontWeight: 700, padding: "3px 8px", borderRadius: 4, background: "#eef0fe", color: "#2c4ffc", textTransform: "uppercase", letterSpacing: 0.5 }}>
                        {subjectLabel(d.subject)}
                      </span>
                      {d.format && (
                        <span style={{ fontSize: 10, fontWeight: 600, padding: "3px 8px", borderRadius: 4, background: "#f6f6f6", color: "#666" }}>
                          {d.format === "hibrido" ? "Físico + digital" : d.format === "digital" ? "Digital" : "Físico"}
                        </span>
                      )}
                    </div>
                    <div style={{ fontSize: 17, fontWeight: 700, color: "#111", lineHeight: 1.25 }}>{d.title}</div>
                    <div style={{ fontSize: 13, color: "#666", marginTop: 3 }}>{d.publisher}</div>
                  </div>
                  <div style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                    <div style={{ fontSize: 17, fontWeight: 800, color: "#111", fontVariantNumeric: "tabular-nums" }}>
                      R$ {d.price.toFixed(2).replace(".", ",")}
                    </div>
                    <div style={{ fontSize: 11, color: "#999", marginTop: 2 }}>preço de capa</div>
                  </div>
                </div>
              ))}
            </div>
          </>
        )}

        {/* Section: Papelaria */}
        {papItems.length > 0 && (
          <>
            <SectionHeader accent={accent} icon="✏️" label="Papelaria" count={papItems.length} subtotal={papTotal} />
            <div style={{ marginTop: 14, background: "#fff", border: "1px solid #e4ebe4", borderRadius: 14, overflow: "hidden" }}>
              <table style={{ width: "100%", borderCollapse: "collapse", fontSize: 14 }}>
                <thead>
                  <tr style={{ background: "#fafafa", borderBottom: "1px solid #eee" }}>
                    <th style={{ padding: "10px 16px", textAlign: "left", fontSize: 11, fontWeight: 700, color: "#888", textTransform: "uppercase", letterSpacing: 0.5 }}>Item</th>
                    <th style={{ padding: "10px 12px", textAlign: "center", fontSize: 11, fontWeight: 700, color: "#888", textTransform: "uppercase", letterSpacing: 0.5, width: 70 }}>Qtd</th>
                    <th style={{ padding: "10px 12px", textAlign: "right", fontSize: 11, fontWeight: 700, color: "#888", textTransform: "uppercase", letterSpacing: 0.5, width: 110 }}>Unitário</th>
                    <th style={{ padding: "10px 16px", textAlign: "right", fontSize: 11, fontWeight: 700, color: "#888", textTransform: "uppercase", letterSpacing: 0.5, width: 120 }}>Total</th>
                  </tr>
                </thead>
                <tbody>
                  {papItems.map((p, i) => (
                    <tr key={i} className="book-row" style={{ borderBottom: i < papItems.length - 1 ? "1px solid #f0f0f0" : "none" }}>
                      <td style={{ padding: "12px 16px" }}>
                        <div style={{ fontWeight: 700, color: "#111", fontSize: 14, lineHeight: 1.3 }}>
                          {p.icon && <span style={{ marginRight: 6 }}>{p.icon}</span>}
                          {p.genericName}
                        </div>
                        <div style={{ fontSize: 12, color: "#888", marginTop: 2 }}>{p.brand} · {p.partner}</div>
                      </td>
                      <td style={{ padding: "12px", textAlign: "center", fontVariantNumeric: "tabular-nums", fontWeight: 600 }}>{p.qty}</td>
                      <td style={{ padding: "12px", textAlign: "right", fontVariantNumeric: "tabular-nums", color: "#666" }}>R$ {p.price.toFixed(2).replace(".", ",")}</td>
                      <td style={{ padding: "12px 16px", textAlign: "right", fontVariantNumeric: "tabular-nums", fontWeight: 700, color: "#111" }}>R$ {p.total.toFixed(2).replace(".", ",")}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </>
        )}

        {/* Total geral */}
        {grandTotal > 0 && (
          <div style={{
            marginTop: 28, padding: "20px 24px",
            background: `${accent}10`, border: `2px solid ${accent}`, borderRadius: 14,
            display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 12,
          }}>
            <div>
              <div style={{ fontSize: 12, fontWeight: 700, color: accent, textTransform: "uppercase", letterSpacing: 1 }}>Total da lista 2026</div>
              <div style={{ fontSize: 12.5, color: "#666", marginTop: 4 }}>
                Compra na rematrícula no Scolados tem 10% off + 6× sem juros
              </div>
            </div>
            <div style={{ textAlign: "right" }}>
              <div style={{ fontSize: 32, fontWeight: 800, color: "#111", letterSpacing: -1, fontVariantNumeric: "tabular-nums" }}>
                R$ {grandTotal.toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
              </div>
              <div style={{ fontSize: 13, color: accent, fontWeight: 700, marginTop: 2, fontVariantNumeric: "tabular-nums" }}>
                ou R$ {(grandTotal * 0.9).toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} com 10% off
              </div>
            </div>
          </div>
        )}
      </div>
    </FamilyFrame>
  );
}

// ============= helpers =============
function SectionHeader({ accent, icon, label, count, subtotal }) {
  return (
    <div style={{
      marginTop: 32, marginBottom: 4,
      display: "flex", alignItems: "baseline", justifyContent: "space-between",
      borderBottom: `2px solid ${accent}`, paddingBottom: 10, gap: 14, flexWrap: "wrap",
    }}>
      <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
        <span style={{ fontSize: 22 }}>{icon}</span>
        <span style={{ fontSize: 18, fontWeight: 800, color: "#111", letterSpacing: -0.3 }}>{label}</span>
        <span style={{ fontSize: 13, color: "#999", fontWeight: 600 }}>· {count} {count === 1 ? "item" : "itens"}</span>
      </div>
      {subtotal > 0 && (
        <div style={{ fontSize: 15, fontWeight: 700, color: "#444", fontVariantNumeric: "tabular-nums" }}>
          R$ {subtotal.toLocaleString("pt-BR", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
        </div>
      )}
    </div>
  );
}
const SUBJECT_LABELS = {
  portugues: "Português", matematica: "Matemática", ciencias: "Ciências",
  historia: "História", geografia: "Geografia", ingles: "Inglês",
  arte: "Arte", filosofia: "Filosofia", sociologia: "Sociologia",
  fisica: "Física", quimica: "Química", biologia: "Biologia",
  espanhol: "Espanhol", frances: "Francês", alemao: "Alemão",
  sistema: "Sistema de ensino",
};
function subjectLabel(s) { return SUBJECT_LABELS[s] || s; }

// ============= ROUTER =============
// Returns a React element for a family route, or null if hash doesn't match.
function renderFamiliaRoute(hash, data, setHash) {
  const go = (path) => setHash(path);
  if (hash === "familia" || hash === "familia/") {
    return <HubFamilia data={data} go={go}/>;
  }
  const m = hash.match(/^familia\/serie\/(\d+)$/);
  if (m) {
    const idx = parseInt(m[1], 10);
    return <SerieFamilia data={data} seriesIdx={idx} go={go}/>;
  }
  return null;
}

Object.assign(window, { renderFamiliaRoute, exportCoordinatorCSV, exportListaPorTurma });
