/* global React */
const { useState, useEffect, useRef, useMemo, createContext, useContext } = React;

// Brand mark, "Scolex" with small ® superscript
const Scolex = ({ children = "Scolex" }) => (
  <React.Fragment>
    {children}<sup style={{ fontSize: "1.05em", verticalAlign: "super", marginLeft: 1, fontWeight: 600, opacity: 0.95, lineHeight: 0 }}>®</sup>
  </React.Fragment>
);

// Deriva o nome de exibição da escola: prioriza o dado real vindo do login
// (account.school.name); se a conta ainda não tiver escola associada, usa o
// domínio do e-mail do usuário como aproximação; sem nenhum dos dois, cai
// num rótulo genérico — nunca mais o nome de outra escola como dummy fixo
// (ver TAREFAS-JOANA-2026-08-20.md, item 1).
function resolveSchoolName(account) {
  const fromAccount = account && account.school && account.school.name;
  if (fromAccount) return fromAccount;

  const email = account && account.user && account.user.email;
  const domain = email && email.includes("@") ? email.split("@")[1] : "";
  const domainLabel = domain ? domain.split(".")[0] : "";
  if (domainLabel) {
    return domainLabel.replace(/[-_]+/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
  }

  return "Sua escola";
}
window.resolveSchoolName = resolveSchoolName;

// ============= SERIES =============
// Lista canônica/fallback — só usada (a) como ponto de partida quando a escola
// ainda não passou pelo Passo 1 (Calendário & séries) nesta sessão, e (b) como
// referência pra achar a faixa Scolex/preço de catálogo por NOME (ver
// inferCanonicalSeriesName/resolveSeriesList abaixo). NÃO é mais a lista usada
// pra exibir séries pro usuário — isso agora vem de resolveSeriesList(data).
const SERIES_LIST = ["1º ano","2º ano","3º ano","4º ano","5º ano","6º ano","7º ano","8º ano","9º ano","1º EM","2º EM","3º EM"];
window.SERIES_LIST = SERIES_LIST;

// Infere o nome canônico ("Nº ano"/"Nº EM") por trás de um nome de série que
// pode ter sido customizado pela escola no Passo 1 (ex: "6º Ano - Turma Nova"
// -> "6º ano"). Usado só pra localizar dado que depende do nome exato (faixa
// Scolex, catálogo estático de livros/didáticos por série) — nunca pra decidir
// o que mostrar pro usuário, que é sempre o nome de verdade cadastrado na
// Seção 1. Sem padrão reconhecível (Educação Infantil, nome 100% customizado
// tipo "Turma Azul") retorna null — quem chama trata como "sem dado
// automático pra essa série", que já cai no fluxo de busca manual existente.
function inferCanonicalSeriesName(name) {
  const normalized = String(name || "")
    .normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().trim();
  if (!normalized) return null;
  const matchAno = normalized.match(/(\d+)\s*(?:o|º)?\s*ano\b/);
  if (matchAno) {
    const n = Number(matchAno[1]);
    if (n >= 1 && n <= 9) return SERIES_LIST[n - 1];
  }
  const matchEm = normalized.match(/(\d+)\s*(?:o|º)?\s*em\b/);
  if (matchEm) {
    const n = Number(matchEm[1]);
    if (n >= 1 && n <= 3) return SERIES_LIST[8 + n];
  }
  const found = SERIES_LIST.find((canonical) => {
    const label = canonical.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
    return normalized === label || normalized.includes(label);
  });
  return found || null;
}
window.inferCanonicalSeriesName = inferCanonicalSeriesName;

// Índice canônico (0-11, mesma ordem de SERIES_LIST) de um nome de série
// qualquer — atalho pra quem precisa saber o "segmento" de uma série (Fund I
// é 0-4, Fund II é 5-8, Médio é 9-11) sem se importar com a posição real
// dessa série na lista dinâmica da escola. -1 quando não reconhece o padrão
// (Educação Infantil, nome 100% customizado).
function canonicalIndexOf(name) {
  const canonical = inferCanonicalSeriesName(name);
  return canonical ? SERIES_LIST.indexOf(canonical) : -1;
}
window.canonicalIndexOf = canonicalIndexOf;

// Lista de séries "de verdade" da escola: o que foi cadastrado no Passo 1
// (Calendário & séries, data.seriesNames), na ordem que a escola organizou
// (Infantil, Fund I, Fund II, Médio, extras). Cai pro fallback fixo de 12
// séries só se a escola ainda não passou pelo Passo 1 nesta sessão. Essa é a
// lista que TODA tela com abas/rótulos de série deve usar pra exibição e
// indexação (Recomendações, Didáticos, Papelaria, Revisão, Confirmação final,
// páginas da família) — sem isso, essas telas mostravam sempre "1º ano".."3º
// EM" fixo, ignorando o que a escola cadastrou de verdade (Educação Infantil
// sumia, séries renomeadas voltavam pro nome padrão).
function resolveSeriesList(data) {
  const names = data && Array.isArray(data.seriesNames) ? data.seriesNames : null;
  return (names && names.length) ? names : SERIES_LIST;
}
window.resolveSeriesList = resolveSeriesList;

// Períodos letivos usados pra marcar "quando" um livro vai ser trabalhado
// (Passo Recomendações e Revisão, data.bimestres) — depende do calendário
// escolhido no Passo 1 (data.calendar: "bimestres" = 4 períodos, "trimestres"
// = 3). Antes disso essas telas ficavam sempre em 4 "bimestres", mesmo
// quando a escola escolhia trimestre.
function periodInfo(data) {
  if ((data && data.calendar) === "trimestres") {
    return { count: 3, label: "trimestre", labelPlural: "Trimestres", abbrev: "tri" };
  }
  return { count: 4, label: "bimestre", labelPlural: "Bimestres", abbrev: "bim" };
}
window.periodInfo = periodInfo;

// Faixa de Scolex esperada por série (limite inferior/superior), tabela
// enviada pela Joana em 27/08/2026 — mesma ordem de SERIES_LIST (1º EM/2º
// EM/3º EM equivalem às linhas "10º ano"/"11º ano"/"12º ano" da tabela
// dela). Usada pra restringir a busca por tema (Passo Recomendações) à
// faixa certa por série — sem isso, a busca devolvia o mesmo pool de
// livros pra qualquer série, sem levar a dificuldade em conta (bug
// reportado pela Joana, 27/08/2026: "ele não está filtrando pelos
// colexes", vídeo mostrando o mesmo nível de livro pra séries bem
// diferentes).
const SCOLEX_RANGE_BY_SERIES = [
  { min: 0,   max: 336 }, // 1º ano
  { min: 157, max: 422 }, // 2º ano
  { min: 283, max: 489 }, // 3º ano
  { min: 381, max: 541 }, // 4º ano
  { min: 457, max: 582 }, // 5º ano
  { min: 516, max: 614 }, // 6º ano
  { min: 562, max: 638 }, // 7º ano
  { min: 598, max: 657 }, // 8º ano
  { min: 626, max: 672 }, // 9º ano
  { min: 648, max: 683 }, // 1º EM (10º ano)
  { min: 665, max: 692 }, // 2º EM (11º ano)
  { min: 678, max: 702 }, // 3º EM (12º ano)
];
window.SCOLEX_RANGE_BY_SERIES = SCOLEX_RANGE_BY_SERIES;

// Faixa Scolex pra um nome de série QUALQUER (o nome de exibição cadastrado
// pela escola, não a posição no array) — casa pelo nome canônico embutido
// nele, então continua funcionando mesmo com séries customizadas/reordenadas/
// Educação Infantil no meio da lista. Sem padrão reconhecido, retorna null
// (sem faixa automática, busca manual assume).
function scolexRangeForSeriesName(name) {
  const canonical = inferCanonicalSeriesName(name);
  const idx = canonical ? SERIES_LIST.indexOf(canonical) : -1;
  return idx >= 0 ? SCOLEX_RANGE_BY_SERIES[idx] : null;
}
window.scolexRangeForSeriesName = scolexRangeForSeriesName;

// ============= STEP DEFINITIONS =============
const STEPS = [
  { id: 0,  label: "Boas-vindas",            group: "Início" },
  { id: 1,  label: "Calendário & séries",    group: "Dados" },
  { id: 2,  label: "Lista do ano passado",   group: "Dados" },
  { id: 3,  label: "Diretrizes pedagógicas", group: "Dados" },
  { id: 4,  label: <>Nível <Scolex/></>,     short: "Scolex",             group: "Dados" },
  { id: 5,  label: "Calibragem",             group: "Curadoria Literária" },
  { id: 6,  label: "Disponibilidade",        group: "Curadoria Literária" },
  { id: 7,  label: "Recomendações",          group: "Curadoria Literária" },
  { id: 8,  label: "Revisão Livros Literatura", short: "Revisão", group: "Curadoria Literária" },
  { id: 9,  label: "Configuração",           group: "Didáticos" },
  { id: 10, label: "Adoção dos didáticos",   group: "Didáticos" },
  { id: 11, label: "Ponto de partida",       group: "Papelaria" },
  { id: 12, label: "Montagem da lista",      group: "Papelaria" },
  { id: 13, label: "Confirmação final",      group: "Fechamento" },
  { id: 14, label: "Previsão do preço da lista", short: "Previsão de preço", group: "Fechamento" },
  { id: 15, label: "Baixar lista",           group: "Fechamento" },
];

// ============= CONTEXT =============
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

// ============= LEX AVATAR =============
// Real Lex image, head-focused crop, round avatar style
function LexAvatar({ size = 72, mood = "happy", style = {} }) {
  return (
    <div style={{
      width: size, height: size,
      borderRadius: "50%",
      background: "linear-gradient(135deg, #e8f7e4, #d2eecb)",
      boxShadow: "0 6px 16px rgba(47,151,161,0.2)",
      border: "3px solid #fff",
      overflow: "hidden",
      position: "relative",
      flexShrink: 0,
      ...style,
    }}>
      <img src="assets/lex3.png" alt="Lex" style={{
        position: "absolute",
        width: size * 1.8,
        height: "auto",
        left: "50%",
        top: "-8%",
        transform: "translateX(-45%)",
        pointerEvents: "none",
      }}/>
    </div>
  );
}

// Full-body Lex for hero / welcome
function LexFullBody({ size = 300, pose = "wave" }) {
  // pose: wave (lex3), read (lex2), celebrate (lex1), study (lex4)
  const src = {
    wave: "assets/lex3.png",
    read: "assets/lex2.png",
    celebrate: "assets/lex1.png",
    study: "assets/lex4.png",
  }[pose] || "assets/lex3.png";
  return (
    <img src={src} alt="Lex" style={{
      width: size,
      height: "auto",
      display: "block",
      filter: "drop-shadow(0 18px 24px rgba(47,151,161,0.25))",
    }}/>
  );
}


// ============= LEX BUBBLE =============
// pose: "wave" (lex3), "read" (lex2), "cheer" (lex1), "books" (lex4), "papelaria" (lex-papelaria)
function LexBubble({ children, sub, pose = "wave", style = {} }) {
  const src = {
    wave: "assets/lex3.png",
    read: "assets/lex2.png",
    cheer: "assets/lex1.png",
    books: "assets/lex4.png",
    papelaria: "assets/lex-papelaria.png",
  }[pose] || "assets/lex3.png";

  return (
    <div style={{
      display: "flex", gap: 18, alignItems: "flex-end",
      ...style,
    }}>
      {/* Lex de corpo inteiro pequeno, SEM círculo, "pousado" ao lado da bolha */}
      <div style={{
        flexShrink: 0,
        width: 104,
        display: "flex", alignItems: "flex-end", justifyContent: "center",
        paddingBottom: 2,
      }}>
        <img src={src} alt="Lex" style={{
          width: "100%", height: "auto", display: "block",
          filter: "drop-shadow(0 6px 10px rgba(47,151,161,0.25))",
        }}/>
      </div>
      <div style={{
        position: "relative",
        background: "#fff",
        border: "1px solid var(--border)",
        borderRadius: "20px 20px 20px 4px",
        padding: "18px 24px",
        maxWidth: 680,
        boxShadow: "var(--shadow-sm)",
        marginBottom: 8,
      }}>
        {/* seta apontando pro Lex (canto inferior-esquerdo da bolha) */}
        <div style={{
          position: "absolute", left: -10, bottom: 14,
          width: 0, height: 0,
          borderTop: "10px solid transparent",
          borderBottom: "10px solid transparent",
          borderRight: "10px solid #fff",
        }}/>
        <div style={{
          position: "absolute", left: -11, bottom: 14,
          width: 0, height: 0,
          borderTop: "10px solid transparent",
          borderBottom: "10px solid transparent",
          borderRight: "10px solid var(--border)",
          zIndex: -1,
        }}/>
        <div style={{
          fontSize: 17, lineHeight: 1.55, fontWeight: 500, color: "var(--text)",
        }}>{children}</div>
        {sub && <div style={{
          fontSize: 14, lineHeight: 1.5, color: "var(--text-mute)", marginTop: 8,
        }}>{sub}</div>}
      </div>
    </div>
  );
}

// ============= BUTTONS =============
function BtnPrimary({ children, onClick, disabled, style = {}, size = "md" }) {
  const pad = size === "lg" ? "18px 40px" : "14px 32px";
  const fs = size === "lg" ? 18 : 16;
  return (
    <button onClick={onClick} disabled={disabled} style={{
      background: disabled ? "#b7c7c9" : "var(--blue)",
      color: "#fff",
      borderRadius: "var(--radius-btn)",
      padding: pad,
      fontSize: fs, fontWeight: 600,
      transition: "all 0.2s",
      boxShadow: disabled ? "none" : "0 4px 12px rgba(44,79,252,0.25)",
      cursor: disabled ? "not-allowed" : "pointer",
      ...style,
    }}
    onMouseEnter={e => { if (!disabled) e.currentTarget.style.background = "var(--blue-dark)"; }}
    onMouseLeave={e => { if (!disabled) e.currentTarget.style.background = "var(--blue)"; }}>
      {children}
    </button>
  );
}
function BtnGhost({ children, onClick, style = {} }) {
  return (
    <button onClick={onClick} style={{
      background: "transparent",
      color: "var(--teal-dark)",
      borderRadius: "var(--radius-btn)",
      padding: "14px 28px",
      fontSize: 16, fontWeight: 500,
      border: "1.5px solid var(--border)",
      transition: "all 0.2s",
      ...style,
    }}
    onMouseEnter={e => { e.currentTarget.style.background = "var(--bg-light)"; e.currentTarget.style.borderColor = "var(--teal)"; }}
    onMouseLeave={e => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.borderColor = "var(--border)"; }}>
      {children}
    </button>
  );
}
function BtnTeal({ children, onClick, style = {} }) {
  return (
    <button onClick={onClick} style={{
      background: "var(--teal)",
      color: "#fff",
      borderRadius: "var(--radius-btn)",
      padding: "14px 28px",
      fontSize: 16, fontWeight: 600,
      boxShadow: "0 4px 12px rgba(47,151,161,0.22)",
      transition: "all 0.2s",
      ...style,
    }}
    onMouseEnter={e => { e.currentTarget.style.background = "var(--teal-dark)"; }}
    onMouseLeave={e => { e.currentTarget.style.background = "var(--teal)"; }}>
      {children}
    </button>
  );
}

