// Sections for "VI. Bestiário" page — grimório / sketchbook spreads.
// Data lives in bestiario-data.jsx (exposed as window.BESTIARY).
const BESTIARY = window.BESTIARY;
// ---------- Page Hero ----------
function BestiaryHero({ intensity }) {
return (
Capítulo VI · As Criaturas
O Bestiário
O Legado do Fogo
Registro ilustrado das criaturas conhecidas que habitam — e ameaçam — o Grande Continente. Cada espécime foi observado em campo, capturado em esboço e descrito em linguagens antigas antes de ser arquivado neste volume.
Folheie o grimório com cautela. Algumas destas páginas foram lacradas por décadas — e por bons motivos. O que se segue não é um inventário: é um aviso.
Abra o grimório
);
}
// ---------- Index of creatures ----------
function BestiaryIndex() {
return (
Index Bestiarum
{BESTIARY.length} espécimes catalogados
);
}
// ---------- Slot primitive ----------
function ArtSlot({ shape = "tall", label, dropLabel = "ilustração", image, alt, fit = "cover" }) {
if (image) {
return (
{label &&
{label} }
);
}
return (
⌬
[ {dropLabel} ]
{label &&
{label} }
);
}
// ---------- Page halves (extracted so we can flip them independently) ----------
function CreatureLeftPage({ entry }) {
const { figures, scene, sceneCaption } = entry;
const figureCount = figures ? figures.length : 0;
return (
{entry.classification}
{entry.intro.map((line, i) => {line} )}
{scene ? (
{sceneCaption && {sceneCaption} }
) : (
{figures && figures.map((f, i) => (
))}
)}
);
}
function CreatureRightPage({ entry }) {
const { anatomy } = entry;
return (
{anatomy.mainCaption}
{anatomy.sub.label}
{anatomy.sub.caption}
{anatomy.variantsNote}
{anatomy.variants.map((v) => (
))}
);
}
// ---------- Book container: stacked spreads, small side arrows ----------
function BestiarySpread({ entry, index, total }) {
return (
{entry.numeral}
— FOLIO {String(index + 1).padStart(2, "0")} / {String(total).padStart(2, "0")} —
{entry.name}
);
}
function BestiaryBook() {
const total = BESTIARY.length;
const [index, setIndex] = React.useState(0);
const [animating, setAnimating] = React.useState(false);
const animTimer = React.useRef(null);
const go = React.useCallback((target) => {
const clamped = Math.max(0, Math.min(total - 1, target));
if (clamped === index || animating) return;
setIndex(clamped);
setAnimating(true);
if (animTimer.current) clearTimeout(animTimer.current);
animTimer.current = setTimeout(() => setAnimating(false), 620);
}, [index, total, animating]);
React.useEffect(() => () => {
if (animTimer.current) clearTimeout(animTimer.current);
}, []);
// keyboard nav when the book is on screen
React.useEffect(() => {
const onKey = (e) => {
if (e.target && (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA")) return;
const book = document.getElementById("bestiary-book");
if (!book) return;
const r = book.getBoundingClientRect();
const visible = r.bottom > 100 && r.top < window.innerHeight - 100;
if (!visible) return;
if (e.key === "ArrowRight") { e.preventDefault(); go(index + 1); }
else if (e.key === "ArrowLeft") { e.preventDefault(); go(index - 1); }
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [go, index]);
// jump from index cards
React.useEffect(() => {
const onJump = (e) => {
const target = e.detail?.target;
if (typeof target === "number") {
go(target);
const el = document.getElementById("bestiary-book");
if (el) {
const y = window.scrollY + el.getBoundingClientRect().top - 70;
window.scrollTo({ top: y, behavior: "smooth" });
}
}
};
window.addEventListener("bestiary:goto", onJump);
return () => window.removeEventListener("bestiary:goto", onJump);
}, [go]);
return (
Liber Bestiarum · MMXXIV
{BESTIARY.map((entry, i) => (
))}
{/* Floating side arrows — small, do not overlap pages */}
go(index - 1)}
disabled={index <= 0 || animating}
aria-label="Folio anterior"
>
‹
go(index + 1)}
disabled={index >= total - 1 || animating}
aria-label="Próximo folio"
>
›
);
}
// ---------- Outro ----------
function BestiaryOutro() {
return (
“Conhecer a criatura é metade da espada. A outra metade — apenas a coragem forja.”
Liber Bestiarum · Epílogo
);
}
Object.assign(window, { BestiaryHero, BestiaryIndex, BestiaryBook, BestiaryOutro });