// ============= LOGO MARK =============
function ScolistMark({ size = 28, variant = "full" }) {
  // variant: "full" (icon+wordmark) | "mark" (icon only, for collapsed menus)
  const src = variant === "mark" ? "assets/scolist-mark-only.png" : "assets/scolist-logo-tight.png";
  const h = size * 1.4;
  return (
    <img
      src={src}
      alt="Scolist"
      style={{
        height: h,
        width: "auto",
        display: "block",
      }}
    />
  );
}

// ============= STEPPER (sidebar) =============
function Stepper({ current, onJump, collapsed = false }) {
  const groups = [];
  let last = null;
  STEPS.forEach(s => {
    if (s.group !== last) { groups.push({ name: s.group, items: [] }); last = s.group; }
    groups[groups.length - 1].items.push(s);
  });
  if (collapsed) {
    // Compact rail: só os números em círculos, com tooltip
    return (
      <div style={{ padding: "0 10px", display: "flex", flexDirection: "column", gap: 4 }}>
        {groups.map((g, gi) => (
          <React.Fragment key={g.name}>
            {gi > 0 && <div style={{ height: 1, background: "var(--border)", margin: "8px 4px" }}/>}
            {g.items.map(s => {
              const done = s.id < current;
              const active = s.id === current;
              return (
                <button key={s.id} onClick={() => onJump(s.id)} title={s.label} style={{
                  display: "flex", alignItems: "center", justifyContent: "center",
                  padding: "6px 0", borderRadius: 10,
                  background: active ? "rgba(47,151,161,0.12)" : "transparent",
                  border: "none", cursor: "pointer",
                  transition: "background 0.15s",
                }}
                onMouseEnter={e => { if (!active) e.currentTarget.style.background = "rgba(47,151,161,0.08)"; }}
                onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}>
                  <span style={{
                    display: "inline-flex", alignItems: "center", justifyContent: "center",
                    width: 28, height: 28, borderRadius: "50%",
                    background: done ? "var(--teal)" : (active ? "var(--blue)" : "#e4f3f4"),
                    color: done || active ? "#fff" : "var(--text-mute)",
                    fontSize: 12, fontWeight: 700,
                  }}>
                    {done ? "✓" : (s.id === 0 ? "👋" : s.id)}
                  </span>
                </button>
              );
            })}
          </React.Fragment>
        ))}
      </div>
    );
  }
  return (
    <div style={{ padding: "0 20px" }}>
      {groups.map(g => (
        <div key={g.name} style={{ marginBottom: 22 }}>
          <div style={{
            fontSize: 11, fontWeight: 700, letterSpacing: 1.2,
            textTransform: "uppercase", color: "var(--teal-dark)",
            opacity: 0.6, marginBottom: 10, paddingLeft: 4,
          }}>{g.name}</div>
          {g.items.map(s => {
            const done = s.id < current;
            const active = s.id === current;
            return (
              <button key={s.id} onClick={() => onJump(s.id)} style={{
                width: "100%", display: "flex", alignItems: "center", gap: 12,
                padding: "9px 10px", borderRadius: 10, marginBottom: 2,
                background: active ? "rgba(47,151,161,0.12)" : "transparent",
                color: active ? "var(--teal-dark)" : (done ? "var(--text)" : "var(--text-mute)"),
                fontSize: 14, fontWeight: active ? 600 : 500,
                textAlign: "left", transition: "background 0.15s",
              }}
              onMouseEnter={e => { if (!active) e.currentTarget.style.background = "rgba(47,151,161,0.05)"; }}
              onMouseLeave={e => { if (!active) e.currentTarget.style.background = "transparent"; }}>
                <span style={{
                  display: "inline-flex", alignItems: "center", justifyContent: "center",
                  width: 22, height: 22, borderRadius: "50%",
                  background: done ? "var(--teal)" : (active ? "var(--blue)" : "#e4f3f4"),
                  color: done || active ? "#fff" : "var(--text-mute)",
                  fontSize: 11, fontWeight: 700,
                  flexShrink: 0,
                }}>
                  {done ? "✓" : (s.id === 0 ? "👋" : s.id)}
                </span>
                <span>{s.label}</span>
              </button>
            );
          })}
        </div>
      ))}
    </div>
  );
}

// ============= SHELL LAYOUT =============
function Shell({ current, onJump, children, onPrev, onNext, canNext = true, nextLabel = "Continuar", showNav = true, onDemo, demoActive }) {
  const [collapsed, setCollapsed] = useState(() => {
    try { return localStorage.getItem("scolist.sidebarCollapsed") === "1"; } catch { return false; }
  });
  const toggleCollapsed = () => {
    setCollapsed(c => {
      const nv = !c;
      try { localStorage.setItem("scolist.sidebarCollapsed", nv ? "1" : "0"); } catch {}
      return nv;
    });
  };
  const sidebarW = collapsed ? 68 : 280;
  return (
    <div style={{
      display: "grid", gridTemplateColumns: `${sidebarW}px 1fr`,
      minHeight: "100vh", background: "var(--bg-light)",
      transition: "grid-template-columns 0.22s ease",
    }}>
      {/* Sidebar */}
      <aside style={{
        background: "#fff", borderRight: "1px solid var(--border)",
        padding: "0 0 28px", position: "sticky", top: 0, height: "100vh",
        overflowY: "auto", overflowX: "hidden",
        transition: "width 0.22s ease",
      }}>
        {/* Header brand */}
        <div style={{
          background: "#3f8ca0",
          padding: collapsed ? "18px 8px" : "20px 28px 24px",
          marginBottom: 16,
          position: "relative",
          minHeight: collapsed ? 68 : 104,
          display: "flex", flexDirection: "column",
          alignItems: collapsed ? "center" : "flex-start",
          justifyContent: "flex-start",
        }}>
          <ScolistMark size={collapsed ? 26 : 36} variant={collapsed ? "mark" : "full"} />
          {!collapsed && (
            <div style={{
              marginTop: 10, fontSize: 12.5, color: "rgba(255,255,255,0.92)",
              lineHeight: 1.45, fontWeight: 500,
            }}>
              Sua lista de volta às aulas<br/>mais inteligente e fácil.
            </div>
          )}

          {/* Toggle button, bleed onto the border */}
          <button
            onClick={toggleCollapsed}
            title={collapsed ? "Expandir menu" : "Recolher menu"}
            aria-label={collapsed ? "Expandir menu" : "Recolher menu"}
            style={{
              position: "absolute",
              top: 14,
              right: collapsed ? 6 : 12,
              width: 24, height: 24, borderRadius: "50%",
              background: "rgba(255,255,255,0.18)",
              border: "1px solid rgba(255,255,255,0.28)",
              color: "#fff", cursor: "pointer",
              display: "flex", alignItems: "center", justifyContent: "center",
              fontSize: 13, fontWeight: 700, lineHeight: 1,
              transition: "background 0.15s",
              padding: 0,
              zIndex: 2,
            }}
            onMouseEnter={e => { e.currentTarget.style.background = "rgba(255,255,255,0.32)"; }}
            onMouseLeave={e => { e.currentTarget.style.background = "rgba(255,255,255,0.18)"; }}
          >
            {collapsed ? "›" : "‹"}
          </button>
        </div>

        {/* School chip — reads from useApp().account (vem do login via /api/me) */}
        {(() => {
          const ctx = useApp();
          const acc = ctx && ctx.account;
          const sName = ctx?.schoolName || resolveSchoolName(acc);
          const sLogo = acc && acc.school && acc.school.logoUrl;
          const role  = (acc && acc.user && acc.user.role) || "Coord. pedagógica";
          const initials = sName.split(/\s+/).map(w => w[0]).filter(Boolean).slice(0,2).join("").toUpperCase() || "CS";
          // Confirmação inline (não window.confirm — inconsistente com o
          // resto da UI, que já usa modais/overlays próprios em todo lugar).
          const [confirmingLogout, setConfirmingLogout] = useState(false);
          const doLogout = () => { setConfirmingLogout(false); ctx && ctx.logout && ctx.logout(); };
          if (collapsed) {
            return (
              <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 8, marginBottom: 16 }}>
                <div title={sName} style={{
                  width: 36, height: 36, borderRadius: "50%",
                  background: sLogo ? "#fff" : "var(--teal)",
                  color: "#fff", border: sLogo ? "1px solid var(--border)" : "none",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  overflow: "hidden",
                  fontSize: 13, fontWeight: 700,
                }}>
                  {sLogo
                    ? <img src={sLogo} alt={sName} style={{ maxWidth: "82%", maxHeight: "82%", objectFit: "contain" }}/>
                    : initials}
                </div>
                {confirmingLogout ? (
                  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                    <button onClick={doLogout} title="Confirmar saída" aria-label="Confirmar saída" style={{
                      width: 28, height: 28, borderRadius: "50%",
                      background: "#e0798e", border: "none", color: "#fff", cursor: "pointer",
                      display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13,
                    }}>✓</button>
                    <button onClick={() => setConfirmingLogout(false)} title="Cancelar" aria-label="Cancelar" style={{
                      width: 28, height: 28, borderRadius: "50%",
                      background: "rgba(255,255,255,0.14)", border: "1px solid rgba(255,255,255,0.24)",
                      color: "#fff", cursor: "pointer",
                      display: "flex", alignItems: "center", justifyContent: "center", fontSize: 13,
                    }}>✕</button>
                  </div>
                ) : (
                  <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
                    {ctx && ctx.canSwitchSchool && (
                      <button
                        onClick={() => ctx.switchSchool()}
                        title="Trocar escola"
                        aria-label="Trocar escola"
                        style={{
                          width: 28, height: 28, borderRadius: "50%",
                          background: "rgba(255,255,255,0.14)",
                          border: "1px solid rgba(255,255,255,0.24)",
                          color: "#fff", cursor: "pointer",
                          display: "flex", alignItems: "center", justifyContent: "center",
                          fontSize: 13,
                        }}
                      >⇄</button>
                    )}
                    <button
                      onClick={() => setConfirmingLogout(true)}
                      title="Sair"
                      aria-label="Sair da conta"
                      style={{
                        width: 28, height: 28, borderRadius: "50%",
                        background: "rgba(255,255,255,0.14)",
                        border: "1px solid rgba(255,255,255,0.24)",
                        color: "#fff", cursor: "pointer",
                        display: "flex", alignItems: "center", justifyContent: "center",
                        fontSize: 13,
                      }}
                    >⏻</button>
                  </div>
                )}
              </div>
            );
          }
          if (confirmingLogout) {
            return (
              <div style={{
                margin: "0 20px 20px", padding: "12px 14px",
                background: "#fdecec", border: "1px solid #e0798e",
                borderRadius: 12,
              }}>
                <div style={{ fontSize: 12.5, color: "var(--text)", fontWeight: 600, marginBottom: 10 }}>
                  Sair da conta? Você precisará entrar de novo pra continuar.
                </div>
                <div style={{ display: "flex", gap: 8 }}>
                  <button onClick={doLogout} style={{
                    flex: 1, padding: "7px 10px", borderRadius: 8,
                    background: "#e0798e", border: "none", color: "#fff",
                    fontSize: 12.5, fontWeight: 700, cursor: "pointer",
                  }}>Sair</button>
                  <button onClick={() => setConfirmingLogout(false)} style={{
                    flex: 1, padding: "7px 10px", borderRadius: 8,
                    background: "#fff", border: "1px solid var(--border)", color: "var(--text)",
                    fontSize: 12.5, fontWeight: 600, cursor: "pointer",
                  }}>Cancelar</button>
                </div>
              </div>
            );
          }
          return (
            <div style={{
              margin: "0 20px 20px", padding: "12px 14px",
              background: "var(--bg-light)", border: "1px solid var(--border)",
              borderRadius: 12, display: "flex", gap: 10, alignItems: "center",
            }}>
              <div style={{
                width: 36, height: 36, borderRadius: sLogo ? 8 : "50%",
                background: sLogo ? "#fff" : "var(--teal)",
                color: "#fff", border: sLogo ? "1px solid var(--border)" : "none",
                display: "flex", alignItems: "center", justifyContent: "center",
                overflow: "hidden", flexShrink: 0,
                fontSize: 13, fontWeight: 700,
              }}>
                {sLogo
                  ? <img src={sLogo} alt={sName} style={{ maxWidth: "82%", maxHeight: "82%", objectFit: "contain" }}/>
                  : initials}
              </div>
              <div style={{ minWidth: 0, flex: 1 }}>
                <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{sName}</div>
                <div style={{ fontSize: 11, color: "var(--text-mute)" }}>{role}</div>
                {ctx && ctx.canSwitchSchool && (
                  <button
                    onClick={() => ctx.switchSchool()}
                    style={{
                      marginTop: 2, background: "transparent", border: "none", padding: 0,
                      color: "var(--teal-dark)", fontSize: 11, fontWeight: 600, cursor: "pointer",
                    }}
                  >Trocar escola</button>
                )}
              </div>
              <button
                onClick={() => setConfirmingLogout(true)}
                title="Sair da conta"
                aria-label="Sair da conta"
                style={{
                  flexShrink: 0, width: 28, height: 28, borderRadius: "50%",
                  background: "transparent", border: "1px solid var(--border)",
                  color: "var(--text-mute)", cursor: "pointer",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 13,
                }}
                onMouseEnter={e => { e.currentTarget.style.background = "#fdecec"; e.currentTarget.style.borderColor = "#e0798e"; e.currentTarget.style.color = "#b53a3a"; }}
                onMouseLeave={e => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.color = "var(--text-mute)"; }}
              >⏻</button>
            </div>
          );
        })()}

        <Stepper current={current} onJump={onJump} collapsed={collapsed}/>

        {/* Demo button */}
        <div style={{ padding: collapsed ? "16px 10px 12px" : "18px 24px 12px", marginTop: 12, borderTop: "1px solid var(--border)" }}>
          {collapsed ? (
            <button onClick={onDemo} title={demoActive ? "Pausar demo" : "Modo demo"} style={{
              width: 40, height: 40, margin: "0 auto", display: "flex", alignItems: "center", justifyContent: "center",
              borderRadius: 10,
              background: demoActive ? "var(--blue)" : "var(--bg-light)",
              color: demoActive ? "#fff" : "var(--teal-dark)",
              border: "1px solid " + (demoActive ? "var(--blue)" : "var(--border)"),
              fontSize: 14, fontWeight: 600, cursor: "pointer",
            }}>
              {demoActive ? "■" : "▶"}
            </button>
          ) : (
            <button onClick={onDemo} style={{
              width: "100%", display: "flex", alignItems: "center", gap: 10,
              padding: "10px 12px", borderRadius: 10,
              background: demoActive ? "var(--blue)" : "var(--bg-light)",
              color: demoActive ? "#fff" : "var(--teal-dark)",
              border: "1px solid " + (demoActive ? "var(--blue)" : "var(--border)"),
              fontSize: 13, fontWeight: 600,
            }}>
              <span style={{fontSize:14}}>{demoActive ? "■" : "▶"}</span>
              {demoActive ? "Pausar demo" : "Modo demo auto-guiado"}
            </button>
          )}
        </div>
      </aside>

      {/* Main */}
      <main style={{
        padding: "40px 56px 160px", position: "relative",
        width: "100%", minWidth: 0,
      }}>
        <div style={{ maxWidth: 1160, width: "100%" }}>
          {children}
        </div>
        {showNav && (
          <div style={{
            position: "fixed", bottom: 0, left: sidebarW, right: 0,
            padding: "18px 56px", background: "rgba(240,251,251,0.95)",
            backdropFilter: "blur(12px)",
            borderTop: "1px solid var(--border)",
            display: "flex", justifyContent: "space-between", alignItems: "center",
            zIndex: 20,
            transition: "left 0.22s ease",
          }}>
            <div style={{
              display: "flex", alignItems: "center", gap: 14,
              color: "var(--text-mute)", fontSize: 13, fontWeight: 500,
            }}>
              <div style={{
                display: "flex", gap: 4,
              }}>
                {STEPS.map(s => (
                  <div key={s.id} style={{
                    width: 24, height: 3, borderRadius: 2,
                    background: s.id <= current ? "var(--teal)" : "#dce9ea",
                    transition: "background 0.3s",
                  }}/>
                ))}
              </div>
              <span>Passo {current} de {STEPS.length - 1}</span>
            </div>
            <div style={{ display: "flex", gap: 12 }}>
              {current > 0 && <BtnGhost onClick={onPrev}>← Voltar</BtnGhost>}
              <BtnPrimary onClick={onNext} disabled={!canNext}>{nextLabel} →</BtnPrimary>
            </div>
          </div>
        )}
      </main>
    </div>
  );
}

// ============= CARDS =============
function OptionCard({ selected, onClick, children, style = {} }) {
  return (
    <button onClick={onClick} style={{
      background: selected ? "var(--bg-light)" : "#fff",
      border: `2px solid ${selected ? "var(--teal)" : "var(--border)"}`,
      borderRadius: "var(--radius-card)",
      padding: "22px 24px",
      textAlign: "left",
      transition: "all 0.2s",
      boxShadow: selected ? "0 6px 20px rgba(47,151,161,0.15)" : "var(--shadow-sm)",
      width: "100%",
      cursor: "pointer",
      ...style,
    }}
    onMouseEnter={e => { if (!selected) { e.currentTarget.style.borderColor = "var(--teal)"; e.currentTarget.style.boxShadow = "var(--shadow-md)"; }}}
    onMouseLeave={e => { if (!selected) { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.boxShadow = "var(--shadow-sm)"; }}}>
      {children}
    </button>
  );
}

function Chip({ selected, onClick, children }) {
  return (
    <button onClick={onClick} style={{
      padding: "10px 18px",
      borderRadius: 999,
      border: `1.5px solid ${selected ? "var(--teal)" : "var(--border)"}`,
      background: selected ? "var(--teal)" : "#fff",
      color: selected ? "#fff" : "var(--text)",
      fontSize: 14, fontWeight: 500,
      transition: "all 0.15s",
    }}>{children}</button>
  );
}

// ============= UPLOAD ZONE =============
// Mesmo padrão do backend (functions/lib/utils/Utils.js, ISBN_REGEX) —
// mantém os dois lados consistentes no reconhecimento de ISBN.
const ISBN_REGEX = /(?:97[89]|789)\d{10}/g;

function extractIsbnsFromText(text) {
  const matches = String(text || "").match(ISBN_REGEX) || [];
  return Array.from(new Set(matches));
}

async function extractIsbnsFromPdfFile(file) {
  if (!window.pdfjsLib) return [];
  const buffer = await file.arrayBuffer();
  const pdf = await window.pdfjsLib.getDocument({ data: buffer }).promise;
  let fullText = "";
  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const content = await page.getTextContent();
    fullText += content.items.map((it) => it.str || "").join(" ") + "\n";
  }
  return extractIsbnsFromText(fullText);
}

function extractIsbnsFromSpreadsheetFile(file) {
  return new Promise((resolve, reject) => {
    if (!window.XLSX) { resolve([]); return; }
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const wb = window.XLSX.read(e.target.result, { type: "array" });
        let fullText = "";
        wb.SheetNames.forEach((name) => {
          const rows = window.XLSX.utils.sheet_to_json(wb.Sheets[name], { header: 1, raw: false });
          fullText += rows.map((r) => (r || []).join(" ")).join("\n") + "\n";
        });
        resolve(extractIsbnsFromText(fullText));
      } catch (err) { reject(err); }
    };
    reader.onerror = reject;
    reader.readAsArrayBuffer(file);
  });
}

// Reconhece ISBNs num arquivo de lista (PDF ou planilha). Usado no upload de
// lista (item 4, TAREFAS-JOANA-2026-08-20.md) — listas sem ISBN reconhecível
// não são suportadas ainda, então quem chama isso precisa avisar o usuário
// quando a extração vier vazia, em vez de fingir sucesso.
async function extractIsbnsFromFile(file) {
  const name = (file.name || "").toLowerCase();
  if (name.endsWith(".pdf")) return extractIsbnsFromPdfFile(file);
  if (name.endsWith(".xlsx") || name.endsWith(".xls") || name.endsWith(".csv")) return extractIsbnsFromSpreadsheetFile(file);
  return [];
}
window.extractIsbnsFromFile = extractIsbnsFromFile;

// `extractIsbns`: quando ligado, cada arquivo é lido de verdade (PDF via
// pdf.js, Excel/CSV via SheetJS) pra reconhecer ISBNs — mostra quantos foram
// achados, ou um aviso claro quando não acha nenhum (listas sem ISBN não são
// suportadas ainda). Sem essa flag, mantém o comportamento antigo (só marca
// "carregado"), usado pelas planilhas de Scolex PT/EN que não são listas de
// livro.
function UploadZone({ label, accept = ".pdf,.xlsx,.csv", files, onAdd, onIsbnsFound, single = false, extractIsbns = false }) {
  const inputRef = useRef();
  const [isbnStatusByFile, setIsbnStatusByFile] = useState({}); // { [fileName]: "reading" | { count } | "empty" | "error" }

  const handleFiles = async (fileList) => {
    const fileArr = Array.from(fileList || []);
    const names = fileArr.map(f => f.name);
    if (!names.length) return;
    onAdd(names);

    if (!extractIsbns) return;

    setIsbnStatusByFile(prev => {
      const next = { ...prev };
      names.forEach(n => { next[n] = "reading"; });
      return next;
    });

    for (const file of fileArr) {
      try {
        const isbns = await extractIsbnsFromFile(file);
        setIsbnStatusByFile(prev => ({ ...prev, [file.name]: isbns.length ? { count: isbns.length } : "empty" }));
        if (onIsbnsFound) onIsbnsFound(file.name, isbns);
      } catch (err) {
        console.error("Falha ao reconhecer ISBNs em", file.name, err);
        setIsbnStatusByFile(prev => ({ ...prev, [file.name]: "error" }));
      }
    }
  };

  return (
    <div>
      <div
        onClick={() => inputRef.current?.click()}
        style={{
          border: "2px dashed var(--border)",
          borderRadius: 16, padding: "28px 24px",
          background: "var(--bg-soft)",
          textAlign: "center",
          cursor: "pointer",
          transition: "all 0.15s",
        }}
        onMouseEnter={e => { e.currentTarget.style.borderColor = "var(--teal)"; e.currentTarget.style.background = "var(--bg-light)"; }}
        onMouseLeave={e => { e.currentTarget.style.borderColor = "var(--border)"; e.currentTarget.style.background = "var(--bg-soft)"; }}>
        <div style={{
          width: 42, height: 42, margin: "0 auto 10px",
          borderRadius: 12, background: "var(--teal)",
          display: "flex", alignItems: "center", justifyContent: "center",
          color: "#fff", fontSize: 22, fontWeight: 700,
        }}>↑</div>
        <div style={{ fontSize: 15, fontWeight: 600, color: "var(--text)" }}>{label}</div>
        <div style={{ fontSize: 12.5, color: "var(--text-mute)", marginTop: 4 }}>
          Arraste ou clique, PDF, Excel, CSV
        </div>
      </div>
      <input
        ref={inputRef} type="file" accept={accept} multiple={!single}
        style={{ display: "none" }}
        onChange={e => {
          handleFiles(e.target.files);
          e.target.value = "";
        }}
      />
      {files && files.length > 0 && (
        <div style={{ marginTop: 12, display: "flex", flexDirection: "column", gap: 6 }}>
          {files.map((f, i) => {
            const isbnStatus = isbnStatusByFile[f];
            return (
            <div key={i}>
              <div style={{
                display: "flex", gap: 10, alignItems: "center",
                padding: "10px 14px",
                background: "#fff", border: "1px solid var(--border)",
                borderRadius: 10, fontSize: 13,
              }}>
                <div style={{
                  width: 28, height: 28, borderRadius: 6,
                  background: "var(--bg-light)", color: "var(--teal-dark)",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 11, fontWeight: 700,
                }}>{f.split(".").pop().toUpperCase().slice(0,4)}</div>
                <div style={{flex:1, fontWeight:500, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>{f}</div>
                {!extractIsbns ? (
                  <div style={{fontSize: 11, color: "var(--teal)", fontWeight: 600}}>✓ carregado</div>
                ) : isbnStatus === "reading" ? (
                  <div style={{fontSize: 11, color: "var(--text-mute)", fontWeight: 600}}>Lendo…</div>
                ) : isbnStatus === "error" ? (
                  <div style={{fontSize: 11, color: "var(--red, #c0392b)", fontWeight: 600}}>Erro ao ler arquivo</div>
                ) : isbnStatus === "empty" ? (
                  <div style={{fontSize: 11, color: "var(--red, #c0392b)", fontWeight: 600}}>⚠ Nenhum ISBN encontrado</div>
                ) : isbnStatus && isbnStatus.count ? (
                  <div style={{fontSize: 11, color: "var(--teal)", fontWeight: 600}}>✓ {isbnStatus.count} ISBN{isbnStatus.count !== 1 ? "s" : ""} reconhecido{isbnStatus.count !== 1 ? "s" : ""}</div>
                ) : (
                  <div style={{fontSize: 11, color: "var(--teal)", fontWeight: 600}}>✓ carregado</div>
                )}
              </div>
              {extractIsbns && isbnStatus === "empty" && (
                <div style={{
                  marginTop: 4, padding: "8px 14px", fontSize: 12.5, color: "var(--text-mute)",
                  background: "var(--bg-light)", borderRadius: 8, lineHeight: 1.5,
                }}>
                  Não encontramos nenhum ISBN em <strong>{f}</strong>. Listas sem ISBN não são suportadas ainda — confira se o arquivo tem o código ISBN de cada item.
                </div>
              )}
            </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ============= PREMIUM HEADER =============
// Compact horizontal header with side image, for papelaria + didáticos.
// Less screaming than a full hero, still has personality.
function PremiumHeader({ image, imageAlt = "", eyebrow, title, subtitle }) {
  return (
    <div style={{
      margin: "-20px -20px 32px",
      borderRadius: 18,
      overflow: "hidden",
      background: "linear-gradient(135deg, #f5efe5 0%, #ebe2d2 100%)",
      border: "1px solid var(--border)",
      display: "grid",
      gridTemplateColumns: "minmax(280px, 38%) 1fr",
      alignItems: "stretch",
      minHeight: 180,
    }}>
      <div style={{ position: "relative", overflow: "hidden", background: "#2a1e17" }}>
        <img src={image} alt={imageAlt} style={{
          width: "100%", height: "100%",
          objectFit: "cover", objectPosition: "center center",
          display: "block",
        }}/>
        <div aria-hidden style={{
          position: "absolute", inset: 0,
          background: "linear-gradient(90deg, transparent 70%, rgba(245,239,229,0.85) 100%)",
          pointerEvents: "none",
        }}/>
      </div>
      <div style={{ padding: "26px 32px 26px 24px", alignSelf: "center", maxWidth: 720 }}>
        {eyebrow && <div style={{
          fontSize: 11, fontWeight: 700, letterSpacing: 1.3,
          textTransform: "uppercase", color: "var(--teal)", marginBottom: 10,
        }}>{eyebrow}</div>}
        <h1 style={{
          fontSize: 30, fontWeight: 800, lineHeight: 1.15,
          color: "var(--text)", letterSpacing: -0.5, textWrap: "balance",
        }}>{title}</h1>
        {subtitle && <p style={{
          marginTop: 12, fontSize: 15, color: "var(--text-mute)",
          lineHeight: 1.55, textWrap: "pretty",
        }}>{subtitle}</p>}
      </div>
    </div>
  );
}

// ============= HEADING =============
function Heading({ eyebrow, title, subtitle }) {
  return (
    <div style={{ marginBottom: 32 }}>
      {eyebrow && <div style={{
        fontSize: 12, fontWeight: 700, letterSpacing: 1.3,
        textTransform: "uppercase", color: "var(--teal)", marginBottom: 10,
      }}>{eyebrow}</div>}
      <h1 style={{
        fontSize: 36, fontWeight: 800, lineHeight: 1.15,
        color: "var(--text)", maxWidth: 820, letterSpacing: -0.5,
      }}>{title}</h1>
      {subtitle && <p style={{
        marginTop: 14, fontSize: 17, color: "var(--text-mute)",
        lineHeight: 1.55, maxWidth: 760,
      }}>{subtitle}</p>}
    </div>
  );
}

// ============= BOOK COVER (SVG placeholder + real image fallback) =============
// Extract ASIN from amazon URL like https://www.amazon.com.br/dp/XXXXXXXXXX
function extractAsin(url) {
  if (!url) return null;
  const m = url.match(/\/dp\/([A-Z0-9]{10})/i);
  return m ? m[1] : null;
}

// Curated list, only these 16 books get a real cover (scope: telas principais).
// Keyed by book id from CATALOG. URLs are Google Books publisher frontcover CDN
// (hotlink-friendly, stable). Probed live, all 15 below load at ~600px.
// bk128 (Catcher in the Rye) não tem match confiável; cai no fallback sintético.
const REAL_COVERS = {
  // PT
  bk001: "https://books.google.com/books/publisher/content/images/frontcover/DWeNDwAAQBAJ?fife=w600-h900", // Amoras
  bk013: "https://books.google.com/books/publisher/content/images/frontcover/5mGoEAAAQBAJ?fife=w600-h900", // O Dia em que a Poesia fugiu
  bk025: "https://books.google.com/books/publisher/content/images/frontcover/yqCIEQAAQBAJ?fife=w600-h900", // A Droga da Obediência
  bk037: "https://books.google.com/books/publisher/content/images/frontcover/GzWFEAAAQBAJ?fife=w600-h900", // A Bolsa Amarela
  bk049: "https://books.google.com/books/publisher/content/images/frontcover/HGuFEQAAQBAJ?fife=w600-h900", // Extraordinário
  bk061: "https://books.google.com/books/publisher/content/images/frontcover/LhRKEAAAQBAJ?fife=w600-h900", // Ponte para Terabítia
  bk074: "https://books.google.com/books/publisher/content/images/frontcover/AskKEAAAQBAJ?fife=w600-h900", // A Vida não é útil
  bk085: "https://books.google.com/books/publisher/content/images/frontcover/eZwPVuqhDbYC?fife=w600-h900", // A Revolução dos Bichos
  bk097: "https://books.google.com/books/publisher/content/images/frontcover/CZNuEQAAQBAJ?fife=w600-h900", // Quarto de Despejo
  bk113: "https://books.google.com/books/publisher/content/images/frontcover/lL2GDwAAQBAJ?fife=w600-h900", // Torto Arado
  bk121: "https://books.google.com/books/publisher/content/images/frontcover/dk4tAAAAYAAJ?fife=w600-h900", // Dom Casmurro
  bk133: "https://books.google.com/books/publisher/content/images/frontcover/zPe-DwAAQBAJ?fife=w600-h900", // Grande Sertão
  // EN
  bk055: "https://books.google.com/books/publisher/content/images/frontcover/VbAxEAAAQBAJ?fife=w600-h900", // Wonder
  bk104: "https://books.google.com/books/publisher/content/images/frontcover/GtthEAAAQBAJ?fife=w600-h900", // Animal Farm
  bk115: "https://books.google.com/books/publisher/content/images/frontcover/iXn5U2IzVH0C?fife=w600-h900", // The Great Gatsby
  // ==== Livros solicitados pela escola (Land School) ====
  bk145: "https://books.google.com/books/publisher/content/images/frontcover/jSmLEQAAQBAJ?fife=w600-h900", // Diário de Pilar Amazônia
  bk146: "https://books.google.com/books/publisher/content/images/frontcover/ZmxfAAAAMAAJ?fife=w600-h900", // Memórias Inventadas
  bk147: "https://books.google.com/books/publisher/content/images/frontcover/BrksEAAAQBAJ?fife=w600-h900", // 360 Língua Portuguesa
  bk148: "https://books.google.com/books/publisher/content/images/frontcover/HL6iEAAAQBAJ?fife=w600-h900", // How to Carry Water
  bk149: "https://books.google.com/books/publisher/content/images/frontcover/yz0GAQAAIAAJ?fife=w600-h900", // Things Fall Apart
  bk150: "https://books.google.com/books/publisher/content/images/frontcover/9kyTimytKJoC?fife=w600-h900", // Cuentos de la selva
  bk151: "https://books.google.com/books/publisher/content/images/frontcover/Xq0mAQAAMAAJ?fife=w600-h900", // Doce cuentos peregrinos
  bk152: "https://books.google.com/books/publisher/content/images/frontcover/nO2NEAAAQBAJ?fife=w600-h900", // Cuentos Completos Borges
  bk153: "https://books.google.com/books/publisher/content/images/frontcover/kuMJU0BSLZwC?fife=w600-h900", // O Quinze
  bk154: "https://books.google.com/books/publisher/content/images/frontcover/xPwMEQAAQBAJ?fife=w600-h900", // Sentimento do mundo
  bk155: "https://books.google.com/books/publisher/content/images/frontcover/ZRYfAgAAQBAJ?fife=w600-h900", // A Metamorfose (The Metamorphosis)
  bk156: "https://books.google.com/books/publisher/content/images/frontcover/iXn5U2IzVH0C?fife=w600-h900", // Gatsby Alma (fallback to same)
  // ==== Livros Colégio Sementeira (300 parceiros Grupo Cia das Letras) ====
  bk200: "https://books.google.com/books/publisher/content/images/frontcover/9uMzvgAACAAJ?fife=w600-h900",
  bk201: "https://books.google.com/books/publisher/content/images/frontcover/J9c9zgEACAAJ?fife=w600-h900",
  bk202: "https://books.google.com/books/publisher/content/images/frontcover/mzB4PgAACAAJ?fife=w600-h900",
  bk203: "https://books.google.com/books/publisher/content/images/frontcover/qmx-swEACAAJ?fife=w600-h900",
  bk204: "https://books.google.com/books/publisher/content/images/frontcover/uhA9YgEACAAJ?fife=w600-h900",
  bk205: "https://books.google.com/books/publisher/content/images/frontcover/NHZ8rgEACAAJ?fife=w600-h900",
  bk206: "https://books.google.com/books/publisher/content/images/frontcover/aiiWPgAACAAJ?fife=w600-h900",
  bk207: "https://books.google.com/books/publisher/content/images/frontcover/U2qbZwEACAAJ?fife=w600-h900",
  bk208: "https://books.google.com/books/publisher/content/images/frontcover/w-J0PgAACAAJ?fife=w600-h900",
  bk209: "https://books.google.com/books/publisher/content/images/frontcover/GGq4PwAACAAJ?fife=w600-h900",
  bk210: "https://books.google.com/books/publisher/content/images/frontcover/GmgRyAEACAAJ?fife=w600-h900",
  bk211: "https://books.google.com/books/publisher/content/images/frontcover/_DnYPwAACAAJ?fife=w600-h900",
  bk212: "https://books.google.com/books/publisher/content/images/frontcover/xFkSvgAACAAJ?fife=w600-h900",
  bk213: "https://books.google.com/books/publisher/content/images/frontcover/FbTb0AEACAAJ?fife=w600-h900",
  bk214: "https://books.google.com/books/publisher/content/images/frontcover/uuuwtAEACAAJ?fife=w600-h900",
  bk215: "https://books.google.com/books/publisher/content/images/frontcover/UkxMvgAACAAJ?fife=w600-h900",
  bk216: "https://books.google.com/books/publisher/content/images/frontcover/EzhpOwAACAAJ?fife=w600-h900",
  bk218: "https://books.google.com/books/publisher/content/images/frontcover/ulukcQAACAAJ?fife=w600-h900",
  bk219: "https://books.google.com/books/publisher/content/images/frontcover/sgmtPwAACAAJ?fife=w600-h900",
  bk220: "https://books.google.com/books/publisher/content/images/frontcover/rBGTPgAACAAJ?fife=w600-h900",
  bk221: "https://books.google.com/books/publisher/content/images/frontcover/S4e3PwAACAAJ?fife=w600-h900",
  bk222: "https://books.google.com/books/publisher/content/images/frontcover/IQkpLgEACAAJ?fife=w600-h900",
  bk223: "https://books.google.com/books/publisher/content/images/frontcover/b5JqzQEACAAJ?fife=w600-h900",
  bk224: "https://books.google.com/books/publisher/content/images/frontcover/aBMtzgEACAAJ?fife=w600-h900",
  bk225: "https://books.google.com/books/publisher/content/images/frontcover/Ngas0AEACAAJ?fife=w600-h900",
  bk226: "https://books.google.com/books/publisher/content/images/frontcover/7aTb0AEACAAJ?fife=w600-h900",
  bk229: "https://books.google.com/books/publisher/content/images/frontcover/7IcV0QEACAAJ?fife=w600-h900",
  bk230: "https://books.google.com/books/publisher/content/images/frontcover/UuQvLgEACAAJ?fife=w600-h900",
  bk231: "https://books.google.com/books/publisher/content/images/frontcover/iA8_PwAACAAJ?fife=w600-h900",
  bk232: "https://books.google.com/books/publisher/content/images/frontcover/3rBSuwEACAAJ?fife=w600-h900",
  bk233: "https://books.google.com/books/publisher/content/images/frontcover/Z_D-zwEACAAJ?fife=w600-h900",
  bk234: "https://books.google.com/books/publisher/content/images/frontcover/-y8F0AEACAAJ?fife=w600-h900",
  bk235: "https://books.google.com/books/publisher/content/images/frontcover/YXHaAAAACAAJ?fife=w600-h900",
  bk236: "https://books.google.com/books/publisher/content/images/frontcover/3sSOPgAACAAJ?fife=w600-h900",
  bk237: "https://books.google.com/books/publisher/content/images/frontcover/b4ItAAAAYAAJ?fife=w600-h900",
  bk238: "https://books.google.com/books/publisher/content/images/frontcover/hnaiAAAACAAJ?fife=w600-h900",
  bk239: "https://books.google.com/books/publisher/content/images/frontcover/xSv3zwEACAAJ?fife=w600-h900",
  bk240: "https://books.google.com/books/publisher/content/images/frontcover/zCkJwQEACAAJ?fife=w600-h900",
  bk241: "https://books.google.com/books/publisher/content/images/frontcover/LL34PQAACAAJ?fife=w600-h900",
  bk242: "https://books.google.com/books/publisher/content/images/frontcover/PJiVQwAACAAJ?fife=w600-h900",
  bk243: "https://books.google.com/books/publisher/content/images/frontcover/GHc4swEACAAJ?fife=w600-h900",
  bk244: "https://books.google.com/books/publisher/content/images/frontcover/P1NktgEACAAJ?fife=w600-h900",
  bk245: "https://books.google.com/books/publisher/content/images/frontcover/QXz9zwEACAAJ?fife=w600-h900",
  bk246: "https://books.google.com/books/publisher/content/images/frontcover/hcVwAAAACAAJ?fife=w600-h900",
  bk247: "https://books.google.com/books/publisher/content/images/frontcover/1KoEmgEACAAJ?fife=w600-h900",
  bk248: "https://books.google.com/books/publisher/content/images/frontcover/JYCr0AEACAAJ?fife=w600-h900",
  bk249: "https://books.google.com/books/publisher/content/images/frontcover/EBVpvgEACAAJ?fife=w600-h900",
  bk250: "https://books.google.com/books/publisher/content/images/frontcover/pTLMAAAACAAJ?fife=w600-h900",
  bk251: "https://books.google.com/books/publisher/content/images/frontcover/U9KpcQAACAAJ?fife=w600-h900",
  bk252: "https://books.google.com/books/publisher/content/images/frontcover/haidxgEACAAJ?fife=w600-h900",
  bk253: "https://books.google.com/books/publisher/content/images/frontcover/0sCR0AEACAAJ?fife=w600-h900",
  bk254: "https://books.google.com/books/publisher/content/images/frontcover/nTDiQwAACAAJ?fife=w600-h900",
  bk255: "https://books.google.com/books/publisher/content/images/frontcover/oPU8AAAAYAAJ?fife=w600-h900",
  bk256: "https://books.google.com/books/publisher/content/images/frontcover/ezEjQwAACAAJ?fife=w600-h900",
  bk257: "https://books.google.com/books/publisher/content/images/frontcover/hRksvgAACAAJ?fife=w600-h900",
  bk258: "https://books.google.com/books/publisher/content/images/frontcover/vjn5zwEACAAJ?fife=w600-h900",
  bk260: "https://books.google.com/books/publisher/content/images/frontcover/mAMbvgAACAAJ?fife=w600-h900",
  bk261: "https://books.google.com/books/publisher/content/images/frontcover/RmCQAAAACAAJ?fife=w600-h900",
  bk262: "https://books.google.com/books/publisher/content/images/frontcover/NIpNxgEACAAJ?fife=w600-h900",
  bk263: "https://books.google.com/books/publisher/content/images/frontcover/_jdMtAEACAAJ?fife=w600-h900",
  bk264: "https://books.google.com/books/publisher/content/images/frontcover/0_u8swEACAAJ?fife=w600-h900",
  bk265: "https://books.google.com/books/publisher/content/images/frontcover/YZ_cSAAACAAJ?fife=w600-h900",
  bk267: "https://books.google.com/books/publisher/content/images/frontcover/zhEJQgAACAAJ?fife=w600-h900",
  bk268: "https://books.google.com/books/publisher/content/images/frontcover/UaV7AAAACAAJ?fife=w600-h900",
  bk269: "https://books.google.com/books/publisher/content/images/frontcover/D-T2zwEACAAJ?fife=w600-h900",
  bk270: "https://books.google.com/books/publisher/content/images/frontcover/g_9pswEACAAJ?fife=w600-h900",
  bk271: "https://books.google.com/books/publisher/content/images/frontcover/3m1nzgEACAAJ?fife=w600-h900",
  bk272: "https://books.google.com/books/publisher/content/images/frontcover/TWmruQEACAAJ?fife=w600-h900",
  bk273: "https://books.google.com/books/publisher/content/images/frontcover/nW2jQwAACAAJ?fife=w600-h900",
  bk275: "https://books.google.com/books/publisher/content/images/frontcover/0mxbwQEACAAJ?fife=w600-h900",
  bk276: "https://books.google.com/books/publisher/content/images/frontcover/t3bAmgEACAAJ?fife=w600-h900",
  bk277: "https://books.google.com/books/publisher/content/images/frontcover/bsHgAAAACAAJ?fife=w600-h900",
  bk278: "https://books.google.com/books/publisher/content/images/frontcover/HalDvgAACAAJ?fife=w600-h900",
  bk279: "https://books.google.com/books/publisher/content/images/frontcover/T3rAAAAACAAJ?fife=w600-h900",
  bk280: "https://books.google.com/books/publisher/content/images/frontcover/3-kU0QEACAAJ?fife=w600-h900",
  bk281: "https://books.google.com/books/publisher/content/images/frontcover/mwbc0AEACAAJ?fife=w600-h900",
  bk282: "https://books.google.com/books/publisher/content/images/frontcover/PFtjPwAACAAJ?fife=w600-h900",
  bk284: "https://books.google.com/books/publisher/content/images/frontcover/P4kH0QEACAAJ?fife=w600-h900",
  bk285: "https://books.google.com/books/publisher/content/images/frontcover/9Ow2PwAACAAJ?fife=w600-h900",
  bk286: "https://books.google.com/books/publisher/content/images/frontcover/nLwtt6tAkYAC?fife=w600-h900",
  bk287: "https://books.google.com/books/publisher/content/images/frontcover/-r3gPgAACAAJ?fife=w600-h900",
  bk288: "https://books.google.com/books/publisher/content/images/frontcover/cDe7NAEACAAJ?fife=w600-h900",
  bk289: "https://books.google.com/books/publisher/content/images/frontcover/BCtTvgAACAAJ?fife=w600-h900",
  bk290: "https://books.google.com/books/publisher/content/images/frontcover/zcPetAEACAAJ?fife=w600-h900",
  bk291: "https://books.google.com/books/publisher/content/images/frontcover/oZMeAQAAIAAJ?fife=w600-h900",
  bk292: "https://books.google.com/books/publisher/content/images/frontcover/hVaabePGPj4C?fife=w600-h900",
  bk293: "https://books.google.com/books/publisher/content/images/frontcover/36X0zQEACAAJ?fife=w600-h900",
  bk294: "https://books.google.com/books/publisher/content/images/frontcover/ZJW_n8dQrygC?fife=w600-h900",
  bk295: "https://books.google.com/books/publisher/content/images/frontcover/yLPtmNNckQgC?fife=w600-h900",
  bk296: "https://books.google.com/books/publisher/content/images/frontcover/5EWDxwEACAAJ?fife=w600-h900",
  bk297: "https://books.google.com/books/publisher/content/images/frontcover/G5gbvgAACAAJ?fife=w600-h900",
  bk299: "https://books.google.com/books/publisher/content/images/frontcover/X-lHqjsqtr8C?fife=w600-h900",
  bk300: "https://books.google.com/books/publisher/content/images/frontcover/f6XHkQEACAAJ?fife=w600-h900",
  bk301: "https://books.google.com/books/publisher/content/images/frontcover/Y21kvgAACAAJ?fife=w600-h900",
  bk302: "https://books.google.com/books/publisher/content/images/frontcover/PfyFygEACAAJ?fife=w600-h900",
  bk303: "https://books.google.com/books/publisher/content/images/frontcover/IUQD0AEACAAJ?fife=w600-h900",
  bk304: "https://books.google.com/books/publisher/content/images/frontcover/EhHGswEACAAJ?fife=w600-h900",
  bk305: "https://books.google.com/books/publisher/content/images/frontcover/YC3nciMBXzkC?fife=w600-h900",
  bk306: "https://books.google.com/books/publisher/content/images/frontcover/Hhv0zwEACAAJ?fife=w600-h900",
  bk307: "https://books.google.com/books/publisher/content/images/frontcover/Q1OVpwAACAAJ?fife=w600-h900",
  bk308: "https://books.google.com/books/publisher/content/images/frontcover/UzahxwEACAAJ?fife=w600-h900",
  bk309: "https://books.google.com/books/publisher/content/images/frontcover/V_ZqzgEACAAJ?fife=w600-h900",
  bk310: "https://books.google.com/books/publisher/content/images/frontcover/2G-kuAAACAAJ?fife=w600-h900",
  bk311: "https://books.google.com/books/publisher/content/images/frontcover/iYBNLgEACAAJ?fife=w600-h900",
  bk312: "https://books.google.com/books/publisher/content/images/frontcover/HWSGPgAACAAJ?fife=w600-h900",
  bk313: "https://books.google.com/books/publisher/content/images/frontcover/lim1zwEACAAJ?fife=w600-h900",
  bk314: "https://books.google.com/books/publisher/content/images/frontcover/UblVXwAACAAJ?fife=w600-h900",
  bk315: "https://books.google.com/books/publisher/content/images/frontcover/Tc7brQEACAAJ?fife=w600-h900",
  bk316: "https://books.google.com/books/publisher/content/images/frontcover/JPO5ugEACAAJ?fife=w600-h900",
  bk317: "https://books.google.com/books/publisher/content/images/frontcover/RDeutAEACAAJ?fife=w600-h900",
  bk319: "https://books.google.com/books/publisher/content/images/frontcover/5a-pigIIQoEC?fife=w600-h900",
  bk320: "https://books.google.com/books/publisher/content/images/frontcover/XNrVoAEACAAJ?fife=w600-h900",
  bk321: "https://books.google.com/books/publisher/content/images/frontcover/3jRQTluuTncC?fife=w600-h900",
  bk322: "https://books.google.com/books/publisher/content/images/frontcover/wgZ0zgEACAAJ?fife=w600-h900",
  bk323: "https://books.google.com/books/publisher/content/images/frontcover/MkP0zwEACAAJ?fife=w600-h900",
  bk324: "https://books.google.com/books/publisher/content/images/frontcover/Rw1WwgEACAAJ?fife=w600-h900",
  bk325: "https://books.google.com/books/publisher/content/images/frontcover/mGRZ99mPY7gC?fife=w600-h900",
  bk326: "https://books.google.com/books/publisher/content/images/frontcover/a6GZngEACAAJ?fife=w600-h900",
  bk327: "https://books.google.com/books/publisher/content/images/frontcover/2anBoAEACAAJ?fife=w600-h900",
  bk329: "https://books.google.com/books/publisher/content/images/frontcover/mxT90AEACAAJ?fife=w600-h900",
  bk331: "https://books.google.com/books/publisher/content/images/frontcover/5-On0AEACAAJ?fife=w600-h900",
  bk332: "https://books.google.com/books/publisher/content/images/frontcover/FiPL0QEACAAJ?fife=w600-h900",
  bk333: "https://books.google.com/books/publisher/content/images/frontcover/N2TfzwEACAAJ?fife=w600-h900",
  bk334: "https://books.google.com/books/publisher/content/images/frontcover/g9vANAEACAAJ?fife=w600-h900",
  bk335: "https://books.google.com/books/publisher/content/images/frontcover/MHkX0AEACAAJ?fife=w600-h900",
  bk336: "https://books.google.com/books/publisher/content/images/frontcover/BC4PkAEACAAJ?fife=w600-h900",
  bk337: "https://books.google.com/books/publisher/content/images/frontcover/FgxsrgEACAAJ?fife=w600-h900",
  bk338: "https://books.google.com/books/publisher/content/images/frontcover/adGEFSmPDWEC?fife=w600-h900",
  bk339: "https://books.google.com/books/publisher/content/images/frontcover/94OmzQEACAAJ?fife=w600-h900",
  bk340: "https://books.google.com/books/publisher/content/images/frontcover/64ok2x2hZxIC?fife=w600-h900",
  bk341: "https://books.google.com/books/publisher/content/images/frontcover/CGwtzgEACAAJ?fife=w600-h900",
  bk342: "https://books.google.com/books/publisher/content/images/frontcover/lbb9zQEACAAJ?fife=w600-h900",
  bk343: "https://books.google.com/books/publisher/content/images/frontcover/bCYRkgAACAAJ?fife=w600-h900",
  bk344: "https://books.google.com/books/publisher/content/images/frontcover/dBfcyAEACAAJ?fife=w600-h900",
  bk345: "https://books.google.com/books/publisher/content/images/frontcover/kMOWzQEACAAJ?fife=w600-h900",
  bk346: "https://books.google.com/books/publisher/content/images/frontcover/v3GbnQAACAAJ?fife=w600-h900",
  bk347: "https://books.google.com/books/publisher/content/images/frontcover/T9hhvgAACAAJ?fife=w600-h900",
  bk348: "https://books.google.com/books/publisher/content/images/frontcover/U0EpjwEACAAJ?fife=w600-h900",
  bk349: "https://books.google.com/books/publisher/content/images/frontcover/zpw8KQEACAAJ?fife=w600-h900",
  bk350: "https://books.google.com/books/publisher/content/images/frontcover/gcHGuwEACAAJ?fife=w600-h900",
  bk351: "https://books.google.com/books/publisher/content/images/frontcover/iuSVPwAACAAJ?fife=w600-h900",
  bk352: "https://books.google.com/books/publisher/content/images/frontcover/ekemzwEACAAJ?fife=w600-h900",
  bk353: "https://books.google.com/books/publisher/content/images/frontcover/zmPI0QEACAAJ?fife=w600-h900",
  bk354: "https://books.google.com/books/publisher/content/images/frontcover/u1YtAAAAYAAJ?fife=w600-h900",
  bk355: "https://books.google.com/books/publisher/content/images/frontcover/ZDAJzgEACAAJ?fife=w600-h900",
  bk356: "https://books.google.com/books/publisher/content/images/frontcover/P25w0AEACAAJ?fife=w600-h900",
  bk357: "https://books.google.com/books/publisher/content/images/frontcover/hvw9vgAACAAJ?fife=w600-h900",
  bk358: "https://books.google.com/books/publisher/content/images/frontcover/GNa-tAEACAAJ?fife=w600-h900",
  bk359: "https://books.google.com/books/publisher/content/images/frontcover/maV86uhgECIC?fife=w600-h900",
  bk360: "https://books.google.com/books/publisher/content/images/frontcover/hJqqzQEACAAJ?fife=w600-h900",
  bk361: "https://books.google.com/books/publisher/content/images/frontcover/ZpKAyQEACAAJ?fife=w600-h900",
  bk362: "https://books.google.com/books/publisher/content/images/frontcover/2rX6zwEACAAJ?fife=w600-h900",
  bk363: "https://books.google.com/books/publisher/content/images/frontcover/Bd6stAEACAAJ?fife=w600-h900",
  bk364: "https://books.google.com/books/publisher/content/images/frontcover/m8TArQEACAAJ?fife=w600-h900",
  bk365: "https://books.google.com/books/publisher/content/images/frontcover/lxnx0AEACAAJ?fife=w600-h900",
  bk366: "https://books.google.com/books/publisher/content/images/frontcover/SKMqAQAAIAAJ?fife=w600-h900",
  bk367: "https://books.google.com/books/publisher/content/images/frontcover/7Jbb0AEACAAJ?fife=w600-h900",
  bk368: "https://books.google.com/books/publisher/content/images/frontcover/bMf2zwEACAAJ?fife=w600-h900",
  bk369: "https://books.google.com/books/publisher/content/images/frontcover/cPoxvgAACAAJ?fife=w600-h900",
  bk370: "https://books.google.com/books/publisher/content/images/frontcover/JhIy0di6bN8C?fife=w600-h900",
  bk371: "https://books.google.com/books/publisher/content/images/frontcover/lKXb0AEACAAJ?fife=w600-h900",
  bk372: "https://books.google.com/books/publisher/content/images/frontcover/ksDb0AEACAAJ?fife=w600-h900",
  bk373: "https://books.google.com/books/publisher/content/images/frontcover/goDb0AEACAAJ?fife=w600-h900",
  bk374: "https://books.google.com/books/publisher/content/images/frontcover/1UYN0AEACAAJ?fife=w600-h900",
  bk375: "https://books.google.com/books/publisher/content/images/frontcover/jo3S0AEACAAJ?fife=w600-h900",
  bk376: "https://books.google.com/books/publisher/content/images/frontcover/YU_CrQEACAAJ?fife=w600-h900",
  bk377: "https://books.google.com/books/publisher/content/images/frontcover/tfFUvQEACAAJ?fife=w600-h900",
  bk378: "https://books.google.com/books/publisher/content/images/frontcover/bF0A0AEACAAJ?fife=w600-h900",
  bk379: "https://books.google.com/books/publisher/content/images/frontcover/_ibh0AEACAAJ?fife=w600-h900",
  bk380: "https://books.google.com/books/publisher/content/images/frontcover/VXDx0AEACAAJ?fife=w600-h900",
  bk381: "https://books.google.com/books/publisher/content/images/frontcover/B-jzzwEACAAJ?fife=w600-h900",
  bk382: "https://books.google.com/books/publisher/content/images/frontcover/WLf9zwEACAAJ?fife=w600-h900",
  bk383: "https://books.google.com/books/publisher/content/images/frontcover/yPq00AEACAAJ?fife=w600-h900",
  bk384: "https://books.google.com/books/publisher/content/images/frontcover/EGDx0AEACAAJ?fife=w600-h900",
  bk387: "https://books.google.com/books/publisher/content/images/frontcover/qvuIzQEACAAJ?fife=w600-h900",
  bk388: "https://books.google.com/books/publisher/content/images/frontcover/DzX_zwEACAAJ?fife=w600-h900",
  bk390: "https://books.google.com/books/publisher/content/images/frontcover/2UsW0AEACAAJ?fife=w600-h900",
  bk391: "https://books.google.com/books/publisher/content/images/frontcover/Gfn1zwEACAAJ?fife=w600-h900",
  bk392: "https://books.google.com/books/publisher/content/images/frontcover/jAn5zwEACAAJ?fife=w600-h900",
  bk393: "https://books.google.com/books/publisher/content/images/frontcover/XjH2zwEACAAJ?fife=w600-h900",
  bk394: "https://books.google.com/books/publisher/content/images/frontcover/-kRW0AEACAAJ?fife=w600-h900",
  bk395: "https://books.google.com/books/publisher/content/images/frontcover/T1UW0AEACAAJ?fife=w600-h900",
  bk396: "https://books.google.com/books/publisher/content/images/frontcover/k3sY0AEACAAJ?fife=w600-h900",
  bk397: "https://books.google.com/books/publisher/content/images/frontcover/724A0AEACAAJ?fife=w600-h900",
  bk398: "https://books.google.com/books/publisher/content/images/frontcover/2zynQgAACAAJ?fife=w600-h900",
  bk399: "https://books.google.com/books/publisher/content/images/frontcover/4H3XjieBxMwC?fife=w600-h900",
  bk400: "https://books.google.com/books/publisher/content/images/frontcover/YibD6kttrA8C?fife=w600-h900",
  bk401: "https://books.google.com/books/publisher/content/images/frontcover/ntXuAAAAMAAJ?fife=w600-h900",
  bk402: "https://books.google.com/books/publisher/content/images/frontcover/8igrEAAAQBAJ?fife=w600-h900",
  bk403: "https://books.google.com/books/publisher/content/images/frontcover/v8JTzgEACAAJ?fife=w600-h900",
  bk404: "https://books.google.com/books/publisher/content/images/frontcover/06IqAQAAIAAJ?fife=w600-h900",
  bk405: "https://books.google.com/books/publisher/content/images/frontcover/AX1LRwAACAAJ?fife=w600-h900",
  bk406: "https://books.google.com/books/publisher/content/images/frontcover/BV7CoNskYwMC?fife=w600-h900",
  bk407: "https://books.google.com/books/publisher/content/images/frontcover/Lar9QQAACAAJ?fife=w600-h900",
  bk408: "https://books.google.com/books/publisher/content/images/frontcover/djnfRgAACAAJ?fife=w600-h900",
  bk409: "https://books.google.com/books/publisher/content/images/frontcover/X6BkvQEACAAJ?fife=w600-h900",
  bk410: "https://books.google.com/books/publisher/content/images/frontcover/TS9VLwEACAAJ?fife=w600-h900",
  bk411: "https://books.google.com/books/publisher/content/images/frontcover/2hP5zwEACAAJ?fife=w600-h900",
  bk412: "https://books.google.com/books/publisher/content/images/frontcover/6CkA0AEACAAJ?fife=w600-h900",
  bk413: "https://books.google.com/books/publisher/content/images/frontcover/E4c3zQEACAAJ?fife=w600-h900",
  bk415: "https://books.google.com/books/publisher/content/images/frontcover/dZyqzwEACAAJ?fife=w600-h900",
  bk416: "https://books.google.com/books/publisher/content/images/frontcover/-8UW0AEACAAJ?fife=w600-h900",
  bk417: "https://books.google.com/books/publisher/content/images/frontcover/Kf0dtAEACAAJ?fife=w600-h900",
  bk418: "https://books.google.com/books/publisher/content/images/frontcover/hBgojwEACAAJ?fife=w600-h900",
  bk419: "https://books.google.com/books/publisher/content/images/frontcover/xMYmygAACAAJ?fife=w600-h900",
  bk420: "https://books.google.com/books/publisher/content/images/frontcover/3fEbAQAAMAAJ?fife=w600-h900",
  bk421: "https://books.google.com/books/publisher/content/images/frontcover/i3CZPgAACAAJ?fife=w600-h900",
  bk422: "https://books.google.com/books/publisher/content/images/frontcover/7T2aAQAACAAJ?fife=w600-h900",
  bk423: "https://books.google.com/books/publisher/content/images/frontcover/a0MOzgEACAAJ?fife=w600-h900",
  bk424: "https://books.google.com/books/publisher/content/images/frontcover/2T4ynQAACAAJ?fife=w600-h900",
  bk425: "https://books.google.com/books/publisher/content/images/frontcover/9De8jwEACAAJ?fife=w600-h900",
  bk426: "https://books.google.com/books/publisher/content/images/frontcover/vHJgvgAACAAJ?fife=w600-h900",
  bk427: "https://books.google.com/books/publisher/content/images/frontcover/DvYA0AEACAAJ?fife=w600-h900",
  bk428: "https://books.google.com/books/publisher/content/images/frontcover/ZI7rugEACAAJ?fife=w600-h900",
  bk429: "https://books.google.com/books/publisher/content/images/frontcover/me-uzSCEbrsC?fife=w600-h900",
  bk430: "https://books.google.com/books/publisher/content/images/frontcover/G9aS_lkwiCoC?fife=w600-h900",
  bk431: "https://books.google.com/books/publisher/content/images/frontcover/VlT-zwEACAAJ?fife=w600-h900",
  bk432: "https://books.google.com/books/publisher/content/images/frontcover/JsgbAQAAMAAJ?fife=w600-h900",
  bk433: "https://books.google.com/books/publisher/content/images/frontcover/B-0bAQAAMAAJ?fife=w600-h900",
  bk434: "https://books.google.com/books/publisher/content/images/frontcover/CpV7PgAACAAJ?fife=w600-h900",
  bk435: "https://books.google.com/books/publisher/content/images/frontcover/23vJ0AEACAAJ?fife=w600-h900",
  bk436: "https://books.google.com/books/publisher/content/images/frontcover/_OC3swEACAAJ?fife=w600-h900",
  bk437: "https://books.google.com/books/publisher/content/images/frontcover/RLQJzgEACAAJ?fife=w600-h900",
  bk438: "https://books.google.com/books/publisher/content/images/frontcover/2t8dvgAACAAJ?fife=w600-h900",
  bk439: "https://books.google.com/books/publisher/content/images/frontcover/nHzjswEACAAJ?fife=w600-h900",
  bk440: "https://books.google.com/books/publisher/content/images/frontcover/dK3_tQEACAAJ?fife=w600-h900",
  bk441: "https://books.google.com/books/publisher/content/images/frontcover/vqRZzgEACAAJ?fife=w600-h900",
  bk442: "https://books.google.com/books/publisher/content/images/frontcover/bIp1zQEACAAJ?fife=w600-h900",
  bk443: "https://books.google.com/books/publisher/content/images/frontcover/14xnzwEACAAJ?fife=w600-h900",
  bk444: "https://books.google.com/books/publisher/content/images/frontcover/KRLTzQEACAAJ?fife=w600-h900",
  bk445: "https://books.google.com/books/publisher/content/images/frontcover/aaXyzwEACAAJ?fife=w600-h900",
  bk446: "https://books.google.com/books/publisher/content/images/frontcover/-_EX0AEACAAJ?fife=w600-h900",
  bk447: "https://books.google.com/books/publisher/content/images/frontcover/3GPOzQEACAAJ?fife=w600-h900",
  bk448: "https://books.google.com/books/publisher/content/images/frontcover/CYl4zQEACAAJ?fife=w600-h900",
  bk449: "https://books.google.com/books/publisher/content/images/frontcover/KCXctAEACAAJ?fife=w600-h900",
  bk450: "https://books.google.com/books/publisher/content/images/frontcover/UT_DzQEACAAJ?fife=w600-h900",
  bk452: "https://books.google.com/books/publisher/content/images/frontcover/x3D5oAEACAAJ?fife=w600-h900",
  bk453: "https://books.google.com/books/publisher/content/images/frontcover/BYD4zwEACAAJ?fife=w600-h900",
  bk454: "https://books.google.com/books/publisher/content/images/frontcover/QMDI0AEACAAJ?fife=w600-h900",
  bk455: "https://books.google.com/books/publisher/content/images/frontcover/9sJ-AAAAMAAJ?fife=w600-h900",
  bk456: "https://books.google.com/books/publisher/content/images/frontcover/hktFxAEACAAJ?fife=w600-h900",
  bk457: "https://books.google.com/books/publisher/content/images/frontcover/pRxDzgEACAAJ?fife=w600-h900",
  bk458: "https://books.google.com/books/publisher/content/images/frontcover/7TWIzwEACAAJ?fife=w600-h900",
  bk459: "https://books.google.com/books/publisher/content/images/frontcover/IUYdlQEACAAJ?fife=w600-h900",
  bk460: "https://books.google.com/books/publisher/content/images/frontcover/s0rFsgEACAAJ?fife=w600-h900",
  bk461: "https://books.google.com/books/publisher/content/images/frontcover/qc88swEACAAJ?fife=w600-h900",
  bk462: "https://books.google.com/books/publisher/content/images/frontcover/UZyEygEACAAJ?fife=w600-h900",
  bk463: "https://books.google.com/books/publisher/content/images/frontcover/yQPuywEACAAJ?fife=w600-h900",
  bk464: "https://books.google.com/books/publisher/content/images/frontcover/5c-5ywEACAAJ?fife=w600-h900",
  bk465: "https://books.google.com/books/publisher/content/images/frontcover/ezNZvgAACAAJ?fife=w600-h900",
  bk466: "https://books.google.com/books/publisher/content/images/frontcover/qlWvswEACAAJ?fife=w600-h900",
  bk467: "https://books.google.com/books/publisher/content/images/frontcover/nTSkuAEACAAJ?fife=w600-h900",
  bk468: "https://books.google.com/books/publisher/content/images/frontcover/wsJ9swEACAAJ?fife=w600-h900",
  bk469: "https://books.google.com/books/publisher/content/images/frontcover/Rv0OAQAAIAAJ?fife=w600-h900",
  bk471: "https://books.google.com/books/publisher/content/images/frontcover/1wk9vgAACAAJ?fife=w600-h900",
  bk472: "https://books.google.com/books/publisher/content/images/frontcover/KQ1XzgEACAAJ?fife=w600-h900",
  bk473: "https://books.google.com/books/publisher/content/images/frontcover/VDY_vgAACAAJ?fife=w600-h900",
  bk474: "https://books.google.com/books/publisher/content/images/frontcover/PmbBzAEACAAJ?fife=w600-h900",
  bk475: "https://books.google.com/books/publisher/content/images/frontcover/SqC8swEACAAJ?fife=w600-h900",
  bk476: "https://books.google.com/books/publisher/content/images/frontcover/q7hVvgAACAAJ?fife=w600-h900",
  bk477: "https://books.google.com/books/publisher/content/images/frontcover/wH7vzgEACAAJ?fife=w600-h900",
  bk478: "https://books.google.com/books/publisher/content/images/frontcover/FzzzzwEACAAJ?fife=w600-h900",
  bk479: "https://books.google.com/books/publisher/content/images/frontcover/Dd9duAAACAAJ?fife=w600-h900",
  bk480: "https://books.google.com/books/publisher/content/images/frontcover/nNx4DQEACAAJ?fife=w600-h900",
  bk481: "https://books.google.com/books/publisher/content/images/frontcover/Rw5oygEACAAJ?fife=w600-h900",
  bk482: "https://books.google.com/books/publisher/content/images/frontcover/4GRw0AEACAAJ?fife=w600-h900",
  bk483: "https://books.google.com/books/publisher/content/images/frontcover/iPfH0QEACAAJ?fife=w600-h900",
  bk484: "https://books.google.com/books/publisher/content/images/frontcover/fh5azgEACAAJ?fife=w600-h900",
  bk486: "https://books.google.com/books/publisher/content/images/frontcover/lBr7zwEACAAJ?fife=w600-h900",
  bk487: "https://books.google.com/books/publisher/content/images/frontcover/k3wJzgEACAAJ?fife=w600-h900",
  bk488: "https://books.google.com/books/publisher/content/images/frontcover/K_bS0AEACAAJ?fife=w600-h900",
  bk489: "https://books.google.com/books/publisher/content/images/frontcover/K9EHzgEACAAJ?fife=w600-h900",
  bk490: "https://books.google.com/books/publisher/content/images/frontcover/_qon0AEACAAJ?fife=w600-h900",
  bk491: "https://books.google.com/books/publisher/content/images/frontcover/DVcS0QEACAAJ?fife=w600-h900",
  bk492: "https://books.google.com/books/publisher/content/images/frontcover/otPftAEACAAJ?fife=w600-h900",
  bk493: "https://books.google.com/books/publisher/content/images/frontcover/fHaWswEACAAJ?fife=w600-h900",
  bk494: "https://books.google.com/books/publisher/content/images/frontcover/OFX2zwEACAAJ?fife=w600-h900",
  bk495: "https://books.google.com/books/publisher/content/images/frontcover/_dDb0AEACAAJ?fife=w600-h900",
  bk496: "https://books.google.com/books/publisher/content/images/frontcover/AC5gwwEACAAJ?fife=w600-h900",
  bk497: "https://books.google.com/books/publisher/content/images/frontcover/65eHzwEACAAJ?fife=w600-h900",
  bk498: "https://books.google.com/books/publisher/content/images/frontcover/vqTEvwEACAAJ?fife=w600-h900",
  bk499: "https://books.google.com/books/publisher/content/images/frontcover/P0gKzgEACAAJ?fife=w600-h900",
  bk500: "https://books.google.com/books/publisher/content/images/frontcover/nxc2vgAACAAJ?fife=w600-h900",
};
window.REAL_COVERS = REAL_COVERS;

function BookCover({ book, size = "md" }) {
  const W = typeof size === "number" ? size : size === "lg" ? 300 : size === "sm" ? 110 : 160;
  const H = W * 1.45;
  const palette = book.palette || ["#2c4ffc", "#c7de40"];
  const sanitizeUrl = (value) => {
    const text = String(value || "").trim();
    if (!text) return "";
    if (/^(javascript|data):/i.test(text)) return "";
    return text;
  };
  const isbn = (book && book.isbn ? String(book.isbn) : "").trim();
  const localCover = window.LOCAL_COVERS && window.LOCAL_COVERS[book.id];
  const isbnCover = isbn && window.ISBN_COVERS && window.ISBN_COVERS[isbn];
  const [coverIdx, setCoverIdx] = React.useState(0);
  const [failed, setFailed] = React.useState(false);

  const coverCandidates = React.useMemo(() => {
    const candidates = [
      isbnCover || "",
      localCover || "",
      sanitizeUrl((book && book.imageUrl) || ""),
      isbn ? `https://covers.openlibrary.org/b/isbn/${encodeURIComponent(isbn)}-L.jpg` : "",
      isbn ? `https://books.google.com/books/content?vid=ISBN:${encodeURIComponent(isbn)}&printsec=frontcover&img=1&zoom=1&source=gbs_api` : "",
      REAL_COVERS[book.id] || "",
    ].filter(Boolean);

    return [...new Set(candidates)];
  }, [book.id, book.imageUrl, isbnCover, localCover, isbn]);

  const realCover = coverCandidates[coverIdx] || null;

  React.useEffect(() => {
    setCoverIdx(0);
    setFailed(false);
  }, [book.id, book.imageUrl, isbnCover, localCover, isbn]);

  const onCoverError = () => {
    const next = coverIdx + 1;
    if (next < coverCandidates.length) {
      setCoverIdx(next);
      return;
    }
    setFailed(true);
  };

  // Local covers never "fail", they're on disk. Only remote REAL_COVERS can 404.
  if (realCover && !failed) {
    return (
      <div style={{
        width: W, height: H, borderRadius: 6,
        overflow: "hidden", position: "relative",
        boxShadow: "0 12px 24px rgba(0,0,0,0.15), 0 2px 6px rgba(0,0,0,0.1)",
        flexShrink: 0,
        background: `linear-gradient(135deg, ${palette[0]}, ${palette[1] || palette[0]})`,
      }}>
        <img src={realCover} alt={book.title}
             loading="eager" decoding="async"
             onError={onCoverError}
             style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}/>
        {/* level tag */}
        <div style={{
          position: "absolute", right: 8, top: 8,
          background: "rgba(255,255,255,0.95)", color: "var(--text)",
          padding: "3px 8px", borderRadius: 6,
          fontSize: W * 0.06, fontWeight: 700,
          boxShadow: "0 2px 6px rgba(0,0,0,0.15)",
        }}>{book.level}</div>
      </div>
    );
  }

  return (
    <div style={{
      width: W, height: H, borderRadius: 6,
      background: palette[0],
      overflow: "hidden", position: "relative",
      boxShadow: "0 12px 24px rgba(0,0,0,0.15), 0 2px 6px rgba(0,0,0,0.1)",
      flexShrink: 0,
    }}>
      {/* cover decoration */}
      <div style={{
        position: "absolute", inset: 0,
        background: book.bgPattern || `linear-gradient(135deg, ${palette[0]}, ${palette[1] || palette[0]})`,
      }}/>
      {book.shape && (
        <div style={{
          position: "absolute", left: "50%", top: "42%",
          transform: "translate(-50%,-50%)",
          width: W * 0.55, height: W * 0.55,
          background: palette[2] || "#fff",
          borderRadius: book.shape === "circle" ? "50%" : "6px",
          opacity: 0.9,
        }}/>
      )}
      <div style={{
        position: "absolute", left: 10, right: 10, bottom: 10,
        color: "#fff", fontFamily: "Georgia, serif",
      }}>
        <div style={{ fontSize: W * 0.09, fontWeight: 800, lineHeight: 1.1, textShadow: "0 1px 3px rgba(0,0,0,0.3)" }}>
          {book.title}
        </div>
        <div style={{ fontSize: W * 0.055, marginTop: 4, opacity: 0.85, fontFamily: "Poppins, sans-serif", fontWeight: 500 }}>
          {book.author}
        </div>
      </div>
      {/* level tag */}
      <div style={{
        position: "absolute", right: 8, top: 8,
        background: "#fff", color: "var(--text)",
        padding: "3px 8px", borderRadius: 6,
        fontSize: W * 0.06, fontWeight: 700,
        boxShadow: "0 2px 6px rgba(0,0,0,0.15)",
      }}>{book.level}</div>
    </div>
  );
}

Object.assign(window, {
  STEPS, AppCtx, useApp,
  LexAvatar, LexFullBody, LexBubble,
  BtnPrimary, BtnGhost, BtnTeal,
  ScolistMark, Stepper, Shell, Scolex,
  OptionCard, Chip, UploadZone, Heading, PremiumHeader, BookCover,
  REAL_COVERS,
});
